fix: ios-tv work ok

This commit is contained in:
James Chen
2025-09-22 15:16:13 +08:00
parent aa7efb0dfb
commit e6194a50ab
11 changed files with 405 additions and 232 deletions

View File

@@ -1,5 +1,5 @@
import React, { useState, useRef, useEffect } from "react";
import { Modal, View, TextInput, StyleSheet, ActivityIndicator, Alert } from "react-native";
import { Modal, View, TextInput, StyleSheet, ActivityIndicator, Alert, Keyboard, InteractionManager } from "react-native";
import { usePathname } from "expo-router";
import Toast from "react-native-toast-message";
import useAuthStore from "@/stores/authStore";
@@ -23,9 +23,14 @@ const LoginModal = () => {
const pathname = usePathname();
const isSettingsPage = pathname.includes("settings");
const [isModalReady, setIsModalReady] = useState(false);
// Load saved credentials when modal opens
useEffect(() => {
if (isLoginModalVisible && !isSettingsPage) {
// 先确保键盘状态清理
Keyboard.dismiss();
const loadCredentials = async () => {
const savedCredentials = await LoginCredentialsManager.get();
if (savedCredentials) {
@@ -34,12 +39,22 @@ const LoginModal = () => {
}
};
loadCredentials();
// 延迟设置 Modal 就绪状态
const readyTimeout = setTimeout(() => {
setIsModalReady(true);
}, 300);
return () => {
clearTimeout(readyTimeout);
setIsModalReady(false);
};
}
}, [isLoginModalVisible, isSettingsPage]);
// Focus management with better TV remote handling
useEffect(() => {
if (isLoginModalVisible && !isSettingsPage) {
if (isModalReady && isLoginModalVisible && !isSettingsPage) {
const isUsernameVisible = serverConfig?.StorageType !== "localstorage";
// Use a small delay to ensure the modal is fully rendered
@@ -49,11 +64,19 @@ const LoginModal = () => {
} else {
passwordInputRef.current?.focus();
}
}, 100);
}, 300);
return () => clearTimeout(focusTimeout);
}
}, [isLoginModalVisible, serverConfig, isSettingsPage]);
}, [isModalReady, isLoginModalVisible, serverConfig, isSettingsPage]);
// 清理 effect - 确保 Modal 关闭时清理所有状态
useEffect(() => {
return () => {
Keyboard.dismiss();
setIsModalReady(false);
};
}, []);
const handleLogin = async () => {
const isLocalStorage = serverConfig?.StorageType === "localstorage";
@@ -66,19 +89,38 @@ const LoginModal = () => {
await api.login(isLocalStorage ? undefined : username, password);
await checkLoginStatus(apiBaseUrl);
await refreshPlayRecords();
// Save credentials on successful login
await LoginCredentialsManager.save({ username, password });
Toast.show({ type: "success", text1: "登录成功" });
hideLoginModal();
// Show disclaimer alert after successful login
Alert.alert(
"免责声明",
"本应用仅提供影视信息搜索服务,所有内容均来自第三方网站。本站不存储任何视频资源,不对任何内容的准确性、合法性、完整性负责。",
[{ text: "确定" }]
);
Toast.show({ type: "success", text1: "登录成功" });
// hideLoginModal();
// // Show disclaimer alert after successful login
// Alert.alert(
// "免责声明",
// "本应用仅提供影视信息搜索服务,所有内容均来自第三方网站。本站不存储任何视频资源,不对任何内容的准确性、合法性、完整性负责。",
// [{ text: "确定" }]
// );
// 在登录成功后清理状态,再显示 Alert
const hideAndAlert = () => {
hideLoginModal();
setIsModalReady(false);
Keyboard.dismiss();
setTimeout(() => {
Alert.alert(
"免责声明",
"本应用仅提供影视信息搜索服务,所有内容均来自第三方网站。本站不存储任何视频资源,不对任何内容的准确性、合法性、完整性负责。",
[{ text: "确定" }]
);
}, 100);
};
// 使用 InteractionManager 确保 UI 稳定后再执行
InteractionManager.runAfterInteractions(hideAndAlert);
} catch (error) {
Toast.show({
type: "error",

View File

@@ -1,5 +1,5 @@
import React, { useCallback } from "react";
import { View, Switch, StyleSheet, Pressable, Animated, Platform } from "react-native";
import { View, Switch, StyleSheet, Pressable, Animated, Platform, TouchableOpacity } from "react-native";
import { useTVEventHandler } from "react-native";
import { ThemedText } from "@/components/ThemedText";
import { SettingsSection } from "./SettingsSection";
@@ -59,20 +59,31 @@ export const RemoteInputSection: React.FC<RemoteInputSectionProps> = ({ onChange
return (
<SettingsSection focusable onFocus={handleSectionFocus} onBlur={handleSectionBlur}
{...Platform.isTV||deviceType !=='tv'? undefined :{onPress:handlePress}}
>
{...Platform.isTV || deviceType !== 'tv' ? undefined : { onPress: handlePress }}
>
<Pressable style={styles.settingItem} onFocus={handleSectionFocus} onBlur={handleSectionBlur}>
<View style={styles.settingInfo}>
<ThemedText style={styles.settingName}></ThemedText>
</View>
<Animated.View style={animationStyle}>
<Switch
value={remoteInputEnabled}
onValueChange={() => {}} // 禁用Switch的直接交互
trackColor={{ false: "#767577", true: Colors.dark.primary }}
thumbColor={remoteInputEnabled ? "#ffffff" : "#f4f3f4"}
pointerEvents="none"
/>
{ Platform.OS === 'ios' && Platform.isTV ? (
<TouchableOpacity
activeOpacity={0.8}
onPress={() => handlePress()}
style={styles.statusLabel}
>
<ThemedText style={styles.statusValue}>{remoteInputEnabled ? '已启用' : '已禁用'}</ThemedText>
</TouchableOpacity>
) : (
<Switch
value={remoteInputEnabled}
onValueChange={() => { }} // 禁用Switch的直接交互
trackColor={{ false: "#767577", true: Colors.dark.primary }}
thumbColor={remoteInputEnabled ? "#ffffff" : "#f4f3f4"}
pointerEvents="none"
/>
)
}
</Animated.View>
</Pressable>