- Sign-up emails a confirmation link (24h); unconfirmed accounts see a banner on the web with "send it again", and status in Settings. - Change email (needs the password): the new address gets a confirmation link and only replaces the old one once clicked; the old address gets a heads-up with a reset-password link. Pending changes can be cancelled. - Change password (needs the current one): signs out other sessions. - Password fields have Show/Hide on web and mobile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
This commit is contained in:
@@ -97,22 +97,37 @@ export function Button({
|
||||
);
|
||||
}
|
||||
|
||||
/** A text field. Password fields (secureTextEntry) get a Show/Hide button. */
|
||||
export const Field = forwardRef<TextInput, TextInputProps & { label?: string }>(function Field(
|
||||
{ label, style, onFocus, onBlur, ...props },
|
||||
{ label, style, onFocus, onBlur, secureTextEntry, ...props },
|
||||
ref,
|
||||
) {
|
||||
const [focused, setFocused] = useState(false);
|
||||
const [revealed, setRevealed] = useState(false);
|
||||
return (
|
||||
<View style={{ gap: space.xs }}>
|
||||
{label && <Text style={styles.label}>{label}</Text>}
|
||||
<TextInput
|
||||
ref={ref}
|
||||
placeholderTextColor={colors.muted}
|
||||
style={[styles.input, focused && styles.inputFocused, style]}
|
||||
onFocus={(e) => (setFocused(true), onFocus?.(e))}
|
||||
onBlur={(e) => (setFocused(false), onBlur?.(e))}
|
||||
{...props}
|
||||
/>
|
||||
<View>
|
||||
<TextInput
|
||||
ref={ref}
|
||||
placeholderTextColor={colors.muted}
|
||||
style={[styles.input, focused && styles.inputFocused, secureTextEntry && { paddingRight: 64 }, style]}
|
||||
onFocus={(e) => (setFocused(true), onFocus?.(e))}
|
||||
onBlur={(e) => (setFocused(false), onBlur?.(e))}
|
||||
secureTextEntry={secureTextEntry && !revealed}
|
||||
{...props}
|
||||
/>
|
||||
{secureTextEntry && (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={revealed ? 'Hide password' : 'Show password'}
|
||||
onPress={() => setRevealed((v) => !v)}
|
||||
hitSlop={8}
|
||||
style={styles.reveal}>
|
||||
<Text style={styles.revealText}>{revealed ? 'Hide' : 'Show'}</Text>
|
||||
</Pressable>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
@@ -175,6 +190,8 @@ const styles = StyleSheet.create({
|
||||
buttonText: { color: colors.text, fontWeight: '700', fontSize: 15 },
|
||||
buttonTextSmall: { fontSize: 13 },
|
||||
label: { color: colors.text, fontWeight: '600', fontSize: 14 },
|
||||
reveal: { position: 'absolute', right: 0, top: 0, bottom: 0, justifyContent: 'center', paddingHorizontal: space.md },
|
||||
revealText: { color: colors.accent2, fontWeight: '600', fontSize: 14 },
|
||||
input: {
|
||||
minHeight: 48,
|
||||
paddingHorizontal: space.md + 2,
|
||||
|
||||
Reference in New Issue
Block a user