diff --git a/App.tsx b/App.tsx index eed2d1a..46e6dff 100644 --- a/App.tsx +++ b/App.tsx @@ -5,9 +5,10 @@ */ import {ApolloProvider} from '@apollo/client'; -import {useEffect} from 'react'; +import {useEffect, useState} from 'react'; import { ActivityIndicator, + Modal, Pressable, StatusBar, StyleSheet, @@ -22,7 +23,12 @@ import { import {apolloClient, logout} from './src/auth/authClient'; import {useDeepLinkListener} from './src/auth/deepLinks'; import {LoginScreen} from './src/auth/LoginScreen'; -import {tokenStore, useAuth, useAuthHydrated} from './src/auth/tokenStore'; +import { + type AuthUser, + tokenStore, + useAuth, + useAuthHydrated, +} from './src/auth/tokenStore'; import {MapScreen} from './src/map/MapScreen'; function App() { @@ -41,7 +47,6 @@ function App() { function AppContent() { const hydrated = useAuthHydrated(); const auth = useAuth(); - const safeAreaInsets = useSafeAreaInsets(); useDeepLinkListener(); @@ -64,25 +69,63 @@ function AppContent() { return ( - - Signed in as {auth.user.email} - { - logout(); - }} - style={({pressed}) => [ - styles.logoutButton, - pressed && styles.logoutPressed, - ]}> - Log out - - + ); } +function AccountMenu({user}: {user: AuthUser}) { + const safeAreaInsets = useSafeAreaInsets(); + const [open, setOpen] = useState(false); + + const initial = user.email.trim().charAt(0).toUpperCase() || '?'; + + return ( + <> + setOpen(true)} + style={({pressed}) => [ + styles.avatarButton, + {top: safeAreaInsets.top + 12}, + pressed && styles.avatarPressed, + ]}> + {initial} + + setOpen(false)}> + setOpen(false)}> + true}> + + + {user.email} + + { + setOpen(false); + logout(); + }} + style={({pressed}) => [ + styles.sheetItem, + pressed && styles.sheetItemPressed, + ]}> + Log out + + + + + + ); +} + const styles = StyleSheet.create({ container: { flex: 1, @@ -91,35 +134,63 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, - logoutBar: { + avatarButton: { position: 'absolute', - left: 0, - right: 0, - bottom: 0, - paddingHorizontal: 16, - paddingTop: 12, - backgroundColor: 'rgba(255,255,255,0.95)', - borderTopWidth: StyleSheet.hairlineWidth, - borderTopColor: '#ccc', - flexDirection: 'row', + right: 16, + width: 40, + height: 40, + borderRadius: 20, + backgroundColor: '#1d6fe0', alignItems: 'center', - justifyContent: 'space-between', + justifyContent: 'center', + shadowColor: '#000', + shadowOpacity: 0.2, + shadowRadius: 4, + shadowOffset: {width: 0, height: 2}, + elevation: 4, }, - signedInAs: { - fontSize: 12, - color: '#444', + avatarPressed: {opacity: 0.8}, + avatarText: { + color: '#ffffff', + fontSize: 16, + fontWeight: '600', + }, + sheetBackdrop: { flex: 1, - marginRight: 12, + backgroundColor: 'rgba(0,0,0,0.35)', + justifyContent: 'flex-end', + }, + sheet: { + backgroundColor: '#ffffff', + borderTopLeftRadius: 16, + borderTopRightRadius: 16, + paddingTop: 8, + paddingHorizontal: 8, }, - logoutButton: { + sheetHandle: { + alignSelf: 'center', + width: 36, + height: 4, + borderRadius: 2, + backgroundColor: '#d0d0d0', + marginBottom: 12, + }, + sheetEmail: { + fontSize: 12, + color: '#666', paddingHorizontal: 12, - paddingVertical: 8, - borderRadius: 6, - backgroundColor: '#eee', + paddingBottom: 8, + }, + sheetItem: { + paddingHorizontal: 12, + paddingVertical: 14, + borderRadius: 8, + }, + sheetItemPressed: { + backgroundColor: '#f2f2f2', }, - logoutPressed: {opacity: 0.7}, - logoutText: { - fontSize: 14, + sheetItemText: { + fontSize: 16, color: '#222', }, }); diff --git a/src/map/MapScreen.tsx b/src/map/MapScreen.tsx index c2a2094..f1d554f 100644 --- a/src/map/MapScreen.tsx +++ b/src/map/MapScreen.tsx @@ -29,10 +29,9 @@ const DEFAULT_INITIAL_VIEW = {center: [0, 20] as [number, number], zoom: 1}; // Show the recenter button once the viewport center drifts more than this // fraction of the visible span away from the user in either axis. const OFF_CENTER_THRESHOLD = 0.2; -// Vertical clearance above the device safe area so the recenter button sits -// above the app's bottom "signed in as…" bar in App.tsx. Keep in sync if that -// bar's height changes. -const BOTTOM_BAR_CLEARANCE = 64; +// Margin above the device safe area for bottom-anchored overlays (recenter +// button, preview card). +const BOTTOM_MARGIN = 16; export function MapScreen() { const cameraRef = useRef(null); @@ -233,7 +232,7 @@ export function MapScreen() { onPress={flyToUser} style={[ styles.recenterButton, - {bottom: safeAreaInsets.bottom + BOTTOM_BAR_CLEARANCE}, + {bottom: safeAreaInsets.bottom + BOTTOM_MARGIN}, ]}> @@ -241,7 +240,7 @@ export function MapScreen() { {selectedElementId ? ( setSelectedElementId(null)} onExpand={() => setDetailExpanded(true)} />