mirror of
https://github.com/zimplexing/OrionTV.git
synced 2026-03-07 03:07:29 +08:00
feat(ui): enhance settings sections with responsive layout and keyboard handling
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useRef, useImperativeHandle, forwardRef } from "react";
|
||||
import { View, TextInput, StyleSheet, Animated } from "react-native";
|
||||
import { View, TextInput, StyleSheet, Animated, Platform } from "react-native";
|
||||
import { useTVEventHandler } from "react-native";
|
||||
import { ThemedText } from "@/components/ThemedText";
|
||||
import { SettingsSection } from "./SettingsSection";
|
||||
@@ -7,11 +7,13 @@ import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { useRemoteControlStore } from "@/stores/remoteControlStore";
|
||||
import { useButtonAnimation } from "@/hooks/useAnimation";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import { useResponsiveLayout } from "@/hooks/useResponsiveLayout";
|
||||
|
||||
interface APIConfigSectionProps {
|
||||
onChanged: () => void;
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
onPress?: () => void;
|
||||
hideDescription?: boolean;
|
||||
}
|
||||
|
||||
@@ -20,13 +22,14 @@ export interface APIConfigSectionRef {
|
||||
}
|
||||
|
||||
export const APIConfigSection = forwardRef<APIConfigSectionRef, APIConfigSectionProps>(
|
||||
({ onChanged, onFocus, onBlur, hideDescription = false }, ref) => {
|
||||
({ onChanged, onFocus, onBlur, onPress, hideDescription = false }, ref) => {
|
||||
const { apiBaseUrl, setApiBaseUrl, remoteInputEnabled } = useSettingsStore();
|
||||
const { serverUrl } = useRemoteControlStore();
|
||||
const [isInputFocused, setIsInputFocused] = useState(false);
|
||||
const [isSectionFocused, setIsSectionFocused] = useState(false);
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
const inputAnimationStyle = useButtonAnimation(isSectionFocused, 1.01);
|
||||
const deviceType = useResponsiveLayout().deviceType;
|
||||
|
||||
const handleUrlChange = (url: string) => {
|
||||
setApiBaseUrl(url);
|
||||
@@ -60,10 +63,17 @@ export const APIConfigSection = forwardRef<APIConfigSectionRef, APIConfigSection
|
||||
[isSectionFocused]
|
||||
);
|
||||
|
||||
const handlePress = () => {
|
||||
inputRef.current?.focus();
|
||||
onPress?.();
|
||||
}
|
||||
|
||||
useTVEventHandler(handleTVEvent);
|
||||
|
||||
return (
|
||||
<SettingsSection focusable onFocus={handleSectionFocus} onBlur={handleSectionBlur}>
|
||||
<SettingsSection focusable onFocus={handleSectionFocus} onBlur={handleSectionBlur}
|
||||
{...Platform.isTV||deviceType !=='tv'? undefined :{ onPress: handlePress}}
|
||||
>
|
||||
<View style={styles.inputContainer}>
|
||||
<View style={styles.titleContainer}>
|
||||
<ThemedText style={styles.sectionTitle}>API 地址</ThemedText>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useRef, useImperativeHandle, forwardRef } from "react";
|
||||
import { View, TextInput, StyleSheet, Animated } from "react-native";
|
||||
import { View, TextInput, StyleSheet, Animated, Platform } from "react-native";
|
||||
import { useTVEventHandler } from "react-native";
|
||||
import { ThemedText } from "@/components/ThemedText";
|
||||
import { SettingsSection } from "./SettingsSection";
|
||||
@@ -7,11 +7,13 @@ import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { useRemoteControlStore } from "@/stores/remoteControlStore";
|
||||
import { useButtonAnimation } from "@/hooks/useAnimation";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import { useResponsiveLayout } from "@/hooks/useResponsiveLayout";
|
||||
|
||||
interface LiveStreamSectionProps {
|
||||
onChanged: () => void;
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
onPress?: () => void;
|
||||
}
|
||||
|
||||
export interface LiveStreamSectionRef {
|
||||
@@ -19,13 +21,14 @@ export interface LiveStreamSectionRef {
|
||||
}
|
||||
|
||||
export const LiveStreamSection = forwardRef<LiveStreamSectionRef, LiveStreamSectionProps>(
|
||||
({ onChanged, onFocus, onBlur }, ref) => {
|
||||
({ onChanged, onFocus, onBlur, onPress }, ref) => {
|
||||
const { m3uUrl, setM3uUrl, remoteInputEnabled } = useSettingsStore();
|
||||
const { serverUrl } = useRemoteControlStore();
|
||||
const [isInputFocused, setIsInputFocused] = useState(false);
|
||||
const [isSectionFocused, setIsSectionFocused] = useState(false);
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
const inputAnimationStyle = useButtonAnimation(isSectionFocused, 1.01);
|
||||
const deviceType = useResponsiveLayout().deviceType;
|
||||
|
||||
const handleUrlChange = (url: string) => {
|
||||
setM3uUrl(url);
|
||||
@@ -49,6 +52,11 @@ export const LiveStreamSection = forwardRef<LiveStreamSectionRef, LiveStreamSect
|
||||
onBlur?.();
|
||||
};
|
||||
|
||||
const handlePress = () => {
|
||||
inputRef.current?.focus();
|
||||
onPress?.();
|
||||
}
|
||||
|
||||
const handleTVEvent = React.useCallback(
|
||||
(event: any) => {
|
||||
if (isSectionFocused && event.eventType === "select") {
|
||||
@@ -61,7 +69,9 @@ export const LiveStreamSection = forwardRef<LiveStreamSectionRef, LiveStreamSect
|
||||
useTVEventHandler(handleTVEvent);
|
||||
|
||||
return (
|
||||
<SettingsSection focusable onFocus={handleSectionFocus} onBlur={handleSectionBlur}>
|
||||
<SettingsSection focusable onFocus={handleSectionFocus} onBlur={handleSectionBlur}
|
||||
onPress={Platform.isTV||deviceType !=='tv' ? undefined : handlePress}
|
||||
>
|
||||
<View style={styles.inputContainer}>
|
||||
<View style={styles.titleContainer}>
|
||||
<ThemedText style={styles.sectionTitle}>直播源地址</ThemedText>
|
||||
@@ -81,6 +91,7 @@ export const LiveStreamSection = forwardRef<LiveStreamSectionRef, LiveStreamSect
|
||||
autoCorrect={false}
|
||||
onFocus={() => setIsInputFocused(true)}
|
||||
onBlur={() => setIsInputFocused(false)}
|
||||
// onPress={handlePress}
|
||||
/>
|
||||
</Animated.View>
|
||||
</View>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { View, Switch, StyleSheet, Pressable, Animated } from "react-native";
|
||||
import { View, Switch, StyleSheet, Pressable, Animated, Platform } from "react-native";
|
||||
import { useTVEventHandler } from "react-native";
|
||||
import { ThemedText } from "@/components/ThemedText";
|
||||
import { SettingsSection } from "./SettingsSection";
|
||||
@@ -7,18 +7,21 @@ import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { useRemoteControlStore } from "@/stores/remoteControlStore";
|
||||
import { useButtonAnimation } from "@/hooks/useAnimation";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import { useResponsiveLayout } from "@/hooks/useResponsiveLayout";
|
||||
|
||||
interface RemoteInputSectionProps {
|
||||
onChanged: () => void;
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
onPress?: () => void;
|
||||
}
|
||||
|
||||
export const RemoteInputSection: React.FC<RemoteInputSectionProps> = ({ onChanged, onFocus, onBlur }) => {
|
||||
export const RemoteInputSection: React.FC<RemoteInputSectionProps> = ({ onChanged, onFocus, onBlur, onPress }) => {
|
||||
const { remoteInputEnabled, setRemoteInputEnabled } = useSettingsStore();
|
||||
const { isServerRunning, serverUrl, error } = useRemoteControlStore();
|
||||
const [isFocused, setIsFocused] = React.useState(false);
|
||||
const animationStyle = useButtonAnimation(isFocused, 1.2);
|
||||
const deviceType = useResponsiveLayout().deviceType;
|
||||
|
||||
const handleToggle = useCallback(
|
||||
(enabled: boolean) => {
|
||||
@@ -38,6 +41,10 @@ export const RemoteInputSection: React.FC<RemoteInputSectionProps> = ({ onChange
|
||||
onBlur?.();
|
||||
};
|
||||
|
||||
const handlePress = () => {
|
||||
handleToggle(!remoteInputEnabled);
|
||||
}
|
||||
|
||||
// TV遥控器事件处理
|
||||
const handleTVEvent = React.useCallback(
|
||||
(event: any) => {
|
||||
@@ -51,7 +58,9 @@ export const RemoteInputSection: React.FC<RemoteInputSectionProps> = ({ onChange
|
||||
useTVEventHandler(handleTVEvent);
|
||||
|
||||
return (
|
||||
<SettingsSection focusable onFocus={handleSectionFocus} onBlur={handleSectionBlur}>
|
||||
<SettingsSection focusable onFocus={handleSectionFocus} onBlur={handleSectionBlur}
|
||||
{...Platform.isTV||deviceType !=='tv'? undefined :{onPress:handlePress}}
|
||||
>
|
||||
<Pressable style={styles.settingItem} onFocus={handleSectionFocus} onBlur={handleSectionBlur}>
|
||||
<View style={styles.settingInfo}>
|
||||
<ThemedText style={styles.settingName}>启用远程输入</ThemedText>
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import React, { useState } from "react";
|
||||
import { StyleSheet, Pressable } from "react-native";
|
||||
import { StyleSheet, Pressable, Platform } from "react-native";
|
||||
import { ThemedView } from "@/components/ThemedView";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import { useResponsiveLayout } from "@/hooks/useResponsiveLayout";
|
||||
|
||||
interface SettingsSectionProps {
|
||||
children: React.ReactNode;
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
onPress?: () => void;
|
||||
focusable?: boolean;
|
||||
}
|
||||
|
||||
export const SettingsSection: React.FC<SettingsSectionProps> = ({ children, onFocus, onBlur, focusable = false }) => {
|
||||
export const SettingsSection: React.FC<SettingsSectionProps> = ({ children, onFocus, onBlur, onPress, focusable = false }) => {
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const deviceType = useResponsiveLayout().deviceType;
|
||||
|
||||
const handleFocus = () => {
|
||||
setIsFocused(true);
|
||||
@@ -23,13 +26,24 @@ export const SettingsSection: React.FC<SettingsSectionProps> = ({ children, onFo
|
||||
onBlur?.();
|
||||
};
|
||||
|
||||
const handlePress = () => {
|
||||
onPress?.();
|
||||
}
|
||||
|
||||
if (!focusable) {
|
||||
return <ThemedView style={styles.section}>{children}</ThemedView>;
|
||||
}
|
||||
|
||||
return (
|
||||
<ThemedView style={[styles.section, isFocused && styles.sectionFocused]}>
|
||||
<Pressable style={styles.sectionPressable} onFocus={handleFocus} onBlur={handleBlur}>
|
||||
<Pressable
|
||||
android_ripple={Platform.isTV||deviceType !=='tv'? {color:'transparent'}:{color:Colors.dark.link}}
|
||||
style={styles.sectionPressable}
|
||||
// {...(Platform.isTV ? {onFocus: handleFocus, onBlur: handleBlur} : {onPress: onPress})}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
onPress={handlePress}
|
||||
>
|
||||
{children}
|
||||
</Pressable>
|
||||
</ThemedView>
|
||||
|
||||
Reference in New Issue
Block a user