fix(mobile): honor auth failures and tighten selectors
This commit is contained in:
parent
217ed13329
commit
6f8d70186c
@ -1,14 +1,14 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { Stack } from 'expo-router';
|
import { Stack } from 'expo-router';
|
||||||
import { StatusBar } from 'expo-status-bar';
|
import { StatusBar } from 'expo-status-bar';
|
||||||
import { useAuthStore } from '../store/auth-store';
|
import { useAuthStore, type AuthState } from '../store/auth-store';
|
||||||
import { useNotesStore } from '../store/notes-store';
|
import { useNotesStore, type NotesState } from '../store/notes-store';
|
||||||
import { useWorkspaceStore } from '../store/workspace-store';
|
import { useWorkspaceStore, type WorkspaceState } from '../store/workspace-store';
|
||||||
|
|
||||||
export default function RootLayout() {
|
export default function RootLayout() {
|
||||||
const bootstrapAuth = useAuthStore((state) => state.bootstrap);
|
const bootstrapAuth = useAuthStore((state: AuthState) => state.bootstrap);
|
||||||
const hydrateNotes = useNotesStore((state) => state.hydrate);
|
const hydrateNotes = useNotesStore((state: NotesState) => state.hydrate);
|
||||||
const hydrateWorkspaces = useWorkspaceStore((state) => state.hydrate);
|
const hydrateWorkspaces = useWorkspaceStore((state: WorkspaceState) => state.hydrate);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void bootstrapAuth();
|
void bootstrapAuth();
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { colors } from '../theme';
|
|||||||
export default function AuthScreen() {
|
export default function AuthScreen() {
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||||
const signIn = useAuthStore((state: AuthState) => state.signIn);
|
const signIn = useAuthStore((state: AuthState) => state.signIn);
|
||||||
const isLoading = useAuthStore((state: AuthState) => state.isLoading);
|
const isLoading = useAuthStore((state: AuthState) => state.isLoading);
|
||||||
|
|
||||||
@ -17,24 +18,36 @@ export default function AuthScreen() {
|
|||||||
<TextInput
|
<TextInput
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
keyboardType="email-address"
|
keyboardType="email-address"
|
||||||
onChangeText={setEmail}
|
onChangeText={(value: string) => {
|
||||||
|
setErrorMessage(null);
|
||||||
|
setEmail(value);
|
||||||
|
}}
|
||||||
placeholder="Email"
|
placeholder="Email"
|
||||||
placeholderTextColor={colors.textTertiary}
|
placeholderTextColor={colors.textTertiary}
|
||||||
style={styles.input}
|
style={styles.input}
|
||||||
value={email}
|
value={email}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
onChangeText={setPassword}
|
onChangeText={(value: string) => {
|
||||||
|
setErrorMessage(null);
|
||||||
|
setPassword(value);
|
||||||
|
}}
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
placeholderTextColor={colors.textTertiary}
|
placeholderTextColor={colors.textTertiary}
|
||||||
secureTextEntry
|
secureTextEntry
|
||||||
style={styles.input}
|
style={styles.input}
|
||||||
value={password}
|
value={password}
|
||||||
/>
|
/>
|
||||||
|
{errorMessage ? <Text style={styles.errorText}>{errorMessage}</Text> : null}
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
await signIn(email, password);
|
const didSignIn = await signIn(email, password);
|
||||||
router.replace('/(tabs)');
|
if (didSignIn) {
|
||||||
|
router.replace('/(tabs)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setErrorMessage('Sign-in failed. Check your credentials or connection and try again.');
|
||||||
}}
|
}}
|
||||||
style={styles.button}
|
style={styles.button}
|
||||||
>
|
>
|
||||||
@ -82,4 +95,8 @@ const styles = StyleSheet.create({
|
|||||||
color: colors.textPrimary,
|
color: colors.textPrimary,
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
},
|
},
|
||||||
|
errorText: {
|
||||||
|
color: colors.danger,
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -6,7 +6,7 @@ export type AuthState = {
|
|||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
email: string | null;
|
email: string | null;
|
||||||
bootstrap: () => Promise<void>;
|
bootstrap: () => Promise<void>;
|
||||||
signIn: (email: string, password: string) => Promise<void>;
|
signIn: (email: string, password: string) => Promise<boolean>;
|
||||||
signOut: () => void;
|
signOut: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -27,7 +27,8 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|||||||
const me = await client.getMe();
|
const me = await client.getMe();
|
||||||
set({ isAuthenticated: true, email: me.email, isLoading: false });
|
set({ isAuthenticated: true, email: me.email, isLoading: false });
|
||||||
} catch {
|
} catch {
|
||||||
set({ isAuthenticated: true, email: 'demo@bytelyst.local', isLoading: false });
|
client.clearTokens();
|
||||||
|
set({ isAuthenticated: false, email: null, isLoading: false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async signIn(email: string, password: string) {
|
async signIn(email: string, password: string) {
|
||||||
@ -36,8 +37,11 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|||||||
try {
|
try {
|
||||||
await getAuthClient().login(email, password);
|
await getAuthClient().login(email, password);
|
||||||
set({ isAuthenticated: true, email, isLoading: false });
|
set({ isAuthenticated: true, email, isLoading: false });
|
||||||
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
set({ isAuthenticated: true, email, isLoading: false });
|
getAuthClient().clearTokens();
|
||||||
|
set({ isAuthenticated: false, email: null, isLoading: false });
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
signOut() {
|
signOut() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user