Files
paseo/packages/app/src/screens/sessions-screen.tsx
T
chyendongnhanh338andGitHub 08ebe5e7e1 Complete client UI i18n migration
Adds client-side i18n support, language settings, translated UI copy, and locale parity coverage.
2026-06-11 14:15:35 +08:00

124 lines
3.8 KiB
TypeScript

import { useMemo, useState, useCallback, useEffect } from "react";
import { View, Text } from "react-native";
import { useIsFocused } from "@react-navigation/native";
import { router } from "expo-router";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import { ChevronLeft } from "lucide-react-native";
import { useTranslation } from "react-i18next";
import { MenuHeader } from "@/components/headers/menu-header";
import { Button } from "@/components/ui/button";
import { LoadingSpinner } from "@/components/ui/loading-spinner";
import { AgentList } from "@/components/agent-list";
import { useAgentHistory } from "@/hooks/use-agent-history";
import { buildHostOpenProjectRoute } from "@/utils/host-routes";
export function SessionsScreen({ serverId }: { serverId: string }) {
const isFocused = useIsFocused();
if (!isFocused) {
return <View style={styles.container} />;
}
return <SessionsScreenContent serverId={serverId} />;
}
function SessionsScreenContent({ serverId }: { serverId: string }) {
const { theme } = useUnistyles();
const { t } = useTranslation();
const { agents, hasMore, isInitialLoad, isLoadingMore, isRevalidating, loadMore, refreshAll } =
useAgentHistory({
serverId,
});
// Track user-initiated refresh to avoid showing spinner on background revalidation
const [isManualRefresh, setIsManualRefresh] = useState(false);
const handleRefresh = useCallback(() => {
setIsManualRefresh(true);
refreshAll();
}, [refreshAll]);
// Reset manual refresh flag when revalidation completes
useEffect(() => {
if (!isRevalidating && isManualRefresh) {
setIsManualRefresh(false);
}
}, [isRevalidating, isManualRefresh]);
const sortedAgents = useMemo(() => {
return [...agents].sort((a, b) => b.lastActivityAt.getTime() - a.lastActivityAt.getTime());
}, [agents]);
const handleBack = useCallback(() => {
router.navigate(buildHostOpenProjectRoute(serverId));
}, [serverId]);
const listFooterComponent = useMemo(
() =>
hasMore ? (
<View style={styles.footer}>
<Button variant="ghost" onPress={loadMore} disabled={isLoadingMore}>
{isLoadingMore ? t("common.loading") : t("sessions.actions.loadMore")}
</Button>
</View>
) : null,
[hasMore, loadMore, isLoadingMore, t],
);
return (
<View style={styles.container}>
<MenuHeader title={t("sessions.title")} />
{isInitialLoad ? (
<View style={styles.loadingContainer}>
<LoadingSpinner size="large" color={theme.colors.foregroundMuted} />
</View>
) : null}
{!isInitialLoad && sortedAgents.length === 0 ? (
<View style={styles.emptyContainer}>
<Text style={styles.emptyText}>{t("sessions.empty")}</Text>
<Button variant="ghost" leftIcon={ChevronLeft} onPress={handleBack}>
{t("common.actions.back")}
</Button>
</View>
) : null}
{!isInitialLoad && sortedAgents.length > 0 ? (
<AgentList
agents={sortedAgents}
showCheckoutInfo={false}
isRefreshing={isManualRefresh && isRevalidating}
onRefresh={handleRefresh}
listFooterComponent={listFooterComponent}
showAttentionIndicator={false}
/>
) : null}
</View>
);
}
const styles = StyleSheet.create((theme) => ({
container: {
flex: 1,
backgroundColor: theme.colors.surface0,
},
emptyContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
gap: theme.spacing[6],
padding: theme.spacing[6],
},
emptyText: {
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.lg,
},
loadingContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
footer: {
alignItems: "center",
paddingVertical: theme.spacing[4],
},
}));