import { Ionicons } from "@expo/vector-icons"; import { useState } from "react"; import { Modal, Pressable, ScrollView, StyleSheet, Text, View, } from "react-native"; import { fonts, radii, spacing } from "@/constants/theme"; import { useAccounts } from "@/contexts/AccountsContext"; import { useSession } from "@/contexts/AuthContext"; import { startAdditionalAccountSignIn } from "@/lib/add-account"; import { useAppTheme } from "@/contexts/ThemeContext"; import { formatServerHost } from "@/lib/server-mode"; function initials(name: string, email: string) { const source = name.trim() || email.trim(); const parts = source.split(/\s+/).filter(Boolean); if (parts.length >= 2) { return `${parts[0]![0] ?? ""}${parts[1]![0] ?? ""}`.toUpperCase(); } return (source[0] ?? "?").toUpperCase(); } function displayName(name: string, email: string) { const trimmed = name.trim(); if (trimmed) return trimmed.split(/\s+/)[0] ?? trimmed; return email.split("@")[0] ?? email; } /** Header control to switch signed-in accounts or add another. */ export function AccountSwitcher() { const { colors } = useAppTheme(); const { data: session } = useSession(); const { accounts, activeAccount, activeAccountId, switchAccount, clearActiveAccount, } = useAccounts(); const [open, setOpen] = useState(false); const label = displayName( activeAccount?.name ?? session?.user.name ?? "", activeAccount?.email ?? session?.user.email ?? "", ); const avatar = initials( activeAccount?.name ?? session?.user.name ?? "", activeAccount?.email ?? session?.user.email ?? "", ); async function handleAddAccount() { setOpen(false); await startAdditionalAccountSignIn(clearActiveAccount); } async function handleSwitch(accountId: string) { if (accountId === activeAccountId) { setOpen(false); return; } setOpen(false); await switchAccount(accountId); } return ( <> setOpen(true)} style={styles.hit} > {avatar} {label} setOpen(false)} transparent visible={open}> setOpen(false)}> event.stopPropagation()} > Accounts setOpen(false)}> Done {accounts.map((account) => { const isActive = account.id === activeAccountId; return ( void handleSwitch(account.id)} style={({ pressed }) => [ styles.accountRow, { borderBottomColor: colors.border, backgroundColor: isActive ? colors.muted : "transparent", }, pressed && styles.pressed, ]} > {initials(account.name, account.email)} {account.name || account.email} {account.email} {formatServerHost(account.instanceUrl)} {isActive ? ( ) : null} ); })} void handleAddAccount()} style={({ pressed }) => [ styles.addRow, { borderTopColor: colors.border }, pressed && styles.pressed, ]} > Add account ); } const styles = StyleSheet.create({ hit: { flexShrink: 1, maxWidth: "58%", }, row: { flexDirection: "row", alignItems: "center", gap: 6, paddingLeft: 4, paddingRight: 8, minHeight: 32, borderRadius: radii.pill, }, avatar: { width: 24, height: 24, borderRadius: 12, alignItems: "center", justifyContent: "center", }, avatarText: { fontFamily: fonts.bodySemiBold, fontSize: 11, }, name: { flexShrink: 1, fontFamily: fonts.bodyMedium, fontSize: 14, lineHeight: 18, }, backdrop: { flex: 1, justifyContent: "flex-end", backgroundColor: "rgba(0,0,0,0.45)", }, sheet: { borderTopLeftRadius: radii.xl, borderTopRightRadius: radii.xl, maxHeight: "70%", }, sheetHeader: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingHorizontal: spacing.lg, paddingVertical: spacing.md, borderBottomWidth: 1, }, sheetTitle: { fontFamily: fonts.bodySemiBold, fontSize: 16, }, done: { fontFamily: fonts.bodySemiBold, fontSize: 16, }, accountRow: { flexDirection: "row", alignItems: "center", gap: spacing.md, paddingHorizontal: spacing.lg, paddingVertical: spacing.md, borderBottomWidth: StyleSheet.hairlineWidth, }, accountMeta: { flex: 1, gap: 2, }, accountName: { fontFamily: fonts.bodyMedium, fontSize: 15, }, accountSub: { fontFamily: fonts.body, fontSize: 12, lineHeight: 16, }, addRow: { flexDirection: "row", alignItems: "center", justifyContent: "center", gap: spacing.sm, paddingVertical: spacing.lg, borderTopWidth: StyleSheet.hairlineWidth, }, addLabel: { fontFamily: fonts.bodySemiBold, fontSize: 15, }, pressed: { opacity: 0.75, }, });