a a vibe-coded abomination experiment of a fragrance review platform built on the atmosphere. drydown.social

cleaned up UI to remove starter vite stuff

+78 -64
+15 -12
src/app.css
··· 7 7 text-align: center; 8 8 } 9 9 10 - .logo { 11 - height: 6em; 12 - padding: 1.5em; 10 + .card { 11 + padding: 2em; 13 12 } 14 13 15 - .logo:hover { 16 - filter: drop-shadow(0 0 2em #646cffaa); 14 + header { 15 + display: flex; 16 + justify-content: space-between; 17 + align-items: center; 18 + margin-bottom: 2rem; 19 + padding-bottom: 1rem; 20 + border-bottom: 1px solid #333; 17 21 } 18 22 19 - .logo.preact:hover { 20 - filter: drop-shadow(0 0 2em #673ab8aa); 23 + header h1 { 24 + margin: 0; 25 + font-size: 1.5rem; 26 + font-weight: 800; 21 27 } 22 28 23 - .card { 24 - padding: 2em; 25 - } 26 - 27 - .read-the-docs { 29 + header .user-info { 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 - import preactLogo from './assets/preact.svg' 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 + 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 + 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 - console.log('Session found:', result.session) 29 - setSession(result.session as OAuthSession) 28 + console.log('Session found:', result.session) 29 + setSession(result.session as OAuthSession) 30 + 31 + // Fetch Profile 32 + try { 33 + // Must bind fetchHandler! 34 + const client = new AtpBaseClient(result.session.fetchHandler.bind(result.session)) 35 + const profileRes = await client.call('app.bsky.actor.getProfile', { actor: result.session.sub }) 36 + setUserProfile({ 37 + displayName: profileRes.data.displayName as string, 38 + handle: profileRes.data.handle as string 39 + }) 40 + } catch (e) { 41 + console.error("Failed to fetch profile", e) 42 + // Fallback to minimal info if fetch fails 43 + setUserProfile({ handle: result.session.sub }) 44 + } 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 - // 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 + 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 - <div> 78 - <a href="https://vite.dev" target="_blank"> 79 - <img src={viteLogo} class="logo" alt="Vite logo" /> 80 - </a> 81 - <a href="https://preactjs.com" target="_blank"> 82 - <img src={preactLogo} class="logo preact" alt="Preact logo" /> 83 - </a> 84 - </div> 85 - <h1>Vite + Preact + Bluesky OAuth</h1> 86 - 87 93 {!session ? ( 88 - <LoginForm /> 94 + <div style={{ marginTop: '4rem' }}> 95 + <h1 style={{ marginBottom: '2rem' }}>Drydown</h1> 96 + <LoginForm /> 97 + </div> 89 98 ) : ( 90 - <div class="card"> 91 - <h2>Welcome, {session.sub}!</h2> 92 - <p>You are now signed in via OAuth.</p> 93 - 94 - {view === 'home' ? ( 95 - <> 96 - <ReviewDashboard 99 + <> 100 + <header> 101 + <h1>Drydown</h1> 102 + <div class="user-info"> 103 + {userProfile?.displayName || userProfile?.handle || session.sub} 104 + </div> 105 + </header> 106 + 107 + <div class="card"> 108 + {view === 'home' ? ( 109 + <> 110 + <ReviewDashboard 111 + session={session} 112 + onCreateNew={handleCreateNew} 113 + onEditReview={(uri, stage) => { 114 + setEditReviewUri(uri) 115 + setEditReviewStage(stage) 116 + setView('edit-review') 117 + }} 118 + /> 119 + <button onClick={handleLogout} style={{ marginTop: '2rem', fontSize: '0.8rem', opacity: 0.8 }}>Sign Out</button> 120 + </> 121 + ) : view === 'create-review' ? ( 122 + <CreateReview 123 + session={session} 124 + onCancel={handleBackToDashboard} 125 + onSuccess={handleBackToDashboard} 126 + /> 127 + ) : ( 128 + <EditReview 97 129 session={session} 98 - onCreateNew={handleCreateNew} 99 - onEditReview={(uri, stage) => { 100 - setEditReviewUri(uri) 101 - setEditReviewStage(stage) 102 - setView('edit-review') 103 - }} 104 - /> 105 - <button onClick={handleLogout} style={{ marginTop: '2rem' }}>Sign Out</button> 106 - </> 107 - ) : view === 'create-review' ? ( 108 - <CreateReview 109 - session={session} 110 - onCancel={handleBackToDashboard} 111 - onSuccess={handleBackToDashboard} 112 - /> 113 - ) : ( 114 - <EditReview 115 - session={session} 116 - reviewUri={editReviewUri!} 117 - stage={editReviewStage!} 118 - onCancel={handleBackToDashboard} 119 - onSuccess={handleBackToDashboard} 120 - /> 121 - )} 122 - </div> 130 + reviewUri={editReviewUri!} 131 + stage={editReviewStage!} 132 + onCancel={handleBackToDashboard} 133 + onSuccess={handleBackToDashboard} 134 + /> 135 + )} 136 + </div> 137 + </> 123 138 )} 124 - 125 - <p class="read-the-docs"> 126 - Click on the Vite and Preact logos to learn more 127 - </p> 128 139 </> 129 140 ) 130 141 }