Files
hushroom/src/features/flight-session/model/useFlightSession.ts

132 lines
3.4 KiB
TypeScript

import { useEffect, useRef, useState } from 'react';
import { useRouter } from 'next/navigation';
import { getCurrentVoyage, saveCurrentVoyage } from '@/shared/lib/store';
import { Voyage } from '@/shared/types';
const getVoyageFromStore = () => {
const current = getCurrentVoyage();
if (!current || current.status !== 'in_progress') {
return null;
}
return current;
};
const getEndTime = (voyage: Voyage | null) => {
if (!voyage) return 0;
return voyage.startedAt + voyage.durationMinutes * 60 * 1000;
};
const getInitialTimerSeconds = (voyage: Voyage | null) => {
if (!voyage) return 0;
if (voyage.durationMinutes === 0) {
return Math.max(0, Math.floor((Date.now() - voyage.startedAt) / 1000));
}
const endTime = getEndTime(voyage);
if (!endTime) return 0;
return Math.max(0, Math.ceil((endTime - Date.now()) / 1000));
};
const formatHHMMSS = (totalSeconds: number) => {
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
return `${hours.toString().padStart(2, '0')}:${minutes
.toString()
.padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
};
export function useFlightSession() {
const router = useRouter();
const [voyage] = useState<Voyage | null>(() => getVoyageFromStore());
const [timeLeft, setTimeLeft] = useState<number>(() =>
getInitialTimerSeconds(getVoyageFromStore()),
);
const [isPaused, setIsPaused] = useState(false);
const endTimeRef = useRef<number>(getEndTime(getVoyageFromStore()));
const pausedElapsedMsRef = useRef<number>(0);
const pausedAtMsRef = useRef<number | null>(null);
useEffect(() => {
if (voyage) return;
router.replace('/');
}, [voyage, router]);
useEffect(() => {
if (!voyage || isPaused) return;
const interval = setInterval(() => {
if (voyage.durationMinutes === 0) {
const elapsedMs = Date.now() - voyage.startedAt - pausedElapsedMsRef.current;
setTimeLeft(Math.max(0, Math.floor(elapsedMs / 1000)));
return;
}
const diff = endTimeRef.current - Date.now();
if (diff <= 0) {
setTimeLeft(0);
clearInterval(interval);
return;
}
setTimeLeft(Math.ceil(diff / 1000));
}, 1000);
return () => clearInterval(interval);
}, [voyage, isPaused]);
const handlePauseToggle = () => {
if (voyage?.durationMinutes === 0) {
if (isPaused) {
if (pausedAtMsRef.current !== null) {
pausedElapsedMsRef.current += Date.now() - pausedAtMsRef.current;
}
pausedAtMsRef.current = null;
setIsPaused(false);
return;
}
pausedAtMsRef.current = Date.now();
setIsPaused(true);
return;
}
if (isPaused) {
endTimeRef.current = Date.now() + timeLeft * 1000;
setIsPaused(false);
return;
}
setIsPaused(true);
};
const handleFinish = () => {
if (!voyage) return null;
const endedVoyage: Voyage = {
...voyage,
endedAt: voyage.endedAt || Date.now(),
};
saveCurrentVoyage(endedVoyage);
return endedVoyage;
};
const formatTime = (seconds: number) => formatHHMMSS(seconds);
return {
voyage,
timeLeft,
isCountdownCompleted: Boolean(voyage && voyage.durationMinutes > 0 && timeLeft === 0),
isPaused,
formattedTime: formatTime(timeLeft),
handlePauseToggle,
handleFinish,
};
}