feat: restart on web

This commit is contained in:
2025-11-17 10:58:28 +01:00
parent e0309c24b9
commit add6a707a2
4 changed files with 106 additions and 73 deletions
+61
View File
@@ -0,0 +1,61 @@
import * as React from "react";
import { View } from "react-native";
import Svg, { ClipPath, Defs, G, Path, Rect } from "react-native-svg";
const RestartSpinnerIcon = ({
size = 30,
color = "#F94697",
background = "transparent",
style,
}) => {
return (
<View
style={[
{
width: size + 8,
height: size + 8,
borderRadius: size,
alignItems: "center",
justifyContent: "center",
backgroundColor: background,
},
style,
]}
>
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
<G clipPath="url(#restartSpinnerClip)">
<Path
d="
M19.7285 10.9286
C20.4412 13.5975 19.7507 16.5633 17.6569 18.6571
C14.5327 21.7813 9.46734 21.7813 6.34315 18.6571
C3.21895 15.5329 3.21895 10.4676 6.34315 7.34338
C9.46734 4.21918 14.5327 4.21918 17.6569 7.34338
L18.364 8.05048
"
stroke={color}
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
<Path
d="M14.1214 8.05026H18.364V3.80762"
stroke={color}
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
</G>
<Defs>
<ClipPath id="restartSpinnerClip">
<Rect width="24" height="24" fill="white" />
</ClipPath>
</Defs>
</Svg>
</View>
);
};
export default RestartSpinnerIcon;
+2 -55
View File
@@ -10,12 +10,13 @@ import React, {
} from "react"; } from "react";
import { Text, TouchableOpacity, View } from "react-native"; import { Text, TouchableOpacity, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
import Svg, { Circle, Path, G, Defs, Rect, ClipPath } from "react-native-svg"; import Svg, { Circle } from "react-native-svg";
import alert from "../../components/Alert"; import alert from "../../components/Alert";
import GradientButton from "../../components/GradientButton"; import GradientButton from "../../components/GradientButton";
import KaraokeLyrics from "../../components/KaraokeLyrics"; import KaraokeLyrics from "../../components/KaraokeLyrics";
import MusicLandHeader from "../../components/MusicLandHeader"; import MusicLandHeader from "../../components/MusicLandHeader";
import { increment, projectsRef } from "../../config/firebase"; import { increment, projectsRef } from "../../config/firebase";
import RestartSpinnerIcon from "../../assets/UI/RestartSpinnerIcon";
import usePlayer from "../../hooks/usePlayer"; import usePlayer from "../../hooks/usePlayer";
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer"; import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
import { Routes } from "../../navigation"; import { Routes } from "../../navigation";
@@ -887,60 +888,6 @@ const ProgressRing = ({ size = 50, strokeWidth = 12, progress = 0 }) => {
); );
}; };
const RestartSpinnerIcon = ({
size = 30,
color = "#F94697", // couleur unie
background = "transparent",
}) => {
return (
<View
style={{
width: size + 8,
height: size + 8,
borderRadius: size,
alignItems: "center",
justifyContent: "center",
backgroundColor: background,
}}
>
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
<G clipPath="url(#clip0)">
{/* Cercle re-dessiné sans opacité */}
<Path
d="
M19.7285 10.9286
C20.4412 13.5975 19.7507 16.5633 17.6569 18.6571
C14.5327 21.7813 9.46734 21.7813 6.34315 18.6571
C3.21895 15.5329 3.21895 10.4676 6.34315 7.34338
C9.46734 4.21918 14.5327 4.21918 17.6569 7.34338
L18.364 8.05048
"
stroke={color}
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
{/* Flèche */}
<Path
d="M14.1214 8.05026H18.364V3.80762"
stroke={color}
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
</G>
<Defs>
<ClipPath id="clip0">
<Rect width="24" height="24" fill="white" />
</ClipPath>
</Defs>
</Svg>
</View>
);
};
export default RecordPlayback; export default RecordPlayback;
// Render previous/current/next line to reduce jumpiness // Render previous/current/next line to reduce jumpiness
+41 -12
View File
@@ -7,7 +7,7 @@ import React, {
useRef, useRef,
useState, useState,
} from "react"; } from "react";
import { Text, View } from "react-native"; import { Text, TouchableOpacity, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
import Svg, { Circle } from "react-native-svg"; import Svg, { Circle } from "react-native-svg";
import alert from "../../components/Alert"; import alert from "../../components/Alert";
@@ -26,6 +26,7 @@ import { FONT_FAMILY } from "../../styles/Fonts";
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader"; import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
import { background } from "../../assets"; import { background } from "../../assets";
import playbackBG2 from "../../assets/UI/playbackBG2.png"; import playbackBG2 from "../../assets/UI/playbackBG2.png";
import RestartSpinnerIcon from "../../assets/UI/RestartSpinnerIcon";
const TIME_BEFORE_INCREMENT_MS = 20000; const TIME_BEFORE_INCREMENT_MS = 20000;
const LOG_PREFIX = "[RecordPlayback.web]"; const LOG_PREFIX = "[RecordPlayback.web]";
@@ -736,6 +737,23 @@ const RecordPlayback = ({ route }) => {
}); });
}, 1000); }, 1000);
}, [songUrl, canRecord, resetSession, startPlayback]); }, [songUrl, canRecord, resetSession, startPlayback]);
const handleRestartRecording = useCallback(async () => {
console.log(LOG_PREFIX, "restartButton:pressed", {
isPreparing,
isRecording,
});
try {
await startCountdownThenRecord();
} catch (e) {
console.log(
LOG_PREFIX,
"restartButton:error",
String(e?.message || e || ""),
);
}
}, [isPreparing, isRecording, startCountdownThenRecord]);
const progressRatio = dur > 0 ? Math.min(1, pos / dur) : 0; const progressRatio = dur > 0 ? Math.min(1, pos / dur) : 0;
return ( return (
@@ -873,7 +891,7 @@ const RecordPlayback = ({ route }) => {
/> />
)} )}
{/* Progress circulaire */} {/* Progress circulaire + restart */}
{permissionsGranted && (isRecording || isPreparing) && ( {permissionsGranted && (isRecording || isPreparing) && (
<View <View
style={{ style={{
@@ -887,27 +905,38 @@ const RecordPlayback = ({ route }) => {
> >
<View <View
style={{ style={{
width: 100, width: 120,
height: 100, height: 120,
borderRadius: 105, borderRadius: 120,
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
}} }}
> >
<ProgressRing <ProgressRing
size={100} size={120}
strokeWidth={8} strokeWidth={8}
progress={progressRatio} progress={progressRatio}
/> />
<View <TouchableOpacity
activeOpacity={0.9}
onPress={handleRestartRecording}
style={{ style={{
position: "absolute", position: "absolute",
width: 50, width: 70,
height: 50, height: 70,
borderRadius: 55, borderRadius: 80,
alignItems: "center",
justifyContent: "center",
backgroundColor: Palette.white, backgroundColor: Palette.white,
shadowColor: "#000000",
shadowOpacity: 0.12,
shadowRadius: 10,
shadowOffset: { width: 0, height: 4 },
elevation: 4,
}} }}
/> >
<RestartSpinnerIcon />
</TouchableOpacity>
</View> </View>
</View> </View>
)} )}
@@ -981,7 +1010,7 @@ const ProgressRing = ({ size = 50, strokeWidth = 12, progress = 0 }) => {
style={{ style={{
transform: [{ rotate: "-90deg" }], transform: [{ rotate: "-90deg" }],
backgroundColor: "#ffffff3d", backgroundColor: "#ffffff3d",
borderRadius: 55, borderRadius: size / 2,
}} }}
> >
<Circle <Circle
+2 -6
View File
@@ -291,9 +291,7 @@ const Lyrics = ({ navigation }) => {
updateData.musicUrls = firebase.firestore.FieldValue.delete(); updateData.musicUrls = firebase.firestore.FieldValue.delete();
updateData.musicStatus = firebase.firestore.FieldValue.delete(); updateData.musicStatus = firebase.firestore.FieldValue.delete();
} }
await projectsRef await projectsRef.doc(selectedProjectId).set(updateData, { merge: true });
.doc(selectedProjectId)
.set(updateData, { merge: true });
const projectForStage = { const projectForStage = {
...selectedProject, ...selectedProject,
title: titleTrimmed, title: titleTrimmed,
@@ -507,9 +505,7 @@ const Lyrics = ({ navigation }) => {
<View style={styles.sensitiveCheckboxWrapper}> <View style={styles.sensitiveCheckboxWrapper}>
<AppCheckbox <AppCheckbox
selected={isSensitiveContentAcknowledged} selected={isSensitiveContentAcknowledged}
onPress={() => onPress={() => setIsSensitiveContentAcknowledged((prev) => !prev)}
setIsSensitiveContentAcknowledged((prev) => !prev)
}
label={RESPONSIBILITY_CHECKBOX_LABEL} label={RESPONSIBILITY_CHECKBOX_LABEL}
/> />
</View> </View>