From 3bcd7496c2c98113b6f4d98afa4062cbf34469aa Mon Sep 17 00:00:00 2001 From: Lei Nelissen Date: Sun, 28 Jan 2024 23:53:06 +0100 Subject: [PATCH] fix: refactor timer and design --- ios/Podfile.lock | 6 + .../modals/Player/components/Timer.tsx | 124 +++++++++++++----- src/utility/PlaybackService.ts | 8 +- 3 files changed, 98 insertions(+), 40 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 247ec5e..4cc9411 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -447,6 +447,8 @@ PODS: - React-perflogger (= 0.71.15) - RNCAsyncStorage (1.17.11): - React-Core + - RNDateTimePicker (7.6.2): + - React-Core - RNFastImage (8.6.3): - React-Core - SDWebImage (~> 5.11.1) @@ -552,6 +554,7 @@ DEPENDENCIES: - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" + - "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)" - RNFastImage (from `../node_modules/react-native-fast-image`) - RNFS (from `../node_modules/react-native-fs`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) @@ -670,6 +673,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" RNCAsyncStorage: :path: "../node_modules/@react-native-async-storage/async-storage" + RNDateTimePicker: + :path: "../node_modules/@react-native-community/datetimepicker" RNFastImage: :path: "../node_modules/react-native-fast-image" RNFS: @@ -746,6 +751,7 @@ SPEC CHECKSUMS: React-runtimeexecutor: 8f2ddd9db7874ec7de84f5c55d73aeaaf82908e2 ReactCommon: 309d965cb51f058d07dea65bc04dcf462911f0a4 RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60 + RNDateTimePicker: fc2e4f2795877d45e84d85659bebe627eba5c931 RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8 RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39 diff --git a/src/screens/modals/Player/components/Timer.tsx b/src/screens/modals/Player/components/Timer.tsx index d072795..01b3b39 100644 --- a/src/screens/modals/Player/components/Timer.tsx +++ b/src/screens/modals/Player/components/Timer.tsx @@ -5,78 +5,132 @@ import { THEME_COLOR } from '@/CONSTANTS'; import { useDispatch } from 'react-redux'; import { useTypedSelector } from '@/store'; import TimerIcon from '@/assets/icons/timer-icon.svg'; -import { Text } from '@/components/Typography'; -import { setTimerDate } from '@/store/music/actions'; +import { setTimerDate } from '@/store/sleep-timer'; +import ticksToDuration from '@/utility/ticksToDuration'; +import useDefaultStyles from '@/components/Colors'; +import { TouchableOpacity } from 'react-native-gesture-handler'; +import { t } from '@/localisation'; const Container = styled.View` + align-self: flex-start; align-items: flex-start; - margin-top: 60px; + margin-top: 52px; + padding: 8px; + margin-left: -8px; + flex: 0 1 auto; + border-radius: 8px; `; const View = styled.View` display: flex; flex-direction: row; align-items: center; - gap: 4px; + gap: 8px; +`; + +const Label = styled.Text` + font-size: 13px; `; export default function Timer() { + // Keep an incrementing counter to force the component to update when the + // interval is active. + const [, setCounter] = useState(0); + + // Show the picker or not const [showPicker, setShowPicker] = useState(false); - const [remainingTime, setRemainingTime] = useState(); - const { timerDate } = useTypedSelector(state => state.music); - + + // Retrieve Redux state and methods + const date = useTypedSelector(state => state.sleepTimer.date); const dispatch = useDispatch(); + + // Retrieve styles + const defaultStyles = useDefaultStyles(); + // Deal with a date being selected const handleConfirm = useCallback((date: Date) => { + // GUARD: If the date is in the past, we need to add 24 hours to it to + // ensure it is in the future + if (date.getTime() < new Date().getTime()) { + date = new Date(date.getTime() + 24 * 60 * 60 * 1_000); + } + + // Only accept minutes and hours date.setSeconds(0); + + // Set the date and close the picker dispatch(setTimerDate(date)); setShowPicker(false); }, [dispatch]); + // Close the picker when it is canceled const handleCancelDatePicker = useCallback(() => { setShowPicker(false); }, []); + // Show it when it should be opened const showDatePicker = useCallback(() => { setShowPicker(!showPicker); }, [showPicker]); + // Periodically trigger updates useEffect(() => { - if (!timerDate) { - setRemainingTime(null); + // GUARD: If there's no date, there's no need to update + if (!date) { return; } + // Set an interval that periodically increments the counter when a date + // is active const interval = setInterval(() => { - const dateSet = timerDate ? timerDate : new Date(); - const millisecondsDiff = dateSet.valueOf() - new Date().valueOf(); - let sec = Math.floor(millisecondsDiff / 1000); - let min = Math.floor(sec/60); - sec = sec%60; - const hours = Math.floor(min/60); - min = min%60; - const ticks = `${hours.toString().length === 1 ? '0' + hours : hours}:${min.toString().length === 1 ? '0' + min : min}:${sec.toString().length === 1 ? '0' + sec : sec}`; - setRemainingTime(ticks); - }, 1000); + setCounter((i) => i + 1); + }, 1_000); + // Clean up the interval on re-renders return () => clearInterval(interval); - }, [setRemainingTime, timerDate]); + }, [date]); + + // Calculate the remaining time by subtracting it from the current date + const remainingTime = date && date - new Date().getTime(); return ( - - - - {!timerDate ? 'Sleep Timer' : remainingTime} - - - + + + + + + + + + ); } \ No newline at end of file diff --git a/src/utility/PlaybackService.ts b/src/utility/PlaybackService.ts index fafe850..3ef7f01 100644 --- a/src/utility/PlaybackService.ts +++ b/src/utility/PlaybackService.ts @@ -10,10 +10,9 @@ import TrackPlayer, { Event, State } from 'react-native-track-player'; import store from '@/store'; import { sendPlaybackEvent } from './JellyfinApi'; -import { setTimerDate } from '@/store/music/actions'; +import { setTimerDate } from '@/store/sleep-timer'; export default async function() { - TrackPlayer.addEventListener(Event.RemotePlay, () => { TrackPlayer.play(); }); @@ -55,8 +54,7 @@ export default async function() { TrackPlayer.addEventListener(Event.PlaybackProgressUpdated, () => { // Retrieve the current settings from the Redux store - const settings = store.getState().settings; - const music = store.getState().music; + const { settings, sleepTimer } = store.getState(); // GUARD: Only report playback when the settings is enabled if (settings.enablePlaybackReporting) { @@ -64,7 +62,7 @@ export default async function() { } // check if timerDate is undefined, otherwise start timer - if (music.timerDate && music.timerDate.valueOf() < new Date().valueOf()) { + if (sleepTimer.date && sleepTimer.date < new Date().valueOf()) { TrackPlayer.pause(); store.dispatch(setTimerDate(null)); }