initial
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Alert, KeyboardAvoidingView, Platform, Pressable, ScrollView, StyleSheet, Text } from 'react-native';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { api } from '@/api';
|
||||
import { useT } from '@/i18n';
|
||||
import { AppBar } from '@/components/AppBar';
|
||||
import { Button, Field, TextField } from '@/components/ui';
|
||||
import { colors, fontSize, spacing } from '@/theme';
|
||||
|
||||
const MIN_PASSWORD = 8;
|
||||
|
||||
export default function ChangePasswordScreen() {
|
||||
const router = useRouter();
|
||||
const t = useT();
|
||||
|
||||
const [current, setCurrent] = useState('');
|
||||
const [next, setNext] = useState('');
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
const change = useMutation({
|
||||
mutationFn: () => api.auth.changePassword(current, next),
|
||||
onSuccess: () => {
|
||||
// The backend keeps this device signed in and drops the others.
|
||||
Alert.alert(t('changePassword.doneTitle'), t('changePassword.doneBody'), [
|
||||
{ text: 'OK', onPress: () => router.back() },
|
||||
]);
|
||||
},
|
||||
onError: (e: any) => {
|
||||
const wrong = e?.fields?.current_password?.length;
|
||||
Alert.alert(
|
||||
t('changePassword.failed'),
|
||||
wrong ? t('changePassword.wrongCurrent') : (e?.message ?? t('common.tryAgain')),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
function onSubmit() {
|
||||
if (!current) return Alert.alert(t('changePassword.failed'), t('changePassword.wrongCurrent'));
|
||||
if (next.length < MIN_PASSWORD) {
|
||||
return Alert.alert(
|
||||
t('changePassword.failed'),
|
||||
t('register.shortPassword', { count: MIN_PASSWORD }),
|
||||
);
|
||||
}
|
||||
change.mutate();
|
||||
}
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
style={{ flex: 1, backgroundColor: colors.bgApp }}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
>
|
||||
<AppBar title={t('changePassword.title')} back />
|
||||
<ScrollView contentContainerStyle={styles.body} keyboardShouldPersistTaps="handled">
|
||||
<Field label={t('changePassword.current')}>
|
||||
<TextField
|
||||
icon="lock-closed-outline"
|
||||
value={current}
|
||||
onChangeText={setCurrent}
|
||||
secureTextEntry={!show}
|
||||
autoCapitalize="none"
|
||||
textContentType="password"
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field label={t('changePassword.next')}>
|
||||
<TextField
|
||||
icon="key-outline"
|
||||
value={next}
|
||||
onChangeText={setNext}
|
||||
placeholder={t('register.passwordHint', { count: MIN_PASSWORD })}
|
||||
secureTextEntry={!show}
|
||||
autoCapitalize="none"
|
||||
textContentType="newPassword"
|
||||
/>
|
||||
<Pressable onPress={() => setShow((v) => !v)} style={styles.showPass} hitSlop={8}>
|
||||
<Text style={styles.link}>{show ? t('login.hide') : t('login.show')}</Text>
|
||||
</Pressable>
|
||||
</Field>
|
||||
|
||||
<Button
|
||||
title={t('changePassword.submit')}
|
||||
icon="checkmark"
|
||||
size="lg"
|
||||
block
|
||||
loading={change.isPending}
|
||||
onPress={onSubmit}
|
||||
/>
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
body: { padding: spacing[4], paddingBottom: spacing[9] },
|
||||
showPass: { position: 'absolute', right: 14, top: 40 },
|
||||
link: { color: colors.teal700, fontWeight: '600', fontSize: fontSize.sm },
|
||||
});
|
||||
Reference in New Issue
Block a user