Messenger/packages/mobile/src/features/auth/SignInScreen.tsx
Заид Омар Медхат | Zaid Omar Medhat f653b083aa f
2026-07-13 12:32:00 +05:00

157 lines
4.9 KiB
TypeScript

import { useState } from 'react';
import type { ReactElement } from 'react';
import {
ActivityIndicator,
KeyboardAvoidingView,
Platform,
Pressable,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
import { ApiError } from '@altricade/core/api';
import { useSession } from '@/stores/session';
import { Screen } from '@/components';
import { useTheme } from '@/theme';
import { spacing, radius, fontSize } from '@/theme';
type Mode = 'login' | 'register';
export const SignInScreen = (): ReactElement => {
const { colors } = useTheme();
const login = useSession((s) => s.login);
const register = useSession((s) => s.register);
const [mode, setMode] = useState<Mode>('login');
const [username, setUsername] = useState('');
const [displayName, setDisplayName] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
const submit = (): void => {
setBusy(true);
setError(null);
void (async () => {
try {
if (mode === 'login') {
await login({ username: username.trim(), password });
} else {
await register({ username: username.trim(), displayName: displayName.trim(), password });
}
} catch (caught) {
setError(caught instanceof ApiError ? caught.message : 'Something went wrong');
setBusy(false);
}
})();
};
const inputStyle = [styles.input, { backgroundColor: colors.surface, color: colors.text }];
return (
<Screen>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
style={styles.flex}
>
<View style={styles.center}>
<View style={[styles.mark, { backgroundColor: colors.accent }]}>
<Text style={[styles.markText, { color: colors.onAccent }]}>Z</Text>
</View>
<Text style={[styles.title, { color: colors.text }]}>Zovi</Text>
<Text style={[styles.tagline, { color: colors.textMuted }]}>
{mode === 'login' ? 'Welcome back.' : 'Create your account.'}
</Text>
<TextInput
style={inputStyle}
placeholder="Username"
placeholderTextColor={colors.textFaint}
autoCapitalize="none"
autoCorrect={false}
value={username}
onChangeText={setUsername}
/>
{mode === 'register' ? (
<TextInput
style={inputStyle}
placeholder="Display name"
placeholderTextColor={colors.textFaint}
value={displayName}
onChangeText={setDisplayName}
/>
) : null}
<TextInput
style={inputStyle}
placeholder="Password"
placeholderTextColor={colors.textFaint}
secureTextEntry
value={password}
onChangeText={setPassword}
/>
{error !== null ? <Text style={[styles.error, { color: colors.danger }]}>{error}</Text> : null}
<Pressable
style={[styles.primary, { backgroundColor: colors.accent }, busy && { opacity: 0.6 }]}
disabled={busy}
onPress={submit}
>
{busy ? (
<ActivityIndicator color={colors.onAccent} />
) : (
<Text style={[styles.primaryText, { color: colors.onAccent }]}>
{mode === 'login' ? 'Log in' : 'Create account'}
</Text>
)}
</Pressable>
<Pressable
onPress={() => {
setMode(mode === 'login' ? 'register' : 'login');
setError(null);
}}
>
<Text style={[styles.switch, { color: colors.accent }]}>
{mode === 'login' ? 'Need an account? Sign up' : 'Have an account? Log in'}
</Text>
</Pressable>
</View>
</KeyboardAvoidingView>
</Screen>
);
};
const styles = StyleSheet.create({
flex: { flex: 1 },
center: { flex: 1, justifyContent: 'center', paddingHorizontal: spacing.xl },
mark: {
width: 56,
height: 56,
borderRadius: 18,
alignItems: 'center',
justifyContent: 'center',
alignSelf: 'center',
marginBottom: spacing.md,
},
markText: { fontSize: 28, fontWeight: '800' },
title: { fontSize: 28, fontWeight: '700', textAlign: 'center' },
tagline: { textAlign: 'center', marginTop: spacing.xs, marginBottom: spacing.xl },
input: {
borderRadius: radius.md,
paddingHorizontal: spacing.lg,
paddingVertical: spacing.md,
fontSize: fontSize.base,
marginBottom: spacing.md,
},
error: { fontSize: fontSize.sm, marginBottom: spacing.sm },
primary: {
borderRadius: radius.md,
paddingVertical: spacing.md,
alignItems: 'center',
marginTop: spacing.xs,
},
primaryText: { fontSize: fontSize.md, fontWeight: '600' },
switch: { textAlign: 'center', marginTop: spacing.xl, fontWeight: '600' },
});