mirror of
https://github.com/zimplexing/OrionTV.git
synced 2026-02-04 03:36:29 +08:00
feat: Support remote input
This commit is contained in:
@@ -7,6 +7,8 @@ import { Platform } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { useRemoteControlStore } from "@/stores/remoteControlStore";
|
||||
import { remoteControlService } from "@/services/remoteControlService";
|
||||
|
||||
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
||||
SplashScreen.preventAutoHideAsync();
|
||||
@@ -31,6 +33,23 @@ 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 (!loaded && !error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ThemedView } from "@/components/ThemedView";
|
||||
import { StyledButton } from "@/components/StyledButton";
|
||||
import { AVPlaybackStatus } from "expo-av";
|
||||
|
||||
const M3U_URL = "https://raw.githubusercontent.com/fanmingming/live/refs/heads/main/tv/m3u/ipv6.m3u";
|
||||
const M3U_URL = "https://raw.githubusercontent.com/sjnhnp/adblock/refs/heads/main/filtered_http_only_valid.m3u";
|
||||
|
||||
export default function LiveScreen() {
|
||||
const [channels, setChannels] = useState<Channel[]>([]);
|
||||
|
||||
@@ -4,8 +4,10 @@ import { ThemedView } from "@/components/ThemedView";
|
||||
import { ThemedText } from "@/components/ThemedText";
|
||||
import VideoCard from "@/components/VideoCard.tv";
|
||||
import { api, SearchResult } from "@/services/api";
|
||||
import { Search } from "lucide-react-native";
|
||||
import { Search, QrCode } from "lucide-react-native";
|
||||
import { StyledButton } from "@/components/StyledButton";
|
||||
import { useRemoteControlStore } from "@/stores/remoteControlStore";
|
||||
import { RemoteControlModal } from "@/components/RemoteControlModal";
|
||||
|
||||
export default function SearchScreen() {
|
||||
const [keyword, setKeyword] = useState("");
|
||||
@@ -15,6 +17,15 @@ export default function SearchScreen() {
|
||||
const textInputRef = useRef<TextInput>(null);
|
||||
const colorScheme = "dark"; // Replace with useColorScheme() if needed
|
||||
const [isInputFocused, setIsInputFocused] = useState(false);
|
||||
const { showModal: showRemoteModal, lastMessage } = useRemoteControlStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (lastMessage) {
|
||||
const realMessage = lastMessage.split("_")[0];
|
||||
setKeyword(realMessage);
|
||||
handleSearch(realMessage);
|
||||
}
|
||||
}, [lastMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
// Focus the text input when the screen loads
|
||||
@@ -24,8 +35,9 @@ export default function SearchScreen() {
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const handleSearch = async () => {
|
||||
if (!keyword.trim()) {
|
||||
const handleSearch = async (searchText?: string) => {
|
||||
const term = typeof searchText === "string" ? searchText : keyword;
|
||||
if (!term.trim()) {
|
||||
Keyboard.dismiss();
|
||||
return;
|
||||
}
|
||||
@@ -33,7 +45,7 @@ export default function SearchScreen() {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await api.searchVideos(keyword);
|
||||
const response = await api.searchVideos(term);
|
||||
if (response.results.length > 0) {
|
||||
setResults(response.results);
|
||||
} else {
|
||||
@@ -47,6 +59,8 @@ export default function SearchScreen() {
|
||||
}
|
||||
};
|
||||
|
||||
const onSearchPress = () => handleSearch();
|
||||
|
||||
const renderItem = ({ item }: { item: SearchResult }) => (
|
||||
<VideoCard
|
||||
id={item.id.toString()}
|
||||
@@ -78,12 +92,15 @@ export default function SearchScreen() {
|
||||
onChangeText={setKeyword}
|
||||
onFocus={() => setIsInputFocused(true)}
|
||||
onBlur={() => setIsInputFocused(false)}
|
||||
onSubmitEditing={handleSearch} // Allow searching with remote 'enter' button
|
||||
onSubmitEditing={onSearchPress}
|
||||
returnKeyType="search"
|
||||
/>
|
||||
<StyledButton style={styles.searchButton} onPress={handleSearch}>
|
||||
<StyledButton style={styles.searchButton} onPress={onSearchPress}>
|
||||
<Search size={24} color={colorScheme === "dark" ? "white" : "black"} />
|
||||
</StyledButton>
|
||||
<StyledButton style={styles.qrButton} onPress={showRemoteModal}>
|
||||
<QrCode size={24} color={colorScheme === "dark" ? "white" : "black"} />
|
||||
</StyledButton>
|
||||
</View>
|
||||
|
||||
{loading ? (
|
||||
@@ -108,6 +125,7 @@ export default function SearchScreen() {
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<RemoteControlModal />
|
||||
</ThemedView>
|
||||
);
|
||||
}
|
||||
@@ -140,6 +158,11 @@ const styles = StyleSheet.create({
|
||||
// backgroundColor is now set dynamically
|
||||
borderRadius: 8,
|
||||
},
|
||||
qrButton: {
|
||||
padding: 12,
|
||||
borderRadius: 8,
|
||||
marginLeft: 10,
|
||||
},
|
||||
centerContainer: {
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
|
||||
Reference in New Issue
Block a user