mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-07-15 17:02:58 +01:00
feat: save login credentials when switching servers
This commit is contained in:
@@ -1,31 +1,83 @@
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type React from "react";
|
||||
import { useMemo } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { ActivityIndicator, Alert, TouchableOpacity, View } from "react-native";
|
||||
import { useMMKVString } from "react-native-mmkv";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import {
|
||||
deleteServerCredential,
|
||||
type SavedServer,
|
||||
} from "@/utils/secureCredentials";
|
||||
import { ListGroup } from "./list/ListGroup";
|
||||
import { ListItem } from "./list/ListItem";
|
||||
|
||||
interface Server {
|
||||
address: string;
|
||||
}
|
||||
|
||||
interface PreviousServersListProps {
|
||||
onServerSelect: (server: Server) => void;
|
||||
onServerSelect: (server: SavedServer) => void;
|
||||
onQuickLogin?: (serverUrl: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export const PreviousServersList: React.FC<PreviousServersListProps> = ({
|
||||
onServerSelect,
|
||||
onQuickLogin,
|
||||
}) => {
|
||||
const [_previousServers, setPreviousServers] =
|
||||
useMMKVString("previousServers");
|
||||
const [loadingServer, setLoadingServer] = useState<string | null>(null);
|
||||
|
||||
const previousServers = useMemo(() => {
|
||||
return JSON.parse(_previousServers || "[]") as Server[];
|
||||
return JSON.parse(_previousServers || "[]") as SavedServer[];
|
||||
}, [_previousServers]);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleServerPress = async (server: SavedServer) => {
|
||||
if (loadingServer) return; // Prevent double-tap
|
||||
|
||||
if (server.hasCredentials && onQuickLogin) {
|
||||
// Quick login with saved credentials
|
||||
setLoadingServer(server.address);
|
||||
try {
|
||||
await onQuickLogin(server.address);
|
||||
} catch (_error) {
|
||||
// Token expired/invalid, fall back to manual login
|
||||
Alert.alert(
|
||||
t("server.session_expired"),
|
||||
t("server.please_login_again"),
|
||||
[{ text: t("common.ok"), onPress: () => onServerSelect(server) }],
|
||||
);
|
||||
} finally {
|
||||
setLoadingServer(null);
|
||||
}
|
||||
} else {
|
||||
onServerSelect(server);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveCredential = async (serverUrl: string) => {
|
||||
Alert.alert(
|
||||
t("server.remove_saved_login"),
|
||||
t("server.remove_saved_login_description"),
|
||||
[
|
||||
{ text: t("common.cancel"), style: "cancel" },
|
||||
{
|
||||
text: t("common.remove"),
|
||||
style: "destructive",
|
||||
onPress: async () => {
|
||||
await deleteServerCredential(serverUrl);
|
||||
// Update UI
|
||||
const updated = previousServers.map((s) =>
|
||||
s.address === serverUrl
|
||||
? { ...s, hasCredentials: false, username: undefined }
|
||||
: s,
|
||||
);
|
||||
setPreviousServers(JSON.stringify(updated));
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
};
|
||||
|
||||
if (!previousServers.length) return null;
|
||||
|
||||
return (
|
||||
@@ -34,10 +86,33 @@ export const PreviousServersList: React.FC<PreviousServersListProps> = ({
|
||||
{previousServers.map((s) => (
|
||||
<ListItem
|
||||
key={s.address}
|
||||
onPress={() => onServerSelect(s)}
|
||||
title={s.address}
|
||||
showArrow
|
||||
/>
|
||||
onPress={() => handleServerPress(s)}
|
||||
title={s.name || s.address}
|
||||
subtitle={
|
||||
s.hasCredentials
|
||||
? `${s.username} • ${t("server.saved")}`
|
||||
: s.name
|
||||
? s.address
|
||||
: undefined
|
||||
}
|
||||
showArrow={loadingServer !== s.address}
|
||||
disabled={loadingServer === s.address}
|
||||
>
|
||||
{loadingServer === s.address ? (
|
||||
<ActivityIndicator size='small' color={Colors.primary} />
|
||||
) : s.hasCredentials ? (
|
||||
<TouchableOpacity
|
||||
onPress={(e) => {
|
||||
e.stopPropagation();
|
||||
handleRemoveCredential(s.address);
|
||||
}}
|
||||
className='p-1'
|
||||
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
||||
>
|
||||
<Ionicons name='key' size={16} color={Colors.primary} />
|
||||
</TouchableOpacity>
|
||||
) : null}
|
||||
</ListItem>
|
||||
))}
|
||||
<ListItem
|
||||
onPress={() => {
|
||||
|
||||
Reference in New Issue
Block a user