tangled
alpha
login
or
join now
taurean.bryant.land
/
drydown
1
fork
atom
a a vibe-coded abomination experiment of a fragrance review platform built on the atmosphere.
drydown.social
1
fork
atom
overview
issues
pulls
pipelines
cleaned up UI to remove starter vite stuff
taurean.bryant.land
2 months ago
f030c7fb
9b2771cc
+78
-64
2 changed files
expand all
collapse all
unified
split
src
app.css
app.tsx
+15
-12
src/app.css
···
7
7
text-align: center;
8
8
}
9
9
10
10
-
.logo {
11
11
-
height: 6em;
12
12
-
padding: 1.5em;
10
10
+
.card {
11
11
+
padding: 2em;
13
12
}
14
13
15
15
-
.logo:hover {
16
16
-
filter: drop-shadow(0 0 2em #646cffaa);
14
14
+
header {
15
15
+
display: flex;
16
16
+
justify-content: space-between;
17
17
+
align-items: center;
18
18
+
margin-bottom: 2rem;
19
19
+
padding-bottom: 1rem;
20
20
+
border-bottom: 1px solid #333;
17
21
}
18
22
19
19
-
.logo.preact:hover {
20
20
-
filter: drop-shadow(0 0 2em #673ab8aa);
23
23
+
header h1 {
24
24
+
margin: 0;
25
25
+
font-size: 1.5rem;
26
26
+
font-weight: 800;
21
27
}
22
28
23
23
-
.card {
24
24
-
padding: 2em;
25
25
-
}
26
26
-
27
27
-
.read-the-docs {
29
29
+
header .user-info {
30
30
+
font-size: 0.9rem;
28
31
color: #888;
29
32
}
+63
-52
src/app.tsx
···
1
1
import { useState, useEffect } from 'preact/hooks'
2
2
-
import preactLogo from './assets/preact.svg'
3
3
-
import viteLogo from '/vite.svg'
4
2
import './app.css'
5
3
import { initAuth, logout } from './auth'
6
4
import { LoginForm } from './components/LoginForm'
···
8
6
import { ReviewDashboard } from './components/ReviewDashboard'
9
7
import { EditReview } from './components/EditReview'
10
8
import type { OAuthSession } from '@atproto/oauth-client-browser'
9
9
+
import { AtpBaseClient } from './client/index'
11
10
12
11
export function App() {
13
12
const [session, setSession] = useState<OAuthSession | null>(null)
14
13
const [isInitializing, setIsInitializing] = useState(true)
14
14
+
const [userProfile, setUserProfile] = useState<{ displayName?: string, handle: string } | null>(null)
15
15
const [view, setView] = useState<'home' | 'create-review' | 'edit-review'>('home')
16
16
const [editReviewUri, setEditReviewUri] = useState<string | null>(null)
17
17
const [editReviewStage, setEditReviewStage] = useState<'stage2' | 'stage3' | null>(null)
···
25
25
26
26
// Check if we have a session from the result
27
27
if (result?.session) {
28
28
-
console.log('Session found:', result.session)
29
29
-
setSession(result.session as OAuthSession)
28
28
+
console.log('Session found:', result.session)
29
29
+
setSession(result.session as OAuthSession)
30
30
+
31
31
+
// Fetch Profile
32
32
+
try {
33
33
+
// Must bind fetchHandler!
34
34
+
const client = new AtpBaseClient(result.session.fetchHandler.bind(result.session))
35
35
+
const profileRes = await client.call('app.bsky.actor.getProfile', { actor: result.session.sub })
36
36
+
setUserProfile({
37
37
+
displayName: profileRes.data.displayName as string,
38
38
+
handle: profileRes.data.handle as string
39
39
+
})
40
40
+
} catch (e) {
41
41
+
console.error("Failed to fetch profile", e)
42
42
+
// Fallback to minimal info if fetch fails
43
43
+
setUserProfile({ handle: result.session.sub })
44
44
+
}
45
45
+
30
46
} else {
31
47
console.log('No session in result')
32
48
}
33
49
} catch (err) {
34
50
console.error('Auth init failed:', err)
35
35
-
// If it's a ZodError about localhost, checking the URL might help
36
51
// If it's a ZodError about localhost, checking the URL might help
37
52
const allowedHosts = ['127.0.0.1', 'drydown.pages.dev', 'drydown.social']
38
53
if (!allowedHosts.includes(window.location.hostname)) {
···
52
67
try {
53
68
await logout(session.sub)
54
69
setSession(null)
70
70
+
setUserProfile(null)
55
71
setView('home')
56
72
} catch (err) {
57
73
console.error('Logout failed:', err)
···
74
90
75
91
return (
76
92
<>
77
77
-
<div>
78
78
-
<a href="https://vite.dev" target="_blank">
79
79
-
<img src={viteLogo} class="logo" alt="Vite logo" />
80
80
-
</a>
81
81
-
<a href="https://preactjs.com" target="_blank">
82
82
-
<img src={preactLogo} class="logo preact" alt="Preact logo" />
83
83
-
</a>
84
84
-
</div>
85
85
-
<h1>Vite + Preact + Bluesky OAuth</h1>
86
86
-
87
93
{!session ? (
88
88
-
<LoginForm />
94
94
+
<div style={{ marginTop: '4rem' }}>
95
95
+
<h1 style={{ marginBottom: '2rem' }}>Drydown</h1>
96
96
+
<LoginForm />
97
97
+
</div>
89
98
) : (
90
90
-
<div class="card">
91
91
-
<h2>Welcome, {session.sub}!</h2>
92
92
-
<p>You are now signed in via OAuth.</p>
93
93
-
94
94
-
{view === 'home' ? (
95
95
-
<>
96
96
-
<ReviewDashboard
99
99
+
<>
100
100
+
<header>
101
101
+
<h1>Drydown</h1>
102
102
+
<div class="user-info">
103
103
+
{userProfile?.displayName || userProfile?.handle || session.sub}
104
104
+
</div>
105
105
+
</header>
106
106
+
107
107
+
<div class="card">
108
108
+
{view === 'home' ? (
109
109
+
<>
110
110
+
<ReviewDashboard
111
111
+
session={session}
112
112
+
onCreateNew={handleCreateNew}
113
113
+
onEditReview={(uri, stage) => {
114
114
+
setEditReviewUri(uri)
115
115
+
setEditReviewStage(stage)
116
116
+
setView('edit-review')
117
117
+
}}
118
118
+
/>
119
119
+
<button onClick={handleLogout} style={{ marginTop: '2rem', fontSize: '0.8rem', opacity: 0.8 }}>Sign Out</button>
120
120
+
</>
121
121
+
) : view === 'create-review' ? (
122
122
+
<CreateReview
123
123
+
session={session}
124
124
+
onCancel={handleBackToDashboard}
125
125
+
onSuccess={handleBackToDashboard}
126
126
+
/>
127
127
+
) : (
128
128
+
<EditReview
97
129
session={session}
98
98
-
onCreateNew={handleCreateNew}
99
99
-
onEditReview={(uri, stage) => {
100
100
-
setEditReviewUri(uri)
101
101
-
setEditReviewStage(stage)
102
102
-
setView('edit-review')
103
103
-
}}
104
104
-
/>
105
105
-
<button onClick={handleLogout} style={{ marginTop: '2rem' }}>Sign Out</button>
106
106
-
</>
107
107
-
) : view === 'create-review' ? (
108
108
-
<CreateReview
109
109
-
session={session}
110
110
-
onCancel={handleBackToDashboard}
111
111
-
onSuccess={handleBackToDashboard}
112
112
-
/>
113
113
-
) : (
114
114
-
<EditReview
115
115
-
session={session}
116
116
-
reviewUri={editReviewUri!}
117
117
-
stage={editReviewStage!}
118
118
-
onCancel={handleBackToDashboard}
119
119
-
onSuccess={handleBackToDashboard}
120
120
-
/>
121
121
-
)}
122
122
-
</div>
130
130
+
reviewUri={editReviewUri!}
131
131
+
stage={editReviewStage!}
132
132
+
onCancel={handleBackToDashboard}
133
133
+
onSuccess={handleBackToDashboard}
134
134
+
/>
135
135
+
)}
136
136
+
</div>
137
137
+
</>
123
138
)}
124
124
-
125
125
-
<p class="read-the-docs">
126
126
-
Click on the Vite and Preact logos to learn more
127
127
-
</p>
128
139
</>
129
140
)
130
141
}