more fixes web + mobile
This commit is contained in:
@@ -25,6 +25,7 @@ import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
|
||||
import { background } from "../../assets";
|
||||
import RestartSpinnerIcon from "../../assets/UI/RestartSpinnerIcon";
|
||||
import { registerBlobUrl, releaseBlobUrl } from "../../utils/blobUrlCache";
|
||||
|
||||
const TIME_BEFORE_INCREMENT_MS = 20000;
|
||||
const WEB_PREVIEW_WIDTH = 360;
|
||||
@@ -82,6 +83,7 @@ const RecordPlayback = ({ route }) => {
|
||||
const stopRecordingPromiseRef = useRef(null);
|
||||
const stopRecordingResolveRef = useRef(null);
|
||||
const recordedUrlRef = useRef(null);
|
||||
const preserveRecordingForNextScreenRef = useRef(false);
|
||||
const originalLoopingValueRef = useRef({ hasValue: false, value: false });
|
||||
const latestLoopingValueRef = useRef(isLooping ?? false);
|
||||
const [mediaReady, setMediaReady] = useState(false);
|
||||
@@ -91,14 +93,18 @@ const RecordPlayback = ({ route }) => {
|
||||
const [countdown, setCountdown] = useState(0);
|
||||
const [isPreparing, setIsPreparing] = useState(false);
|
||||
const [isRecording, setIsRecording] = useState(false);
|
||||
const [isPaused, setIsPaused] = useState(false);
|
||||
const [pos, setPos] = useState(0);
|
||||
const [dur, setDur] = useState(0);
|
||||
|
||||
// timers / refs
|
||||
const isPausedRef = useRef(false);
|
||||
const progressTimerRef = useRef(null);
|
||||
const listenTimerRef = useRef(null);
|
||||
const countdownTimerRef = useRef(null);
|
||||
const perfStartRef = useRef(null);
|
||||
const pausedAtRef = useRef(null);
|
||||
const pausedMsRef = useRef(0);
|
||||
const listenedMsRef = useRef(0);
|
||||
const viewsIncrementedRef = useRef(false);
|
||||
const correctedInitialJumpRef = useRef(false);
|
||||
@@ -106,6 +112,9 @@ const RecordPlayback = ({ route }) => {
|
||||
useEffect(() => {
|
||||
latestLoopingValueRef.current = isLooping ?? false;
|
||||
}, [isLooping]);
|
||||
useEffect(() => {
|
||||
isPausedRef.current = isPaused;
|
||||
}, [isPaused]);
|
||||
|
||||
// Lyrics
|
||||
const alignedWords = useMemo(() => {
|
||||
@@ -131,6 +140,10 @@ const RecordPlayback = ({ route }) => {
|
||||
const resetUI = () => {
|
||||
setIsPreparing(false);
|
||||
setIsRecording(false);
|
||||
setIsPaused(false);
|
||||
isPausedRef.current = false;
|
||||
pausedAtRef.current = null;
|
||||
pausedMsRef.current = 0;
|
||||
setCountdown(0);
|
||||
setPos(0);
|
||||
setDur(0);
|
||||
@@ -141,12 +154,13 @@ const RecordPlayback = ({ route }) => {
|
||||
};
|
||||
|
||||
const releaseRecordingUrl = useCallback(() => {
|
||||
if (recordedUrlRef.current) {
|
||||
try {
|
||||
URL.revokeObjectURL(recordedUrlRef.current);
|
||||
} catch {}
|
||||
recordedUrlRef.current = null;
|
||||
if (!recordedUrlRef.current) {
|
||||
preserveRecordingForNextScreenRef.current = false;
|
||||
return;
|
||||
}
|
||||
releaseBlobUrl(recordedUrlRef.current);
|
||||
recordedUrlRef.current = null;
|
||||
preserveRecordingForNextScreenRef.current = false;
|
||||
}, []);
|
||||
|
||||
const startRecorder = useCallback(() => {
|
||||
@@ -203,6 +217,7 @@ const RecordPlayback = ({ route }) => {
|
||||
});
|
||||
url = URL.createObjectURL(blob);
|
||||
recordedUrlRef.current = url;
|
||||
registerBlobUrl(url, blob);
|
||||
} else {
|
||||
releaseRecordingUrl();
|
||||
}
|
||||
@@ -273,8 +288,10 @@ const RecordPlayback = ({ route }) => {
|
||||
} catch (e) {}
|
||||
try {
|
||||
await stopRecorderAndGetUrl();
|
||||
releaseRecordingUrl();
|
||||
} catch (e) {}
|
||||
if (!preserveRecordingForNextScreenRef.current) {
|
||||
releaseRecordingUrl();
|
||||
}
|
||||
}, [player, releaseRecordingUrl, stopRecorderAndGetUrl]);
|
||||
|
||||
const resetSessionRef = useRef(resetSession);
|
||||
@@ -410,11 +427,14 @@ const RecordPlayback = ({ route }) => {
|
||||
mediaStreamRef.current.getTracks().forEach((track) => track.stop());
|
||||
mediaStreamRef.current = null;
|
||||
}
|
||||
releaseRecordingUrl();
|
||||
if (!preserveRecordingForNextScreenRef.current) {
|
||||
releaseRecordingUrl();
|
||||
}
|
||||
};
|
||||
}, [releaseRecordingUrl]);
|
||||
}, [releaseRecordingUrl, preserveRecordingForNextScreenRef]);
|
||||
|
||||
const stopAndNavigate = useCallback(async () => {
|
||||
preserveRecordingForNextScreenRef.current = true;
|
||||
clearAllTimers();
|
||||
try {
|
||||
await player?.pause?.();
|
||||
@@ -429,6 +449,10 @@ const RecordPlayback = ({ route }) => {
|
||||
} catch (e) {}
|
||||
|
||||
setIsRecording(false);
|
||||
setIsPaused(false);
|
||||
isPausedRef.current = false;
|
||||
pausedAtRef.current = null;
|
||||
pausedMsRef.current = 0;
|
||||
navigate(Routes.RecordedPlayback, { project, videoUri: videoUrl || null });
|
||||
}, [dur, player, pos, project, stopRecorderAndGetUrl]);
|
||||
|
||||
@@ -438,11 +462,19 @@ const RecordPlayback = ({ route }) => {
|
||||
progressTimerRef.current = setInterval(async () => {
|
||||
const rawDur = toSeconds(player?.duration);
|
||||
const rawPos = toSeconds(player?.currentTime);
|
||||
const pausedSince =
|
||||
pausedAtRef.current != null
|
||||
? Math.max(0, performance.now() - pausedAtRef.current)
|
||||
: 0;
|
||||
const pausedTotal = pausedMsRef.current + pausedSince;
|
||||
|
||||
// fallback monotone si le player ne donne rien
|
||||
const fallback =
|
||||
perfStartRef.current != null
|
||||
? Math.max(0, (performance.now() - perfStartRef.current) / 1000)
|
||||
? Math.max(
|
||||
0,
|
||||
(performance.now() - perfStartRef.current - pausedTotal) / 1000
|
||||
)
|
||||
: 0;
|
||||
|
||||
let nextDur = rawDur > 0 ? rawDur : dur || 0;
|
||||
@@ -464,7 +496,7 @@ const RecordPlayback = ({ route }) => {
|
||||
|
||||
const timeSinceStart =
|
||||
perfStartRef.current != null
|
||||
? performance.now() - perfStartRef.current
|
||||
? Math.max(0, performance.now() - perfStartRef.current - pausedTotal)
|
||||
: null;
|
||||
|
||||
const shouldTrustFallback =
|
||||
@@ -484,6 +516,10 @@ const RecordPlayback = ({ route }) => {
|
||||
setDur(nextDur);
|
||||
setPos(nextPos);
|
||||
|
||||
if (isPausedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextDur > 0 && nextPos >= nextDur - 0.3) {
|
||||
void stopAndNavigate();
|
||||
}
|
||||
@@ -516,6 +552,10 @@ const RecordPlayback = ({ route }) => {
|
||||
|
||||
const startPlayback = useCallback(async () => {
|
||||
try {
|
||||
setIsPaused(false);
|
||||
isPausedRef.current = false;
|
||||
pausedAtRef.current = null;
|
||||
pausedMsRef.current = 0;
|
||||
await player?.pause?.(); // s'assure qu'on repart propre
|
||||
await player?.seekTo?.(0); // tente un seek d'amorçage
|
||||
const recorderStarted = startRecorder();
|
||||
@@ -530,6 +570,9 @@ const RecordPlayback = ({ route }) => {
|
||||
} catch (e) {
|
||||
setIsRecording(false);
|
||||
setIsPreparing(false);
|
||||
setIsPaused(false);
|
||||
isPausedRef.current = false;
|
||||
pausedAtRef.current = null;
|
||||
const msg = String(e?.message || e || "");
|
||||
if (/not supported on the simulator/i.test(msg)) {
|
||||
alert(
|
||||
@@ -557,6 +600,8 @@ const RecordPlayback = ({ route }) => {
|
||||
const startCountdownThenRecord = useCallback(async () => {
|
||||
if (!songUrl || !canRecord) return;
|
||||
await resetSession();
|
||||
setIsPaused(false);
|
||||
isPausedRef.current = false;
|
||||
setIsPreparing(true);
|
||||
setCountdown(5);
|
||||
if (countdownTimerRef.current) clearInterval(countdownTimerRef.current);
|
||||
@@ -575,6 +620,43 @@ const RecordPlayback = ({ route }) => {
|
||||
}, 1000);
|
||||
}, [songUrl, canRecord, resetSession, startPlayback]);
|
||||
|
||||
const handleTogglePause = useCallback(async () => {
|
||||
try {
|
||||
if (!isRecording) return;
|
||||
if (!isPausedRef.current) {
|
||||
isPausedRef.current = true;
|
||||
setIsPaused(true);
|
||||
pausedAtRef.current = performance.now();
|
||||
try {
|
||||
await player?.pause?.();
|
||||
} catch (e) {}
|
||||
try {
|
||||
const recorder = mediaRecorderRef.current;
|
||||
if (recorder && recorder.state === "recording") {
|
||||
recorder.pause?.();
|
||||
}
|
||||
} catch (e) {}
|
||||
return;
|
||||
}
|
||||
const pausedAt = pausedAtRef.current;
|
||||
if (pausedAt != null) {
|
||||
pausedMsRef.current += performance.now() - pausedAt;
|
||||
pausedAtRef.current = null;
|
||||
}
|
||||
isPausedRef.current = false;
|
||||
setIsPaused(false);
|
||||
try {
|
||||
const recorder = mediaRecorderRef.current;
|
||||
if (recorder && recorder.state === "paused") {
|
||||
recorder.resume?.();
|
||||
}
|
||||
} catch (e) {}
|
||||
try {
|
||||
await player?.play?.();
|
||||
} catch (e) {}
|
||||
} catch (e) {}
|
||||
}, [isRecording, player]);
|
||||
|
||||
const handleRestartRecording = useCallback(async () => {
|
||||
try {
|
||||
await startCountdownThenRecord();
|
||||
@@ -586,9 +668,7 @@ const RecordPlayback = ({ route }) => {
|
||||
return (
|
||||
<Page
|
||||
style={{ flex: 1 }}
|
||||
width={"100%"}
|
||||
maxWidth={800}
|
||||
containerStyle={{ margin: 0, padding: 0, backgroundColor: Palette.tran }}
|
||||
containerStyle={{ backgroundColor: Palette.tran }}
|
||||
headerType="NONE"
|
||||
backgroundImg={background.playbackBG2}
|
||||
>
|
||||
@@ -766,6 +846,34 @@ const RecordPlayback = ({ route }) => {
|
||||
<RestartSpinnerIcon />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{isRecording && (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.9}
|
||||
onPress={handleTogglePause}
|
||||
style={{
|
||||
marginTop: 14,
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 18,
|
||||
borderRadius: 999,
|
||||
backgroundColor: Palette.white,
|
||||
shadowColor: "#000000",
|
||||
shadowOpacity: 0.12,
|
||||
shadowRadius: 10,
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
elevation: 4,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: Palette.black,
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
fontSize: 14,
|
||||
}}
|
||||
>
|
||||
{isPaused ? "Reprendre" : "Mettre en pause"}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user