From 30724a1e191c38616c6a298d931868cced58e473 Mon Sep 17 00:00:00 2001 From: zimplexing Date: Tue, 8 Jul 2025 22:07:14 +0800 Subject: [PATCH] Add toast notifications for intro and outro time settings, update player store and media button for new time tracking --- app/_layout.tsx | 2 + app/play.tsx | 6 ++- components/MediaButton.tsx | 24 +++++++-- components/PlayerControls.tsx | 24 +++++++-- hooks/useTVRemoteHandler.ts | 3 -- package.json | 1 + services/storage.ts | 5 +- stores/playerStore.ts | 95 +++++++++++++++++++++++++++++------ stores/settingsStore.ts | 2 +- yarn.lock | 5 ++ 10 files changed, 135 insertions(+), 32 deletions(-) diff --git a/app/_layout.tsx b/app/_layout.tsx index f592f51..700de07 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -4,6 +4,7 @@ import { Stack } from "expo-router"; import * as SplashScreen from "expo-splash-screen"; import { useEffect } from "react"; import { Platform } from "react-native"; +import Toast from "react-native-toast-message"; import { useSettingsStore } from "@/stores/settingsStore"; @@ -43,6 +44,7 @@ export default function RootLayout() { + ); } diff --git a/app/play.tsx b/app/play.tsx index 94e13b2..655068b 100644 --- a/app/play.tsx +++ b/app/play.tsx @@ -34,6 +34,7 @@ export default function PlayScreen() { showSourceModal, showNextEpisodeOverlay, initialPosition, + introEndTime, setVideoRef, loadVideo, playEpisode, @@ -102,8 +103,9 @@ export default function PlayScreen() { resizeMode={ResizeMode.CONTAIN} onPlaybackStatusUpdate={handlePlaybackStatusUpdate} onLoad={() => { - if (initialPosition > 0) { - videoRef.current?.setPositionAsync(initialPosition); + const jumpPosition = introEndTime || initialPosition; + if (jumpPosition > 0) { + videoRef.current?.setPositionAsync(jumpPosition); } usePlayerStore.setState({ isLoading: false }); }} diff --git a/components/MediaButton.tsx b/components/MediaButton.tsx index 6f67c68..5eb13f6 100644 --- a/components/MediaButton.tsx +++ b/components/MediaButton.tsx @@ -1,11 +1,16 @@ import React, { ComponentProps } from "react"; import { StyledButton } from "./StyledButton"; -import { StyleSheet } from "react-native"; +import { StyleSheet, View, Text } from "react-native"; -type StyledButtonProps = ComponentProps; +type StyledButtonProps = ComponentProps & { + timeLabel?: string; +}; -export const MediaButton = (props: StyledButtonProps) => ( - +export const MediaButton = ({ timeLabel, ...props }: StyledButtonProps) => ( + + + {timeLabel && {timeLabel}} + ); const styles = StyleSheet.create({ @@ -13,4 +18,15 @@ const styles = StyleSheet.create({ padding: 12, minWidth: 80, }, + timeLabel: { + position: "absolute", + top: 14, + right: 12, + color: "white", + fontSize: 10, + fontWeight: "bold", + backgroundColor: "rgba(0,0,0,0.6)", + paddingHorizontal: 4, + borderRadius: 3, + }, }); diff --git a/components/PlayerControls.tsx b/components/PlayerControls.tsx index 90f25c1..180ff10 100644 --- a/components/PlayerControls.tsx +++ b/components/PlayerControls.tsx @@ -2,7 +2,17 @@ import React, { useCallback, useState } from "react"; import { View, Text, StyleSheet, TouchableOpacity, Pressable } from "react-native"; import { useRouter } from "expo-router"; import { AVPlaybackStatus } from "expo-av"; -import { ArrowLeft, Pause, Play, SkipForward, List, ChevronsRight, ChevronsLeft, Tv } from "lucide-react-native"; +import { + Pause, + Play, + SkipForward, + List, + ChevronsRight, + ChevronsLeft, + Tv, + ArrowDownToDot, + ArrowUpFromDot, +} from "lucide-react-native"; import { ThemedText } from "@/components/ThemedText"; import { MediaButton } from "@/components/MediaButton"; @@ -28,6 +38,10 @@ export const PlayerControls: React.FC = ({ showControls, se playEpisode, setShowEpisodeModal, setShowSourceModal, + setIntroEndTime, + setOutroStartTime, + introEndTime, + outroStartTime, } = usePlayerStore(); const videoTitle = detail?.videoInfo?.title || ""; @@ -81,8 +95,8 @@ export const PlayerControls: React.FC = ({ showControls, se - seek(-15000)}> - + + @@ -97,8 +111,8 @@ export const PlayerControls: React.FC = ({ showControls, se - seek(15000)}> - + + setShowEpisodeModal(true)}> diff --git a/hooks/useTVRemoteHandler.ts b/hooks/useTVRemoteHandler.ts index d3c942f..a5e7c36 100644 --- a/hooks/useTVRemoteHandler.ts +++ b/hooks/useTVRemoteHandler.ts @@ -67,7 +67,6 @@ export const useTVRemoteHandler = () => { if (event.eventType === "longRight" || event.eventType === "longLeft") { if (event.eventKeyAction === 1) { if (fastForwardIntervalRef.current) { - console.log("Long right key released, stopping fast forward."); clearInterval(fastForwardIntervalRef.current); fastForwardIntervalRef.current = null; } @@ -82,8 +81,6 @@ export const useTVRemoteHandler = () => { return; } - console.log("TV Event:", event); - switch (event.eventType) { case "select": togglePlayPause(); diff --git a/package.json b/package.json index 866676e..c5210eb 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "react-native-safe-area-context": "4.10.1", "react-native-screens": "3.31.1", "react-native-svg": "^15.12.0", + "react-native-toast-message": "^2.3.3", "react-native-web": "~0.19.10", "zustand": "^5.0.6" }, diff --git a/services/storage.ts b/services/storage.ts index 91789b8..c1f3df7 100644 --- a/services/storage.ts +++ b/services/storage.ts @@ -10,7 +10,10 @@ const STORAGE_KEYS = { } as const; // --- Type Definitions (aligned with api.ts) --- -export type PlayRecord = ApiPlayRecord; +export interface PlayRecord extends ApiPlayRecord { + introEndTime?: number; + outroStartTime?: number; +} export interface FavoriteItem { id: string; diff --git a/stores/playerStore.ts b/stores/playerStore.ts index c3fc641..6793017 100644 --- a/stores/playerStore.ts +++ b/stores/playerStore.ts @@ -1,8 +1,9 @@ import { create } from "zustand"; +import Toast from "react-native-toast-message"; import { AVPlaybackStatus, Video } from "expo-av"; import { RefObject } from "react"; import { api, VideoDetail as ApiVideoDetail, SearchResult } from "@/services/api"; -import { PlayRecordManager } from "@/services/storage"; +import { PlayRecord, PlayRecordManager } from "@/services/storage"; interface Episode { url: string; @@ -32,6 +33,8 @@ interface PlayerState { seekPosition: number; progressPosition: number; initialPosition: number; + introEndTime?: number; + outroStartTime?: number; setVideoRef: (ref: RefObject