import { useAuthStore } from "@/src/stores/auth.store"; import { signOut } from "@/src/lib/auth-client"; import { useHouseholdMembers, useRevokeInvitation, type PendingInvitation, type HouseholdMember, } from "@/src/hooks/useHouseholdMembers"; import { useHouseholdSettings, useUpdateHouseholdSettings } from "@/src/hooks/useHouseholdSettings"; import { ModalHeader } from "@/src/components/ui/ModalHeader"; import { useGenerateInviteCode } from "@/src/hooks/useInvite"; import { useQueryClient } from "@tanstack/react-query"; import { useRouter } from "expo-router"; import { useState, useEffect } from "react"; import { View, Text, Pressable, ScrollView, ToastAndroid, Platform, Alert, Modal, Share, ActivityIndicator, } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Ionicons } from "@expo/vector-icons"; import { useTranslation } from "react-i18next"; import i18n from "@/src/i18n"; import * as Localization from "expo-localization"; function showToast(message: string) { if (Platform.OS === "android") { ToastAndroid.show(message, ToastAndroid.SHORT); } else { Alert.alert("", message, [{ text: "OK" }], { cancelable: true }); } } // ── Invite Code Modal ────────────────────────────────────────────────────────── function InviteCodeModal({ visible, onClose, }: { visible: boolean; onClose: () => void; }) { const { t } = useTranslation(); const { mutate: generate, data, isPending, reset } = useGenerateInviteCode(); const [copied, setCopied] = useState(false); useEffect(() => { if (visible) { reset(); setCopied(false); generate(); } }, [visible]); const code = data?.code ?? ""; async function handleShare() { if (!code) return; await Share.share({ message: t('invite.shareText', { code }) }); } async function handleCopy() { if (!code) return; await Share.share({ message: code }); setCopied(true); setTimeout(() => setCopied(false), 2000); } function handleClose() { reset(); setCopied(false); onClose(); } return ( {isPending ? ( {t('invite.generating')} ) : ( <> {/* Code display */} {code || "------"} {t('invite.validFor')} {/* Copy button */} {copied ? t('invite.copied') : t('invite.copyCode')} {/* Share button */} {t('invite.share')} {/* Regenerate link */} { setCopied(false); generate(); }} className="active:opacity-60"> {t('invite.newCode')} )} ); } // ── Members Section ──────────────────────────────────────────────────────────── function MembersSection() { const [showInviteModal, setShowInviteModal] = useState(false); const { data, isLoading } = useHouseholdMembers(); const { mutate: revoke } = useRevokeInvitation(); const currentUserId = useAuthStore((s) => s.user?.id); const { t } = useTranslation(); function handleRevoke(inv: PendingInvitation) { Alert.alert( t('settings.revokeTitle'), t('settings.revokeMessage', { email: inv.email }), [ { text: t('common.cancel'), style: "cancel" }, { text: t('settings.revoke'), style: "destructive", onPress: () => revoke(inv.id, { onSuccess: () => showToast(t('settings.revokeSuccess')) }), }, ], ); } return ( <> {t('settings.members')} {isLoading && ( )} {/* Active members */} {data?.members.map((m: HouseholdMember) => ( {m.name.charAt(0).toUpperCase()} {m.name}{m.userId === currentUserId ? ` ${t('settings.youSuffix')}` : ""} {m.email} {m.role} ))} {/* Pending invitations */} {(data?.pendingInvitations ?? []).length > 0 && ( {t('settings.pending')} {data!.pendingInvitations.map((inv: PendingInvitation) => ( {inv.email} handleRevoke(inv)} className="p-1 active:opacity-50"> ))} )} {/* Invite button */} setShowInviteModal(true)} className="mt-3 flex-row items-center justify-center gap-1.5 rounded-lg border border-blue-200 py-3 active:opacity-70" > {t('settings.invitePerson')} setShowInviteModal(false)} /> ); } // ── Main Screen ──────────────────────────────────────────────────────────────── export default function SettingsScreen() { const { user, households, activeHouseholdId, setActiveHousehold } = useAuthStore(); const queryClient = useQueryClient(); const router = useRouter(); const insets = useSafeAreaInsets(); const { t } = useTranslation(); const { data: hhSettings } = useHouseholdSettings(); const { mutate: updateSettings } = useUpdateHouseholdSettings(); // Apply saved language preference when settings load useEffect(() => { if (hhSettings?.language && hhSettings.language !== "auto") { void i18n.changeLanguage(hhSettings.language); } }, [hhSettings?.language]); function handleLanguageChange() { const deviceLanguage = Localization.getLocales()[0]?.languageCode ?? "de"; Alert.alert(t('settings.language'), undefined, [ { text: t('settings.languageAuto'), onPress: () => { void i18n.changeLanguage(deviceLanguage); updateSettings({ language: "auto" }); }, }, { text: t('settings.languageDe'), onPress: () => { void i18n.changeLanguage("de"); updateSettings({ language: "de" }); }, }, { text: t('settings.languageEn'), onPress: () => { void i18n.changeLanguage("en"); updateSettings({ language: "en" }); }, }, { text: t('common.cancel'), style: "cancel" }, ]); } async function handleSwitch(household: { id: string; name: string }) { if (household.id === activeHouseholdId) return; setActiveHousehold(household.id); await queryClient.invalidateQueries(); showToast(t('settings.switchedTo', { name: household.name })); } async function handleSignOut() { await signOut(); useAuthStore.getState().clearAuth(); router.replace("/(auth)/login"); } return ( {/* Back + Title */} router.push("/(app)/mehr")} className="mr-3 p-1"> {t('settings.title')} {/* User Info */} {t('settings.account')} {user?.name} {user?.email} {/* Household Switcher */} {t('settings.households')} {households.map((h) => ( handleSwitch(h)} className="flex-row items-center justify-between py-3 border-b border-gray-100 active:opacity-70 last:border-b-0" > {h.name} {h.role} {activeHouseholdId === h.id && ( )} ))} router.push("/(auth)/onboarding")} className="mt-3 flex-row items-center justify-center gap-1.5 rounded-lg border border-blue-200 py-3 active:opacity-70" > {t('onboarding.createHousehold')} {/* Members + Invite */} {/* Household Settings */} {t('tabs.household')} router.push("/(app)/settings/household")} className="flex-row items-center justify-between py-3 active:opacity-70" > {t('settings.householdPartner')} {/* App Settings */} {t('settings.appSection')} router.push("/(app)/settings/categories")} className="flex-row items-center justify-between py-3 border-b border-gray-100 active:opacity-70" > {t('settings.categories')} router.push("/(app)/settings/fixed-costs")} className="flex-row items-center justify-between py-3 border-b border-gray-100 active:opacity-70" > {t('settings.fixedCosts')} router.push("/(app)/settings/transfer-line-items")} className="flex-row items-center justify-between py-3 border-b border-gray-100 active:opacity-70" > {t('settings.transferItems')} {t('settings.language')} {(() => { switch (hhSettings?.language) { case "de": return t('settings.languageDe'); case "en": return t('settings.languageEn'); default: return t('settings.languageAuto'); } })()} {/* Sign Out */} {t('settings.logout')} ); }