Fix mobile account session selection
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Redirect, router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { ActivityIndicator, Pressable, StyleSheet, Text, View } from "react-native";
|
||||
|
||||
import { AuthCard } from "@/components/auth/AuthCard";
|
||||
import { AuthCardHeader } from "@/components/auth/AuthCardHeader";
|
||||
import { AuthScreenLayout } from "@/components/auth/AuthScreenLayout";
|
||||
import { Button } from "@/components/ui/Button";
|
||||
import { fonts, radii, spacing } from "@/constants/theme";
|
||||
import { useAccounts } from "@/contexts/AccountsContext";
|
||||
import { useAppTheme } from "@/contexts/ThemeContext";
|
||||
import { startAdditionalAccountSignIn } from "@/lib/add-account";
|
||||
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();
|
||||
}
|
||||
|
||||
export default function SelectAccountScreen() {
|
||||
const { colors } = useAppTheme();
|
||||
const { accounts, activeAccountId, switchAccount, clearActiveAccount } = useAccounts();
|
||||
const [selectingId, setSelectingId] = useState<string | null>(null);
|
||||
const [adding, setAdding] = useState(false);
|
||||
|
||||
async function handleSelect(accountId: string) {
|
||||
if (selectingId) return;
|
||||
setSelectingId(accountId);
|
||||
try {
|
||||
await switchAccount(accountId);
|
||||
router.replace("/(auth)/sign-in");
|
||||
} finally {
|
||||
setSelectingId(null);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAddAccount() {
|
||||
if (adding) return;
|
||||
setAdding(true);
|
||||
try {
|
||||
await startAdditionalAccountSignIn(clearActiveAccount);
|
||||
} finally {
|
||||
setAdding(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (activeAccountId || accounts.length === 0) {
|
||||
return <Redirect href="/(auth)/sign-in" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthScreenLayout>
|
||||
<AuthCard>
|
||||
<AuthCardHeader
|
||||
title="Choose account"
|
||||
description="Select the workspace account to use on this device"
|
||||
/>
|
||||
|
||||
<View style={styles.list}>
|
||||
{accounts.map((account) => {
|
||||
const isSelecting = selectingId === account.id;
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
disabled={Boolean(selectingId)}
|
||||
key={account.id}
|
||||
onPress={() => void handleSelect(account.id)}
|
||||
style={({ pressed }) => [
|
||||
styles.accountRow,
|
||||
{ backgroundColor: colors.muted, borderColor: colors.border },
|
||||
pressed && styles.pressed,
|
||||
]}
|
||||
>
|
||||
<View style={[styles.avatar, { backgroundColor: colors.primary }]}>
|
||||
<Text style={[styles.avatarText, { color: colors.primaryForeground }]}>
|
||||
{initials(account.name, account.email)}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.accountMeta}>
|
||||
<Text style={[styles.accountName, { color: colors.foreground }]}>
|
||||
{account.name || account.email}
|
||||
</Text>
|
||||
<Text style={[styles.accountSub, { color: colors.mutedForeground }]}>
|
||||
{account.email}
|
||||
</Text>
|
||||
<Text style={[styles.accountSub, { color: colors.mutedForeground }]}>
|
||||
{formatServerHost(account.instanceUrl)}
|
||||
</Text>
|
||||
</View>
|
||||
{isSelecting ? (
|
||||
<ActivityIndicator color={colors.primary} size="small" />
|
||||
) : (
|
||||
<Ionicons name="chevron-forward" size={18} color={colors.mutedForeground} />
|
||||
)}
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
|
||||
<Button
|
||||
disabled={Boolean(selectingId)}
|
||||
loading={adding}
|
||||
onPress={() => void handleAddAccount()}
|
||||
title="Sign in to another account"
|
||||
variant="secondary"
|
||||
/>
|
||||
</AuthCard>
|
||||
</AuthScreenLayout>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
list: {
|
||||
gap: spacing.sm,
|
||||
},
|
||||
accountRow: {
|
||||
minHeight: 72,
|
||||
borderRadius: radii.lg,
|
||||
borderWidth: 1,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: spacing.md,
|
||||
padding: spacing.md,
|
||||
},
|
||||
avatar: {
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 18,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
avatarText: {
|
||||
fontFamily: fonts.bodySemiBold,
|
||||
fontSize: 13,
|
||||
},
|
||||
accountMeta: {
|
||||
flex: 1,
|
||||
gap: 2,
|
||||
},
|
||||
accountName: {
|
||||
fontFamily: fonts.bodySemiBold,
|
||||
fontSize: 15,
|
||||
lineHeight: 20,
|
||||
},
|
||||
accountSub: {
|
||||
fontFamily: fonts.body,
|
||||
fontSize: 12,
|
||||
lineHeight: 16,
|
||||
},
|
||||
pressed: {
|
||||
opacity: 0.9,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user