1- import { useState , useMemo } from 'react' ;
1+ import { useState , useMemo , useEffect } from 'react' ;
22import { motion } from 'motion/react' ;
33import { ChatPage } from './pages/ChatPage' ;
4+ import { LoginPage } from './pages/LoginPage' ;
5+ import { RegisterPage } from './pages/RegisterPage' ;
6+ import { ProfileSetupPage } from './pages/ProfileSetupPage' ;
7+ import { DiscoveryPage } from './pages/DiscoveryPage' ;
8+ import { useAuthStore } from './stores/authStore' ;
49import { useDevData , type DevUser , type DevRelationship } from './hooks/useDevData' ;
510
611const DEV_MODE = import . meta. env . DEV ;
712
8- type Identity = 'CLIENT' | 'WINGMAN' ;
9-
1013function App ( ) {
1114 if ( DEV_MODE ) {
1215 return (
1316 < >
1417 < AmbientBackground />
1518 < NoiseOverlay />
16- < DevLoginPage />
19+ < DevApp />
1720 </ >
1821 ) ;
1922 }
@@ -22,28 +25,40 @@ function App() {
2225 < >
2326 < AmbientBackground />
2427 < NoiseOverlay />
25- < ProdLoginPage />
28+ < ProdApp />
2629 </ >
2730 ) ;
2831}
2932
30- // ─── DEV Mode: Selection-based Login ───────────── ──────────────
33+ // ─── DEV Mode: Selection-based Login (preserved) ──────────────
3134
32- function DevLoginPage ( ) {
35+ function DevApp ( ) {
3336 const { users, relationships, loading } = useDevData ( ) ;
34- const [ identity , setIdentity ] = useState < Identity | null > ( null ) ;
37+ const [ identity , setIdentity ] = useState < 'CLIENT' | 'WINGMAN' | null > ( null ) ;
3538 const [ selectedUser , setSelectedUser ] = useState < DevUser | null > ( null ) ;
3639 const [ selectedRel , setSelectedRel ] = useState < DevRelationship | null > ( null ) ;
3740 const [ started , setStarted ] = useState ( false ) ;
41+ const [ useRealAuth , setUseRealAuth ] = useState ( false ) ;
42+
43+ // Use authStore page state for new pages
44+ const page = useAuthStore ( ( s ) => s . page ) ;
45+ const authUser = useAuthStore ( ( s ) => s . user ) ;
46+ const chatRelationshipId = useAuthStore ( ( s ) => s . chatRelationshipId ) ;
47+ const fetchMe = useAuthStore ( ( s ) => s . fetchMe ) ;
48+ const exitChat = useAuthStore ( ( s ) => s . exitChat ) ;
49+ const setPage = useAuthStore ( ( s ) => s . setPage ) ;
50+
51+ // Try to resume session
52+ useEffect ( ( ) => {
53+ fetchMe ( ) ;
54+ } , [ fetchMe ] ) ;
3855
39- // Filter users by selected identity
4056 const filteredUsers = useMemo ( ( ) => {
4157 if ( ! identity ) return [ ] ;
4258 if ( identity === 'CLIENT' ) return users . filter ( ( u ) => u . roles . includes ( 'CLIENT' ) ) ;
4359 return users . filter ( ( u ) => u . roles . includes ( 'WINGMAN' ) ) ;
4460 } , [ users , identity ] ) ;
4561
46- // Filter relationships by selected user
4762 const filteredRels = useMemo ( ( ) => {
4863 if ( ! selectedUser ) return [ ] ;
4964 if ( identity === 'CLIENT' ) {
@@ -56,17 +71,13 @@ function DevLoginPage() {
5671 ) ;
5772 } , [ relationships , selectedUser , identity ] ) ;
5873
59- // Compute ChatPage props
6074 const chatUserId = selectedUser ?. id ?? '' ;
61- const chatRelationshipId = selectedRel ?. id ?? '' ;
75+ const chatRelationshipIdDev = selectedRel ?. id ?? '' ;
6276
63- // 当事人:wingmanId = 己方军师 ID,privateChatTargetId = 同上
64- // 军师:wingmanId = undefined(不控制模式),privateChatTargetId = 己方当事人 ID
6577 const { chatWingmanId, chatPrivateTargetId } = useMemo ( ( ) => {
6678 if ( ! selectedRel || ! selectedUser ) return { chatWingmanId : undefined , chatPrivateTargetId : undefined } ;
6779
6880 if ( identity === 'CLIENT' ) {
69- // 判断当事人是 user1 还是 user2,取对应 side 的军师
7081 const side = selectedRel . user1 . id === selectedUser . id ? 1 : 2 ;
7182 const assignment = selectedRel . assignments . find ( ( a ) => a . side === side ) ;
7283 return {
@@ -75,7 +86,6 @@ function DevLoginPage() {
7586 } ;
7687 }
7788
78- // WINGMAN:私聊对象是己方当事人
7989 const assignment = selectedRel . assignments . find ( ( a ) => a . userId === selectedUser . id ) ;
8090 const clientUser = assignment ?. side === 1 ? selectedRel . user1 : selectedRel . user2 ;
8191 return {
@@ -84,14 +94,40 @@ function DevLoginPage() {
8494 } ;
8595 } , [ identity , selectedRel , selectedUser ] ) ;
8696
87- if ( started && chatUserId && chatRelationshipId ) {
97+ // ── All hooks above this line. Early returns below. ──
98+
99+ // If in chat mode (from discovery acceptance)
100+ if ( page === 'chat' && authUser && chatRelationshipId ) {
101+ return (
102+ < ChatPage
103+ userId = { authUser . id }
104+ relationshipId = { chatRelationshipId }
105+ onExit = { exitChat }
106+ />
107+ ) ;
108+ }
109+
110+ // If authenticated, show production-style pages
111+ if ( authUser && page !== 'login' && page !== 'register' ) {
112+ if ( page === 'profile-setup' ) return < ProfileSetupPage /> ;
113+ if ( page === 'discovery' ) return < DiscoveryPage /> ;
114+ }
115+
116+ // If user chose real auth flow
117+ if ( useRealAuth ) {
118+ if ( page === 'register' ) return < > < AmbientBackground /> < NoiseOverlay /> < RegisterPage /> </ > ;
119+ if ( page === 'discovery' && authUser ) return < > < AmbientBackground /> < NoiseOverlay /> < DiscoveryPage /> </ > ;
120+ return < > < AmbientBackground /> < NoiseOverlay /> < LoginPage /> </ > ;
121+ }
122+
123+ if ( started && chatUserId && chatRelationshipIdDev ) {
88124 return (
89125 < >
90126 < AmbientBackground />
91127 < NoiseOverlay />
92128 < ChatPage
93129 userId = { chatUserId }
94- relationshipId = { chatRelationshipId }
130+ relationshipId = { chatRelationshipIdDev }
95131 wingmanId = { chatWingmanId }
96132 privateChatTargetId = { chatPrivateTargetId }
97133 onExit = { ( ) => {
@@ -140,6 +176,15 @@ function DevLoginPage() {
140176 >
141177 DEV MODE
142178 </ motion . span >
179+ < div className = "mt-4" >
180+ < button
181+ onClick = { ( ) => { setUseRealAuth ( true ) ; setPage ( 'login' ) ; } }
182+ className = "text-xs transition-colors"
183+ style = { { color : '#8ca0ff' } }
184+ >
185+ 使用注册/登录 →
186+ </ button >
187+ </ div >
143188 </ motion . div >
144189
145190 { loading ? (
@@ -170,12 +215,11 @@ function DevLoginPage() {
170215 </ div >
171216 </ StepSection >
172217
173- { /* Step 2: User */ }
174218 { identity && (
175219 < StepSection label = "2. 选择用户" >
176220 < div className = "space-y-2" >
177221 { filteredUsers . map ( ( u ) => (
178- < UserCard
222+ < DevUserCard
179223 key = { u . id }
180224 user = { u }
181225 selected = { selectedUser ?. id === u . id }
@@ -190,7 +234,6 @@ function DevLoginPage() {
190234 </ StepSection >
191235 ) }
192236
193- { /* Step 3: Relationship */ }
194237 { selectedUser && (
195238 < StepSection label = "3. 选择聊天室" >
196239 < div className = "space-y-2" >
@@ -209,7 +252,6 @@ function DevLoginPage() {
209252 </ StepSection >
210253 ) }
211254
212- { /* Enter button */ }
213255 { selectedRel && (
214256 < motion . button
215257 onClick = { ( ) => setStarted ( true ) }
@@ -243,6 +285,45 @@ function DevLoginPage() {
243285 ) ;
244286}
245287
288+ // ─── Prod Mode: Auth-based routing ───────────────────────────
289+
290+ function ProdApp ( ) {
291+ const page = useAuthStore ( ( s ) => s . page ) ;
292+ const user = useAuthStore ( ( s ) => s . user ) ;
293+ const loading = useAuthStore ( ( s ) => s . loading ) ;
294+ const chatRelationshipId = useAuthStore ( ( s ) => s . chatRelationshipId ) ;
295+ const fetchMe = useAuthStore ( ( s ) => s . fetchMe ) ;
296+ const exitChat = useAuthStore ( ( s ) => s . exitChat ) ;
297+
298+ useEffect ( ( ) => {
299+ fetchMe ( ) ;
300+ } , [ fetchMe ] ) ;
301+
302+ if ( loading ) {
303+ return (
304+ < div className = "min-h-screen flex items-center justify-center" >
305+ < p className = "text-sm" style = { { color : '#9e98aa' } } > 加载中...</ p >
306+ </ div >
307+ ) ;
308+ }
309+
310+ if ( page === 'chat' && user && chatRelationshipId ) {
311+ return (
312+ < ChatPage
313+ userId = { user . id }
314+ relationshipId = { chatRelationshipId }
315+ onExit = { exitChat }
316+ />
317+ ) ;
318+ }
319+
320+ if ( page === 'register' ) return < RegisterPage /> ;
321+ if ( page === 'profile-setup' ) return < ProfileSetupPage /> ;
322+ if ( page === 'discovery' && user ) return < DiscoveryPage /> ;
323+
324+ return < LoginPage /> ;
325+ }
326+
246327// ─── Shared UI Components ──────────────────────────────────────
247328
248329function StepSection ( { label, children } : { label : string ; children : React . ReactNode } ) {
@@ -273,7 +354,7 @@ function SelectCard({ selected, onClick, label, desc }: {
273354 ) ;
274355}
275356
276- function UserCard ( { user, selected, onClick, isWingman } : {
357+ function DevUserCard ( { user, selected, onClick, isWingman } : {
277358 user : DevUser ; selected : boolean ; onClick : ( ) => void ; isWingman : boolean ;
278359} ) {
279360 const icon = isWingman ? '\uD83C\uDFAF' : '\uD83D\uDC64' ;
@@ -345,94 +426,6 @@ function RelationshipCard({ rel, selected, onClick }: {
345426 ) ;
346427}
347428
348- // ─── Prod Mode: Placeholder for future login ───────────────────
349-
350- function ProdLoginPage ( ) {
351- const [ userId , setUserId ] = useState ( '' ) ;
352- const [ relationshipId , setRelationshipId ] = useState ( '' ) ;
353- const [ started , setStarted ] = useState ( false ) ;
354-
355- if ( started && userId && relationshipId ) {
356- return (
357- < >
358- < AmbientBackground />
359- < NoiseOverlay />
360- < ChatPage
361- userId = { userId }
362- relationshipId = { relationshipId }
363- onExit = { ( ) => setStarted ( false ) }
364- />
365- </ >
366- ) ;
367- }
368-
369- return (
370- < div className = "min-h-screen flex items-center justify-center px-4 relative" >
371- < div className = "w-full max-w-sm" >
372- < div className = "text-center mb-10" >
373- < h1
374- className = "text-ink text-[56px] font-light tracking-wide"
375- style = { { fontFamily : 'var(--font-serif)' , letterSpacing : '0.02em' } }
376- >
377- Violet
378- </ h1 >
379- < p className = "mt-2 text-sm font-light" style = { { color : '#5a627a' } } >
380- 对话即心跳 — 校园恋爱代聊平台
381- </ p >
382- </ div >
383- < div className = "glass rounded-[28px] p-6 space-y-4" >
384- < div >
385- < label className = "block text-xs font-light mb-1.5" style = { { color : '#7a829a' } } >
386- 用户 ID
387- </ label >
388- < input
389- type = "text"
390- value = { userId }
391- onChange = { ( e ) => setUserId ( e . target . value ) }
392- placeholder = "输入你的用户 ID"
393- className = "w-full h-11 px-4 rounded-2xl text-sm outline-none transition-all"
394- style = { {
395- background : 'rgba(255, 255, 255, 0.5)' ,
396- color : '#3a405a' ,
397- border : '1px solid rgba(140, 160, 255, 0.15)' ,
398- } }
399- />
400- </ div >
401- < div >
402- < label className = "block text-xs font-light mb-1.5" style = { { color : '#7a829a' } } >
403- 关系 ID
404- </ label >
405- < input
406- type = "text"
407- value = { relationshipId }
408- onChange = { ( e ) => setRelationshipId ( e . target . value ) }
409- placeholder = "输入关系 ID"
410- className = "w-full h-11 px-4 rounded-2xl text-sm outline-none transition-all"
411- style = { {
412- background : 'rgba(255, 255, 255, 0.5)' ,
413- color : '#3a405a' ,
414- border : '1px solid rgba(140, 160, 255, 0.15)' ,
415- } }
416- />
417- </ div >
418- < button
419- onClick = { ( ) => setStarted ( true ) }
420- disabled = { ! userId . trim ( ) || ! relationshipId . trim ( ) }
421- className = "w-full h-11 rounded-2xl text-sm font-medium transition-all disabled:opacity-30"
422- style = { {
423- background : '#8ca0ff' ,
424- color : '#ffffff' ,
425- boxShadow : '0 8px 24px rgba(140, 160, 255, 0.35)' ,
426- } }
427- >
428- 进入聊天
429- </ button >
430- </ div >
431- </ div >
432- </ div >
433- ) ;
434- }
435-
436429// ─── Background Components ─────────────────────────────────────
437430
438431function NoiseOverlay ( ) {
0 commit comments