diff --git a/app/index.tsx b/app/index.tsx
index a1e25de..aa3489e 100644
--- a/app/index.tsx
+++ b/app/index.tsx
@@ -10,11 +10,11 @@ import { Search, Settings, LogOut, Heart } from "lucide-react-native";
import { StyledButton } from "@/components/StyledButton";
import useHomeStore, { RowItem, Category } from "@/stores/homeStore";
import useAuthStore from "@/stores/authStore";
-import { useSettingsStore } from "@/stores/settingsStore";
import CustomScrollView from "@/components/CustomScrollView";
import { useResponsiveLayout } from "@/hooks/useResponsiveLayout";
import { getCommonResponsiveStyles } from "@/utils/ResponsiveStyles";
import ResponsiveNavigation from "@/components/navigation/ResponsiveNavigation";
+import { useApiConfig, getApiConfigErrorMessage } from "@/hooks/useApiConfig";
const LOAD_MORE_THRESHOLD = 200;
@@ -44,7 +44,7 @@ export default function HomeScreen() {
clearError,
} = useHomeStore();
const { isLoggedIn, logout } = useAuthStore();
- const { apiBaseUrl } = useSettingsStore();
+ const apiConfigStatus = useApiConfig();
useFocusEffect(
useCallback(() => {
@@ -52,34 +52,44 @@ export default function HomeScreen() {
}, [refreshPlayRecords])
);
+ // 统一的数据获取逻辑
useEffect(() => {
- // 只有在 apiBaseUrl 存在时才调用 fetchInitialData,避免时序问题
- if (selectedCategory && !selectedCategory.tags && apiBaseUrl) {
- fetchInitialData();
- } else if (selectedCategory?.tags && !selectedCategory.tag) {
+ if (!selectedCategory) return;
+
+ // 如果是容器分类且没有选择标签,设置默认标签
+ if (selectedCategory.tags && !selectedCategory.tag) {
const defaultTag = selectedCategory.tags[0];
setSelectedTag(defaultTag);
selectCategory({ ...selectedCategory, tag: defaultTag });
+ return;
}
- }, [selectedCategory, fetchInitialData, selectCategory, apiBaseUrl]);
- useEffect(() => {
- // 只有在 apiBaseUrl 存在时才调用 fetchInitialData,避免时序问题
- if (selectedCategory && selectedCategory.tag && apiBaseUrl) {
- fetchInitialData();
+ // 只有在API配置完成且分类有效时才获取数据
+ if (apiConfigStatus.isConfigured && !apiConfigStatus.needsConfiguration) {
+ // 对于有标签的分类,需要确保有标签才获取数据
+ if (selectedCategory.tags && selectedCategory.tag) {
+ fetchInitialData();
+ }
+ // 对于无标签的分类,直接获取数据
+ else if (!selectedCategory.tags) {
+ fetchInitialData();
+ }
}
- }, [fetchInitialData, selectedCategory, selectedCategory.tag, apiBaseUrl]);
+ }, [
+ selectedCategory,
+ selectedCategory?.tag,
+ apiConfigStatus.isConfigured,
+ apiConfigStatus.needsConfiguration,
+ fetchInitialData,
+ selectCategory,
+ ]);
- // 检查是否需要显示API配置提示
- const shouldShowApiConfig = !apiBaseUrl && selectedCategory && !selectedCategory.tags;
-
- // 清除错误状态,当API未配置时
+ // 清除错误状态的逻辑
useEffect(() => {
- if (shouldShowApiConfig && error) {
- // 如果需要显示API配置提示,清除之前的错误状态
+ if (apiConfigStatus.needsConfiguration && error) {
clearError();
}
- }, [shouldShowApiConfig, error, clearError]);
+ }, [apiConfigStatus.needsConfiguration, error, clearError]);
useEffect(() => {
if (!loading && contentData.length > 0) {
@@ -119,7 +129,7 @@ export default function HomeScreen() {
);
};
- const renderContentItem = ({ item, index }: { item: RowItem; index: number }) => (
+ const renderContentItem = ({ item }: { item: RowItem; index: number }) => (
;
};
+ // 检查是否需要显示API配置提示
+ const shouldShowApiConfig = apiConfigStatus.needsConfiguration && selectedCategory && !selectedCategory.tags;
+
// TV端和平板端的顶部导航
const renderHeader = () => {
if (deviceType === "mobile") {
@@ -280,8 +293,21 @@ export default function HomeScreen() {
{/* 内容网格 */}
{shouldShowApiConfig ? (
-
- 请点击右上角设置按钮,配置您的服务器地址
+
+ {getApiConfigErrorMessage(apiConfigStatus)}
+
+
+ ) : apiConfigStatus.isValidating ? (
+
+
+
+ 正在验证服务器配置...
+
+
+ ) : apiConfigStatus.error && !apiConfigStatus.isValid ? (
+
+
+ {apiConfigStatus.error}
) : loading ? (
diff --git a/components/VideoCard.tv.tsx b/components/VideoCard.tv.tsx
index a953fde..eb7882f 100644
--- a/components/VideoCard.tv.tsx
+++ b/components/VideoCard.tv.tsx
@@ -195,7 +195,7 @@ const VideoCard = forwardRef(
{isContinueWatching && (
- 第{episodeIndex! + 1}集 已观看 {Math.round((progress || 0) * 100)}%
+ 第{episodeIndex}集 已观看 {Math.round((progress || 0) * 100)}%
)}
@@ -343,4 +343,4 @@ const styles = StyleSheet.create({
color: Colors.dark.primary,
fontSize: 12,
},
-});
\ No newline at end of file
+});
diff --git a/components/settings/UpdateSection.tsx b/components/settings/UpdateSection.tsx
index ddf6f9a..99e63e1 100644
--- a/components/settings/UpdateSection.tsx
+++ b/components/settings/UpdateSection.tsx
@@ -6,8 +6,16 @@ import { useUpdateStore } from "@/stores/updateStore";
// import { UPDATE_CONFIG } from "@/constants/UpdateConfig";
export function UpdateSection() {
- const { currentVersion, remoteVersion, updateAvailable, downloading, downloadProgress, checkForUpdate } =
- useUpdateStore();
+ const {
+ currentVersion,
+ remoteVersion,
+ updateAvailable,
+ downloading,
+ downloadProgress,
+ checkForUpdate,
+ isLatestVersion,
+ error
+ } = useUpdateStore();
const [checking, setChecking] = React.useState(false);
@@ -36,6 +44,20 @@ export function UpdateSection() {
)}
+ {isLatestVersion && remoteVersion && (
+
+ 状态
+ 已是最新版本
+
+ )}
+
+ {error && (
+
+ 检查结果
+ {error}
+
+ )}
+
{downloading && (
下载进度
@@ -96,6 +118,14 @@ const styles = StyleSheet.create({
color: "#00bb5e",
fontWeight: "bold",
},
+ latestVersion: {
+ color: "#00bb5e",
+ fontWeight: "500",
+ },
+ errorText: {
+ color: "#ff6b6b",
+ fontWeight: "500",
+ },
buttonContainer: {
flexDirection: "row",
gap: 12,
diff --git a/constants/UpdateConfig.ts b/constants/UpdateConfig.ts
index 72f15b7..688e351 100644
--- a/constants/UpdateConfig.ts
+++ b/constants/UpdateConfig.ts
@@ -7,11 +7,11 @@ export const UPDATE_CONFIG = {
// GitHub相关URL
GITHUB_RAW_URL:
- "https://ghfast.top/https://raw.githubusercontent.com/zimplexing/OrionTV/refs/heads/master/package.json",
+ "https://gh-proxy.com/https://raw.githubusercontent.com/zimplexing/OrionTV/refs/heads/master/package.json",
// 获取平台特定的下载URL
getDownloadUrl(version: string): string {
- return `https://ghfast.top/https://github.com/zimplexing/OrionTV/releases/download/v${version}/orionTV.${version}.apk`;
+ return `https://gh-proxy.com/https://github.com/zimplexing/OrionTV/releases/download/v${version}/orionTV.${version}.apk`;
},
// 是否显示更新日志
diff --git a/hooks/useApiConfig.ts b/hooks/useApiConfig.ts
new file mode 100644
index 0000000..0f870ff
--- /dev/null
+++ b/hooks/useApiConfig.ts
@@ -0,0 +1,139 @@
+import { useEffect, useState } from 'react';
+import { useSettingsStore } from '@/stores/settingsStore';
+import { api } from '@/services/api';
+
+export interface ApiConfigStatus {
+ isConfigured: boolean;
+ isValidating: boolean;
+ isValid: boolean | null;
+ error: string | null;
+ needsConfiguration: boolean;
+}
+
+export const useApiConfig = () => {
+ const { apiBaseUrl, serverConfig, isLoadingServerConfig } = useSettingsStore();
+ const [validationState, setValidationState] = useState<{
+ isValidating: boolean;
+ isValid: boolean | null;
+ error: string | null;
+ }>({
+ isValidating: false,
+ isValid: null,
+ error: null,
+ });
+
+ const isConfigured = Boolean(apiBaseUrl && apiBaseUrl.trim());
+ const needsConfiguration = !isConfigured;
+
+ // Validate API configuration when it changes
+ useEffect(() => {
+ if (!isConfigured) {
+ setValidationState({
+ isValidating: false,
+ isValid: false,
+ error: null,
+ });
+ return;
+ }
+
+ const validateConfig = async () => {
+ setValidationState(prev => ({ ...prev, isValidating: true, error: null }));
+
+ try {
+ await api.getServerConfig();
+ setValidationState({
+ isValidating: false,
+ isValid: true,
+ error: null,
+ });
+ } catch (error) {
+ let errorMessage = '服务器连接失败';
+
+ if (error instanceof Error) {
+ switch (error.message) {
+ case 'API_URL_NOT_SET':
+ errorMessage = 'API地址未设置';
+ break;
+ case 'UNAUTHORIZED':
+ errorMessage = '服务器认证失败';
+ break;
+ default:
+ if (error.message.includes('Network')) {
+ errorMessage = '网络连接失败,请检查网络或服务器地址';
+ } else if (error.message.includes('timeout')) {
+ errorMessage = '连接超时,请检查服务器地址';
+ } else if (error.message.includes('404')) {
+ errorMessage = '服务器地址无效,请检查API路径';
+ } else if (error.message.includes('500')) {
+ errorMessage = '服务器内部错误';
+ }
+ break;
+ }
+ }
+
+ setValidationState({
+ isValidating: false,
+ isValid: false,
+ error: errorMessage,
+ });
+ }
+ };
+
+ // Only validate if not already loading server config
+ if (!isLoadingServerConfig) {
+ validateConfig();
+ }
+ }, [apiBaseUrl, isConfigured, isLoadingServerConfig]);
+
+ // Reset validation when server config loading state changes
+ useEffect(() => {
+ if (isLoadingServerConfig) {
+ setValidationState(prev => ({ ...prev, isValidating: true, error: null }));
+ }
+ }, [isLoadingServerConfig]);
+
+ // Update validation state based on server config
+ useEffect(() => {
+ if (!isLoadingServerConfig && isConfigured) {
+ if (serverConfig) {
+ setValidationState(prev => ({ ...prev, isValid: true, error: null }));
+ } else {
+ setValidationState(prev => ({
+ ...prev,
+ isValid: false,
+ error: prev.error || '无法获取服务器配置'
+ }));
+ }
+ }
+ }, [serverConfig, isLoadingServerConfig, isConfigured]);
+
+ const status: ApiConfigStatus = {
+ isConfigured,
+ isValidating: validationState.isValidating || isLoadingServerConfig,
+ isValid: validationState.isValid,
+ error: validationState.error,
+ needsConfiguration,
+ };
+
+ return status;
+};
+
+export const getApiConfigErrorMessage = (status: ApiConfigStatus): string => {
+ if (status.needsConfiguration) {
+ return '请点击右上角设置按钮,配置您的服务器地址';
+ }
+
+ if (status.error) {
+ return status.error;
+ }
+
+ if (status.isValidating) {
+ return '正在验证服务器配置...';
+ }
+
+ if (status.isValid === false) {
+ return '服务器配置验证失败,请检查设置';
+ }
+
+ return '加载失败,请重试';
+};
\ No newline at end of file
diff --git a/stores/homeStore.ts b/stores/homeStore.ts
index bc808d8..cb22752 100644
--- a/stores/homeStore.ts
+++ b/stores/homeStore.ts
@@ -166,11 +166,11 @@ const useHomeStore = create((set, get) => ({
if (pageStart === 0) {
// 缓存新数据
dataCache.set(cacheKey, newItems);
- set((state) => ({
+ set({
contentData: newItems,
pageStart: result.list.length,
hasMore: true,
- }));
+ });
} else {
// 增量加载时不缓存,直接追加
set((state) => ({
@@ -187,11 +187,25 @@ const useHomeStore = create((set, get) => ({
set({ hasMore: false });
}
} catch (err: any) {
+ let errorMessage = "加载失败,请重试";
+
if (err.message === "API_URL_NOT_SET") {
- set({ error: "请点击右上角设置按钮,配置您的服务器地址" });
- } else {
- set({ error: "加载失败,请重试" });
+ errorMessage = "请点击右上角设置按钮,配置您的服务器地址";
+ } else if (err.message === "UNAUTHORIZED") {
+ errorMessage = "认证失败,请重新登录";
+ } else if (err.message.includes("Network")) {
+ errorMessage = "网络连接失败,请检查网络连接";
+ } else if (err.message.includes("timeout")) {
+ errorMessage = "请求超时,请检查网络或服务器状态";
+ } else if (err.message.includes("404")) {
+ errorMessage = "服务器API路径不正确,请检查服务器配置";
+ } else if (err.message.includes("500")) {
+ errorMessage = "服务器内部错误,请联系管理员";
+ } else if (err.message.includes("403")) {
+ errorMessage = "访问被拒绝,请检查权限设置";
}
+
+ set({ error: errorMessage });
} finally {
set({ loading: false, loadingMore: false });
}
diff --git a/stores/updateStore.ts b/stores/updateStore.ts
index 3b9a138..738774f 100644
--- a/stores/updateStore.ts
+++ b/stores/updateStore.ts
@@ -1,6 +1,7 @@
import { create } from 'zustand';
import updateService from '../services/updateService';
import AsyncStorage from '@react-native-async-storage/async-storage';
+import Toast from 'react-native-toast-message';
interface UpdateState {
// 状态
@@ -15,6 +16,7 @@ interface UpdateState {
lastCheckTime: number;
skipVersion: string | null;
showUpdateModal: boolean;
+ isLatestVersion: boolean; // 新增:是否已是最新版本
// 操作
checkForUpdate: (silent?: boolean) => Promise;
@@ -43,11 +45,12 @@ export const useUpdateStore = create((set, get) => ({
lastCheckTime: 0,
skipVersion: null,
showUpdateModal: false,
+ isLatestVersion: false, // 新增:初始为false
// 检查更新
checkForUpdate: async (silent = false) => {
try {
- set({ error: null });
+ set({ error: null, isLatestVersion: false });
// 获取跳过的版本
const skipVersion = await AsyncStorage.getItem(STORAGE_KEYS.SKIP_VERSION);
@@ -58,6 +61,9 @@ export const useUpdateStore = create((set, get) => ({
// 如果有更新且不是要跳过的版本
const shouldShowUpdate = isUpdateAvailable && versionInfo.version !== skipVersion;
+ // 检查是否已经是最新版本
+ const isLatest = !isUpdateAvailable;
+
set({
remoteVersion: versionInfo.version,
downloadUrl: versionInfo.downloadUrl,
@@ -65,8 +71,19 @@ export const useUpdateStore = create((set, get) => ({
lastCheckTime: Date.now(),
skipVersion,
showUpdateModal: shouldShowUpdate && !silent,
+ isLatestVersion: isLatest,
});
+ // 如果是手动检查且已是最新版本,显示提示
+ if (!silent && isLatest) {
+ Toast.show({
+ type: 'success',
+ text1: '已是最新版本',
+ text2: `当前版本 v${updateService.getCurrentVersion()} 已是最新版本`,
+ visibilityTime: 3000,
+ });
+ }
+
// 保存最后检查时间
await AsyncStorage.setItem(
STORAGE_KEYS.LAST_CHECK_TIME,
@@ -76,7 +93,8 @@ export const useUpdateStore = create((set, get) => ({
// console.info('检查更新失败:', error);
set({
error: error instanceof Error ? error.message : '检查更新失败',
- updateAvailable: false
+ updateAvailable: false,
+ isLatestVersion: false,
});
}
},
@@ -166,6 +184,7 @@ export const useUpdateStore = create((set, get) => ({
downloadedPath: null,
error: null,
showUpdateModal: false,
+ isLatestVersion: false, // 重置时也要重置这个状态
});
},
}));