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')}
);
}