import React, { useCallback, useEffect, useState } from "react"; import { View } from "react-native"; import { useAuth } from "../../hooks/useAuth"; import { useNotificationContext } from "../../context/NotificationContext"; import AuthService from "../../services/auth.service"; import { Button } from "../button"; import { Card, CardContent } from "../card"; import { EmailInput, Input, PasswordInput } from "../input"; // FormLabelText, not FormLabel: FormLabel's Root renders a bare // Pressable (it's meant to wrap an input for accessibility linking), // not a Text component. Every use below is a plain static section // caption with a string child and no press behavior — using the // Pressable-based Root left these unstyled (no explicit color/size), // inheriting a low-contrast platform default instead of the intended // text-foreground/dark:text-white, which read as "fields have no // labels" and "text color is wrong" on mobile. import { FormLabelText as Label } from "../label"; export interface ProfileTabProps { /** Whether the surrounding dialog is currently open — drives state reset. */ visible: boolean; } /** * "Profile" tab body: edit display name + email. Drops in unchanged * between vault and bootstrap because every consumer of entropy-ui's * AuthProvider already supports the corresponding AuthService methods. */ export function ProfileTab({ visible }: ProfileTabProps) { const { user, refreshUser } = useAuth(); const { push } = useNotificationContext(); const [displayName, setDisplayName] = useState(""); const [displayNameLoading, setDisplayNameLoading] = useState(false); const [displayNameError, setDisplayNameError] = useState(""); const [newEmail, setNewEmail] = useState(""); const [emailPassword, setEmailPassword] = useState(""); const [emailLoading, setEmailLoading] = useState(false); const [emailError, setEmailError] = useState(""); useEffect(() => { if (visible) { return; } setDisplayName(""); setDisplayNameError(""); setNewEmail(""); setEmailPassword(""); setEmailError(""); }, [visible]); const handleSaveDisplayName = useCallback(async () => { setDisplayNameError(""); const trimmed = displayName.trim(); if (!trimmed) { setDisplayNameError("Display name is required"); return; } if (trimmed.length > 100) { setDisplayNameError("Max 100 characters"); return; } setDisplayNameLoading(true); try { await AuthService.updateDisplayName(trimmed); await refreshUser(); setDisplayName(""); push({ level: "success", title: "Display name updated" }); } catch (err) { setDisplayNameError(err instanceof Error ? err.message : "Failed to update"); } finally { setDisplayNameLoading(false); } }, [displayName, refreshUser, push]); const handleSaveEmail = useCallback(async () => { setEmailError(""); if (!AuthService.validateEmail(newEmail)) { setEmailError("Invalid email address"); return; } if (!emailPassword) { setEmailError("Password is required"); return; } setEmailLoading(true); try { await AuthService.changeEmail(newEmail, emailPassword); await refreshUser(); setNewEmail(""); setEmailPassword(""); push({ level: "success", title: "Email updated" }); } catch (err) { setEmailError(err instanceof Error ? err.message : "Failed to update email"); } finally { setEmailLoading(false); } }, [newEmail, emailPassword, refreshUser, push]); return ( ); }