mirror of
https://github.com/zimplexing/OrionTV.git
synced 2026-02-04 03:36:29 +08:00
feat: Refactor settings management into a dedicated page with new configuration options
This commit is contained in:
@@ -18,11 +18,12 @@ export default function RootLayout() {
|
||||
const [loaded, error] = useFonts({
|
||||
SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
|
||||
});
|
||||
const initializeSettings = useSettingsStore((state) => state.loadSettings);
|
||||
const { loadSettings, remoteInputEnabled } = useSettingsStore();
|
||||
const { startServer, stopServer } = useRemoteControlStore();
|
||||
|
||||
useEffect(() => {
|
||||
initializeSettings();
|
||||
}, [initializeSettings]);
|
||||
loadSettings();
|
||||
}, [loadSettings]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loaded || error) {
|
||||
@@ -34,21 +35,12 @@ export default function RootLayout() {
|
||||
}, [loaded, error]);
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize the service with store actions to break require cycle
|
||||
const { setMessage, hideModal } = useRemoteControlStore.getState();
|
||||
remoteControlService.init({
|
||||
onMessage: setMessage,
|
||||
onHandshake: hideModal,
|
||||
});
|
||||
|
||||
// Start the remote control server on app launch
|
||||
useRemoteControlStore.getState().startServer();
|
||||
|
||||
return () => {
|
||||
// Stop the server on app close
|
||||
useRemoteControlStore.getState().stopServer();
|
||||
};
|
||||
}, []);
|
||||
if (remoteInputEnabled) {
|
||||
startServer();
|
||||
} else {
|
||||
stopServer();
|
||||
}
|
||||
}, [remoteInputEnabled, startServer, stopServer]);
|
||||
|
||||
if (!loaded && !error) {
|
||||
return null;
|
||||
@@ -62,6 +54,7 @@ export default function RootLayout() {
|
||||
{Platform.OS !== "web" && <Stack.Screen name="play" options={{ headerShown: false }} />}
|
||||
<Stack.Screen name="search" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="live" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="settings" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="+not-found" />
|
||||
</Stack>
|
||||
<Toast />
|
||||
|
||||
@@ -6,10 +6,8 @@ import { api } from "@/services/api";
|
||||
import VideoCard from "@/components/VideoCard.tv";
|
||||
import { useFocusEffect, useRouter } from "expo-router";
|
||||
import { Search, Settings } from "lucide-react-native";
|
||||
import { SettingsModal } from "@/components/SettingsModal";
|
||||
import { StyledButton } from "@/components/StyledButton";
|
||||
import useHomeStore, { RowItem, Category } from "@/stores/homeStore";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
|
||||
const NUM_COLUMNS = 5;
|
||||
const { width } = Dimensions.get("window");
|
||||
@@ -34,8 +32,6 @@ export default function HomeScreen() {
|
||||
refreshPlayRecords,
|
||||
} = useHomeStore();
|
||||
|
||||
const showSettingsModal = useSettingsStore((state) => state.showModal);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
refreshPlayRecords();
|
||||
@@ -52,14 +48,14 @@ export default function HomeScreen() {
|
||||
setSelectedTag(defaultTag);
|
||||
selectCategory({ ...selectedCategory, tag: defaultTag });
|
||||
}
|
||||
}, [selectedCategory, fetchInitialData]);
|
||||
}, [selectedCategory, fetchInitialData, selectCategory]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedCategory && selectedCategory.tag) {
|
||||
fetchInitialData();
|
||||
flatListRef.current?.scrollToOffset({ animated: false, offset: 0 });
|
||||
}
|
||||
}, [selectedCategory?.tag]);
|
||||
}, [fetchInitialData, selectedCategory, selectedCategory.tag]);
|
||||
|
||||
const handleCategorySelect = (category: Category) => {
|
||||
setSelectedTag(null);
|
||||
@@ -133,7 +129,7 @@ export default function HomeScreen() {
|
||||
>
|
||||
<Search color={colorScheme === "dark" ? "white" : "black"} size={24} />
|
||||
</StyledButton>
|
||||
<StyledButton style={styles.searchButton} onPress={showSettingsModal} variant="ghost">
|
||||
<StyledButton style={styles.searchButton} onPress={() => router.push("/settings")} variant="ghost">
|
||||
<Settings color={colorScheme === "dark" ? "white" : "black"} size={24} />
|
||||
</StyledButton>
|
||||
</View>
|
||||
@@ -207,7 +203,6 @@ export default function HomeScreen() {
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<SettingsModal />
|
||||
</ThemedView>
|
||||
);
|
||||
}
|
||||
|
||||
98
app/settings.tsx
Normal file
98
app/settings.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { View, StyleSheet, ScrollView, Alert } from "react-native";
|
||||
import { ThemedText } from "@/components/ThemedText";
|
||||
import { ThemedView } from "@/components/ThemedView";
|
||||
import { StyledButton } from "@/components/StyledButton";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { APIConfigSection } from "@/components/settings/APIConfigSection";
|
||||
import { LiveStreamSection } from "@/components/settings/LiveStreamSection";
|
||||
import { RemoteInputSection } from "@/components/settings/RemoteInputSection";
|
||||
import { PlaySourceSection } from "@/components/settings/PlaybackSourceSection";
|
||||
|
||||
export default function SettingsScreen() {
|
||||
const { loadSettings, saveSettings } = useSettingsStore();
|
||||
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
loadSettings();
|
||||
}, [loadSettings]);
|
||||
|
||||
const handleSave = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await saveSettings();
|
||||
setHasChanges(false);
|
||||
Alert.alert("成功", "设置已保存");
|
||||
} catch {
|
||||
Alert.alert("错误", "保存设置失败");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const markAsChanged = () => {
|
||||
setHasChanges(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemedView style={styles.container}>
|
||||
<View style={styles.header}>
|
||||
<ThemedText style={styles.title}>设置</ThemedText>
|
||||
</View>
|
||||
|
||||
<ScrollView style={styles.scrollView} showsVerticalScrollIndicator={false}>
|
||||
<RemoteInputSection onChanged={markAsChanged} />
|
||||
<APIConfigSection onChanged={markAsChanged} />
|
||||
<LiveStreamSection onChanged={markAsChanged} />
|
||||
<PlaySourceSection onChanged={markAsChanged} />
|
||||
</ScrollView>
|
||||
|
||||
<View style={styles.footer}>
|
||||
<StyledButton
|
||||
text={isLoading ? "保存中..." : "保存设置"}
|
||||
onPress={handleSave}
|
||||
variant="primary"
|
||||
disabled={!hasChanges || isLoading}
|
||||
style={[styles.saveButton, (!hasChanges || isLoading) && styles.disabledButton]}
|
||||
/>
|
||||
</View>
|
||||
</ThemedView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
padding: 24,
|
||||
},
|
||||
header: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: 10,
|
||||
},
|
||||
title: {
|
||||
fontSize: 32,
|
||||
fontWeight: "bold",
|
||||
paddingTop: 24,
|
||||
},
|
||||
backButton: {
|
||||
minWidth: 100,
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
},
|
||||
footer: {
|
||||
paddingTop: 24,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: "#333",
|
||||
},
|
||||
saveButton: {
|
||||
minHeight: 50,
|
||||
},
|
||||
disabledButton: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user