eletrotupi / tcc/ frontend/app/auth/activate.tsx6bdc28a3adcc45cf37288df8777dfa536ce0f07a
4.6 KB | 169 lines Raw
1
import React, { useEffect, useRef, useState } from 'react';
2
import { Pressable, StyleSheet, View } from 'react-native';
3
import { router } from 'expo-router';
4
import { useThemeColor } from '@/hooks/use-theme-color';
5
import { Button, CodeInput } from '@/components/ui';
6
import type { CodeInputHandle } from '@/components/ui/CodeInput';
7
import { Text } from '@/components/ui/Text';
8
import { AuthScreen } from '@/components/auth/AuthScreen';
9
import { Colors } from '@/constants/theme';
10
import { useAuth } from '@/context/AuthContext';
11
import { useToast } from '@/context/ToastContext';
12
import { translateError } from '@/lib/errors/translations';
13

14
const CODE_LENGTH = 6;
15
const RESEND_COOLDOWN_SECONDS = 60;
16

17
export default function ActivateScreen() {
18
  const [code, setCode] = useState('');
19
  const [codeError, setCodeError] = useState<string>();
20
  const [loading, setLoading] = useState(false);
21
  const [secondsLeft, setSecondsLeft] = useState(RESEND_COOLDOWN_SECONDS);
22
  const codeInputRef = useRef<CodeInputHandle>(null);
23

24
  const { activate, requestActivateCode, logout } = useAuth();
25
  const { showToast } = useToast();
26
  const textColor = useThemeColor({}, 'text');
27
  const tintColor = useThemeColor({}, 'tint');
28

29
  useEffect(() => {
30
    if (secondsLeft <= 0) return;
31

32
    const timer = setTimeout(() => setSecondsLeft((value) => value - 1), 1000);
33
    return () => clearTimeout(timer);
34
  }, [secondsLeft]);
35

36
  const submitCode = async (value: string) => {
37
    if (loading) return;
38

39
    if (value.length !== CODE_LENGTH) {
40
      setCodeError(`O código deve ter ${CODE_LENGTH} dígitos`);
41
      return;
42
    }
43

44
    setCodeError(undefined);
45
    setLoading(true);
46
    try {
47
      await activate(value);
48
      showToast('Conta ativada com sucesso!', 'success');
49
      router.replace('/');
50
    } catch (error) {
51
      setCode('');
52
      setCodeError(
53
        translateError(error) ||
54
          'Código incorreto. Verifique o código e tente novamente.',
55
      );
56
      codeInputRef.current?.focus();
57
    } finally {
58
      setLoading(false);
59
    }
60
  };
61

62
  const handleCodeChange = (value: string) => {
63
    setCode(value);
64
    setCodeError(undefined);
65
  };
66

67
  // Abandons this half-created session. Nothing exists on an inactive account
68
  // yet, so there is nothing to warn about — it just clears the local session
69
  // and drops back to login.
70
  const backToLogin = async () => {
71
    await logout();
72
    router.replace('/auth/login');
73
  };
74

75
  const resendCode = async () => {
76
    if (secondsLeft > 0) return;
77

78
    setSecondsLeft(RESEND_COOLDOWN_SECONDS);
79
    try {
80
      await requestActivateCode();
81
      showToast('Novo código enviado. Verifique sua caixa de entrada.', 'success');
82
    } catch {
83
      setSecondsLeft(0);
84
      showToast('Falha ao reenviar o código. Tente novamente.', 'error');
85
    }
86
  };
87

88
  return (
89
    <AuthScreen
90
      title="Ativar a conta"
91
      subtitle="Digite o código de 6 dígitos enviado para seu email"
92
      footer={
93
        <View style={styles.footer}>
94
          <Text style={[styles.footerText, { color: textColor, opacity: 0.7 }]}>
95
            Não recebeu?{' '}
96
          </Text>
97

98
          <Button
99
            variant="outline"
100
            title={
101
              secondsLeft > 0
102
                ? `Reenviar em ${secondsLeft}s`
103
                : 'Reenviar um novo código'
104
            }
105
            onPress={resendCode}
106
            disabled={secondsLeft > 0}
107
          />
108

109
          <Pressable
110
            onPress={backToLogin}
111
            style={styles.backLink}
112
            testID="button-back-to-login"
113
          >
114
            <Text style={[styles.backLinkText, { color: tintColor }]}>
115
              Voltar para login
116
            </Text>
117
          </Pressable>
118
        </View>
119
      }
120
    >
121
      <View style={styles.form}>
122
        <CodeInput
123
          ref={codeInputRef}
124
          value={code}
125
          onChange={handleCodeChange}
126
          onComplete={submitCode}
127
          length={CODE_LENGTH}
128
          error={codeError}
129
          editable={!loading}
130
          accessibilityLabel="Código de ativação de 6 dígitos"
131
          testIdPrefix="activate-code"
132
        />
133

134
        <Button
135
          title="Ativar conta"
136
          onPress={() => submitCode(code)}
137
          loading={loading}
138
          style={styles.activateButton}
139
        />
140
      </View>
141
    </AuthScreen>
142
  );
143
}
144

145
const styles = StyleSheet.create({
146
  form: {
147
    marginBottom: 32,
148
  },
149
  activateButton: {
150
    marginTop: 8,
151
  },
152
  footer: {
153
    borderTopColor: Colors.light.accentBlue,
154
    borderTopWidth: 1,
155
    paddingTop: 20,
156
  },
157
  footerText: {
158
    textAlign: 'center',
159
    marginBottom: 10,
160
  },
161
  backLink: {
162
    marginTop: 20,
163
    alignItems: 'center',
164
  },
165
  backLinkText: {
166
    fontSize: 14,
167
    fontWeight: '500',
168
  },
169
});