55 */
66
77import { ApolloProvider } from '@apollo/client' ;
8- import { useEffect } from 'react' ;
8+ import { useEffect , useState } from 'react' ;
99import {
1010 ActivityIndicator ,
11+ Modal ,
1112 Pressable ,
1213 StatusBar ,
1314 StyleSheet ,
@@ -22,7 +23,12 @@ import {
2223import { apolloClient , logout } from './src/auth/authClient' ;
2324import { useDeepLinkListener } from './src/auth/deepLinks' ;
2425import { LoginScreen } from './src/auth/LoginScreen' ;
25- import { tokenStore , useAuth , useAuthHydrated } from './src/auth/tokenStore' ;
26+ import {
27+ type AuthUser ,
28+ tokenStore ,
29+ useAuth ,
30+ useAuthHydrated ,
31+ } from './src/auth/tokenStore' ;
2632import { MapScreen } from './src/map/MapScreen' ;
2733
2834function App ( ) {
@@ -41,7 +47,6 @@ function App() {
4147function AppContent ( ) {
4248 const hydrated = useAuthHydrated ( ) ;
4349 const auth = useAuth ( ) ;
44- const safeAreaInsets = useSafeAreaInsets ( ) ;
4550
4651 useDeepLinkListener ( ) ;
4752
@@ -64,25 +69,63 @@ function AppContent() {
6469 return (
6570 < View style = { styles . container } >
6671 < MapScreen />
67- < View
68- style = { [ styles . logoutBar , { paddingBottom : safeAreaInsets . bottom + 12 } ] } >
69- < Text style = { styles . signedInAs } > Signed in as { auth . user . email } </ Text >
70- < Pressable
71- accessibilityRole = "button"
72- onPress = { ( ) => {
73- logout ( ) ;
74- } }
75- style = { ( { pressed} ) => [
76- styles . logoutButton ,
77- pressed && styles . logoutPressed ,
78- ] } >
79- < Text style = { styles . logoutText } > Log out</ Text >
80- </ Pressable >
81- </ View >
72+ < AccountMenu user = { auth . user } />
8273 </ View >
8374 ) ;
8475}
8576
77+ function AccountMenu ( { user} : { user : AuthUser } ) {
78+ const safeAreaInsets = useSafeAreaInsets ( ) ;
79+ const [ open , setOpen ] = useState ( false ) ;
80+
81+ const initial = user . email . trim ( ) . charAt ( 0 ) . toUpperCase ( ) || '?' ;
82+
83+ return (
84+ < >
85+ < Pressable
86+ accessibilityLabel = "Account menu"
87+ accessibilityRole = "button"
88+ onPress = { ( ) => setOpen ( true ) }
89+ style = { ( { pressed} ) => [
90+ styles . avatarButton ,
91+ { top : safeAreaInsets . top + 12 } ,
92+ pressed && styles . avatarPressed ,
93+ ] } >
94+ < Text style = { styles . avatarText } > { initial } </ Text >
95+ </ Pressable >
96+ < Modal
97+ animationType = "slide"
98+ transparent
99+ visible = { open }
100+ onRequestClose = { ( ) => setOpen ( false ) } >
101+ < Pressable style = { styles . sheetBackdrop } onPress = { ( ) => setOpen ( false ) } >
102+ < View
103+ style = { [ styles . sheet , { paddingBottom : safeAreaInsets . bottom + 12 } ] }
104+ // Stop taps inside the sheet from dismissing it via the backdrop.
105+ onStartShouldSetResponder = { ( ) => true } >
106+ < View style = { styles . sheetHandle } />
107+ < Text style = { styles . sheetEmail } numberOfLines = { 1 } >
108+ { user . email }
109+ </ Text >
110+ < Pressable
111+ accessibilityRole = "button"
112+ onPress = { ( ) => {
113+ setOpen ( false ) ;
114+ logout ( ) ;
115+ } }
116+ style = { ( { pressed} ) => [
117+ styles . sheetItem ,
118+ pressed && styles . sheetItemPressed ,
119+ ] } >
120+ < Text style = { styles . sheetItemText } > Log out</ Text >
121+ </ Pressable >
122+ </ View >
123+ </ Pressable >
124+ </ Modal >
125+ </ >
126+ ) ;
127+ }
128+
86129const styles = StyleSheet . create ( {
87130 container : {
88131 flex : 1 ,
@@ -91,35 +134,63 @@ const styles = StyleSheet.create({
91134 alignItems : 'center' ,
92135 justifyContent : 'center' ,
93136 } ,
94- logoutBar : {
137+ avatarButton : {
95138 position : 'absolute' ,
96- left : 0 ,
97- right : 0 ,
98- bottom : 0 ,
99- paddingHorizontal : 16 ,
100- paddingTop : 12 ,
101- backgroundColor : 'rgba(255,255,255,0.95)' ,
102- borderTopWidth : StyleSheet . hairlineWidth ,
103- borderTopColor : '#ccc' ,
104- flexDirection : 'row' ,
139+ right : 16 ,
140+ width : 40 ,
141+ height : 40 ,
142+ borderRadius : 20 ,
143+ backgroundColor : '#1d6fe0' ,
105144 alignItems : 'center' ,
106- justifyContent : 'space-between' ,
145+ justifyContent : 'center' ,
146+ shadowColor : '#000' ,
147+ shadowOpacity : 0.2 ,
148+ shadowRadius : 4 ,
149+ shadowOffset : { width : 0 , height : 2 } ,
150+ elevation : 4 ,
107151 } ,
108- signedInAs : {
109- fontSize : 12 ,
110- color : '#444' ,
152+ avatarPressed : { opacity : 0.8 } ,
153+ avatarText : {
154+ color : '#ffffff' ,
155+ fontSize : 16 ,
156+ fontWeight : '600' ,
157+ } ,
158+ sheetBackdrop : {
111159 flex : 1 ,
112- marginRight : 12 ,
160+ backgroundColor : 'rgba(0,0,0,0.35)' ,
161+ justifyContent : 'flex-end' ,
162+ } ,
163+ sheet : {
164+ backgroundColor : '#ffffff' ,
165+ borderTopLeftRadius : 16 ,
166+ borderTopRightRadius : 16 ,
167+ paddingTop : 8 ,
168+ paddingHorizontal : 8 ,
113169 } ,
114- logoutButton : {
170+ sheetHandle : {
171+ alignSelf : 'center' ,
172+ width : 36 ,
173+ height : 4 ,
174+ borderRadius : 2 ,
175+ backgroundColor : '#d0d0d0' ,
176+ marginBottom : 12 ,
177+ } ,
178+ sheetEmail : {
179+ fontSize : 12 ,
180+ color : '#666' ,
115181 paddingHorizontal : 12 ,
116- paddingVertical : 8 ,
117- borderRadius : 6 ,
118- backgroundColor : '#eee' ,
182+ paddingBottom : 8 ,
183+ } ,
184+ sheetItem : {
185+ paddingHorizontal : 12 ,
186+ paddingVertical : 14 ,
187+ borderRadius : 8 ,
188+ } ,
189+ sheetItemPressed : {
190+ backgroundColor : '#f2f2f2' ,
119191 } ,
120- logoutPressed : { opacity : 0.7 } ,
121- logoutText : {
122- fontSize : 14 ,
192+ sheetItemText : {
193+ fontSize : 16 ,
123194 color : '#222' ,
124195 } ,
125196} ) ;
0 commit comments