Skip to content

Commit f4fb976

Browse files
committed
Allow users to edit PIN
1 parent 8572ea6 commit f4fb976

File tree

1 file changed

+140
-11
lines changed
  • packages/mobile-app/app/menu/security

1 file changed

+140
-11
lines changed
Lines changed: 140 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,154 @@
1-
import { StatusBar } from "expo-status-bar";
2-
import { Button, StyleSheet, Text, View } from "react-native";
3-
import { useRouter } from "expo-router";
1+
import { StyleSheet, View } from "react-native";
2+
import { Button, Card, Layout, Text } from "@ui-kitten/components";
3+
import { useState } from "react";
4+
import { PinInputComponent } from "@/components/PinInputComponent";
5+
import { useFacade } from "@/data/facades";
6+
7+
const MIN_PIN_LENGTH = 4;
8+
const MAX_PIN_LENGTH = 8;
49

510
export default function MenuSecurity() {
6-
const router = useRouter();
11+
const [step, setStep] = useState<"initial" | "pin" | "confirm">("initial");
12+
const [pinValue, setPinValue] = useState("");
13+
const [confirmPinValue, setConfirmPinValue] = useState("");
14+
const [error, setError] = useState<string | null>(null);
15+
16+
const facade = useFacade();
17+
const setAppSetting = facade.setAppSetting.useMutation();
18+
19+
const isPinValid = (pin: string) => {
20+
return (
21+
pin.length >= MIN_PIN_LENGTH &&
22+
pin.length <= MAX_PIN_LENGTH &&
23+
/^[0-9]*$/.test(pin)
24+
);
25+
};
26+
27+
const handlePinChange = (value: string, isConfirm: boolean) => {
28+
setError(null);
29+
if (isConfirm) {
30+
setConfirmPinValue(value);
31+
} else {
32+
setPinValue(value);
33+
}
34+
};
35+
36+
const handleContinue = async () => {
37+
if (step === "pin") {
38+
if (!isPinValid(pinValue)) {
39+
setError("PIN must be 4-8 digits");
40+
return;
41+
}
42+
setStep("confirm");
43+
setConfirmPinValue(""); // Reset confirmation value
44+
return;
45+
}
46+
47+
if (step === "confirm") {
48+
if (pinValue !== confirmPinValue) {
49+
setError("PINs do not match");
50+
setConfirmPinValue(""); // Reset confirmation value on mismatch
51+
return;
52+
}
53+
54+
try {
55+
await setAppSetting.mutateAsync({
56+
key: "pin",
57+
value: pinValue,
58+
});
59+
// Reset to initial state after successful PIN change
60+
setStep("initial");
61+
setPinValue("");
62+
setConfirmPinValue("");
63+
} catch {
64+
setError("Failed to save PIN. Please try again.");
65+
}
66+
}
67+
};
68+
69+
const getPromptText = () => {
70+
if (step === "pin") return "Enter your new PIN (4-8 digits).";
71+
if (step === "confirm") return "Retype your new PIN to confirm.";
72+
return "";
73+
};
74+
75+
const isStepValid = () => {
76+
if (step === "pin") return isPinValid(pinValue);
77+
if (step === "confirm") return isPinValid(confirmPinValue);
78+
return true;
79+
};
780

881
return (
9-
<View style={styles.container}>
10-
<Button title="Back" onPress={() => router.dismiss()} />
11-
<Text>Edit Passcode</Text>
12-
<Text>Face ID</Text>
13-
<StatusBar style="auto" />
14-
</View>
82+
<Layout style={styles.container} level="1">
83+
{step === "initial" ? (
84+
<Card style={styles.card}>
85+
<Text category="h6" style={styles.cardTitle}>
86+
Security Settings
87+
</Text>
88+
<Button onPress={() => setStep("pin")}>Edit PIN</Button>
89+
</Card>
90+
) : (
91+
<View style={styles.content}>
92+
<PinInputComponent
93+
pinLength={step === "pin" ? MAX_PIN_LENGTH : pinValue.length}
94+
onPinChange={(value) => handlePinChange(value, step === "confirm")}
95+
error={error}
96+
setError={setError}
97+
promptText={getPromptText()}
98+
value={step === "pin" ? pinValue : confirmPinValue}
99+
/>
100+
101+
<Layout
102+
style={{
103+
width: "100%",
104+
}}
105+
>
106+
<Button
107+
style={styles.button}
108+
disabled={!isStepValid()}
109+
onPress={handleContinue}
110+
>
111+
{step === "confirm" ? "Submit" : "Continue"}
112+
</Button>
113+
<Button
114+
style={styles.button}
115+
appearance="outline"
116+
onPress={() => {
117+
setStep("initial");
118+
setPinValue("");
119+
setConfirmPinValue("");
120+
setError("");
121+
}}
122+
>
123+
Cancel
124+
</Button>
125+
</Layout>
126+
</View>
127+
)}
128+
</Layout>
15129
);
16130
}
17131

18132
const styles = StyleSheet.create({
19133
container: {
20134
flex: 1,
21135
backgroundColor: "#fff",
136+
},
137+
content: {
138+
flex: 1,
139+
justifyContent: "space-between",
22140
alignItems: "center",
23-
justifyContent: "center",
141+
paddingHorizontal: 16,
142+
paddingVertical: 32,
143+
},
144+
card: {
145+
margin: 16,
146+
},
147+
cardTitle: {
148+
marginBottom: 16,
149+
},
150+
button: {
151+
width: "100%",
152+
marginTop: 16,
24153
},
25154
});

0 commit comments

Comments
 (0)