feat: fixes and formatter

This commit is contained in:
2026-01-12 16:01:32 +01:00
parent 85c6084351
commit 11e632acff
353 changed files with 23315 additions and 27361 deletions
+39 -47
View File
@@ -1,54 +1,48 @@
import { View, Text, Image } from "react-native";
import React, { useState } from "react";
import Page from "../../layouts/Page";
import { background, img } from "../../assets";
import MusicLandHeader from "../../components/MusicLandHeader";
import { goBack, navigate } from "../../navigation/NavigationService";
import { responsiveHeight } from "react-native-responsive-dimensions";
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
import Style, { size } from "../../styles/Style";
import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import { Routes } from "../../navigation";
import { useRoute } from "@react-navigation/native";
import { View, Text, Image } from 'react-native'
import React, { useState } from 'react'
import Page from '../../layouts/Page'
import { background, img } from '../../assets'
import MusicLandHeader from '../../components/MusicLandHeader'
import { goBack, navigate } from '../../navigation/NavigationService'
import { responsiveHeight } from 'react-native-responsive-dimensions'
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader'
import Style, { size } from '../../styles/Style'
import { Palette } from '../../styles'
import { FONT_FAMILY } from '../../styles/Fonts'
import { Routes } from '../../navigation'
import { useRoute } from '@react-navigation/native'
const DownloadPrices = () => {
const params = useRoute().params;
const action = params?.action;
const [selectedIndex, setSelectedIndex] = useState(null);
const params = useRoute().params
const action = params?.action
const [selectedIndex, setSelectedIndex] = useState(null)
const PRICES = [
{
title: action === "playback" ? "1 Playback" : "Je télécharge ma chanson",
description: `Ce ${
action === "playback" ? "Playback" : "morceau"
} uniquement`,
price: "6,99€",
title: action === 'playback' ? '1 Playback' : 'Je télécharge ma chanson',
description: `Ce ${action === 'playback' ? 'Playback' : 'morceau'} uniquement`,
price: '6,99€',
},
{
title: `3 ${action === "playback" ? "Playback" : "chansons"}`,
title: `3 ${action === 'playback' ? 'Playback' : 'chansons'}`,
description: `Ce ${
action === "playback" ? "Playback" : "morceau"
action === 'playback' ? 'Playback' : 'morceau'
} et les 2 prochains que tu crées`,
price: "12,99€",
price: '12,99€',
},
{
title: `5 ${action === "playback" ? "Playback" : "chansons"}`,
title: `5 ${action === 'playback' ? 'Playback' : 'chansons'}`,
description: `Ce ${
action === "playback" ? "Playback" : "morceau"
action === 'playback' ? 'Playback' : 'morceau'
} et les 4 prochains que tu crées`,
price: "19,99€",
price: '19,99€',
recommended: true,
},
];
]
return (
<Page
backgroundImg={
action === "playback"
? background.playbackBG2
: background.productionBG2
}
backgroundImg={action === 'playback' ? background.playbackBG2 : background.productionBG2}
headerType="NONE"
>
<MusicLandHeader onPressBack={goBack} progress={75} />
@@ -64,7 +58,7 @@ const DownloadPrices = () => {
style={{
...size({ size: 158 }),
borderRadius: 13,
alignSelf: "center",
alignSelf: 'center',
}}
/>
<View style={{ gap: 12, marginTop: 10 }}>
@@ -73,7 +67,7 @@ const DownloadPrices = () => {
fontSize: 22,
color: Palette.white,
fontFamily: FONT_FAMILY.InterSemiBold,
textAlign: "center",
textAlign: 'center',
}}
>
Tarifs
@@ -83,16 +77,14 @@ const DownloadPrices = () => {
<CreateLyricsHeader
key={index}
colors={
selectedIndex === index
? ["#F94697", "#7023F7"]
: [Palette.tran, Palette.tran]
selectedIndex === index ? ['#F94697', '#7023F7'] : [Palette.tran, Palette.tran]
}
tint={!item.recommended ? "dark" : "light"}
tint={!item.recommended ? 'dark' : 'light'}
onPress={() => {
setSelectedIndex(index);
setSelectedIndex(index)
navigate(Routes.SongDownloaded, {
action,
});
})
}}
>
<View style={{ paddingTop: 5 }}>
@@ -115,7 +107,7 @@ const DownloadPrices = () => {
{item.description}
</Text>
</View>
<View style={{ alignSelf: "flex-end", marginTop: 14 }}>
<View style={{ alignSelf: 'flex-end', marginTop: 14 }}>
<Text
style={{
fontSize: 12,
@@ -133,8 +125,8 @@ const DownloadPrices = () => {
height: 26,
...Style.containerCenter,
borderRadius: 100,
position: "absolute",
backgroundColor: "#FFFFFF33",
position: 'absolute',
backgroundColor: '#FFFFFF33',
bottom: 8,
left: 10,
}}
@@ -158,7 +150,7 @@ const DownloadPrices = () => {
</CreateLyricsHeader>
</View>
</Page>
);
};
)
}
export default DownloadPrices;
export default DownloadPrices
+226 -264
View File
@@ -1,353 +1,326 @@
import React, { useEffect, useMemo, useRef, useState } from "react";
import {
Image,
Platform,
Pressable,
ScrollView,
StyleSheet,
Text,
View,
} from "react-native";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import useMinuit from "react-native-minuit/src/hooks/useMinuit.js";
import { background } from "../../assets";
import FullscreenIntroVideo from "../../components/FullscreenIntroVideo";
import BorderGradientButton from "../../components/BorderGradientButton";
import MusicLandHeader from "../../components/MusicLandHeader";
import AppCheckbox from "../../components/AppCheckbox";
import firebase, {
projectsRef,
serverTimestamp,
videosRef,
} from "../../config/firebase";
import { uploadFileToFirebase } from "../../helpers/uploadToFirebase";
import useDataFromRef from "../../hooks/useDataFromRef";
import Page from "../../layouts/Page";
import { Routes } from "../../navigation";
import { goBack, navigate } from "../../navigation/NavigationService";
import { useUserData, useUser } from "../../providers/UserDataProvider";
import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import { size } from "../../styles/Style";
import { getBlobForUrl, releaseBlobUrl } from "../../utils/blobUrlCache";
import ClubAdvantagesCard from "../Profile/components/ClubAdvantagesCard";
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
import { Image as ExpoImage } from "expo-image";
import SubscriptionConfirmModal from "../../components/SubscriptionConfirmModal";
import { isWeb } from "../../hooks/useLayoutType";
import React, { useEffect, useMemo, useRef, useState } from 'react'
import { Image, Platform, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'
import { MaterialCommunityIcons } from '@expo/vector-icons'
import useMinuit from 'react-native-minuit/src/hooks/useMinuit.js'
import { background } from '../../assets'
import FullscreenIntroVideo from '../../components/FullscreenIntroVideo'
import BorderGradientButton from '../../components/BorderGradientButton'
import MusicLandHeader from '../../components/MusicLandHeader'
import AppCheckbox from '../../components/AppCheckbox'
import firebase, { projectsRef, serverTimestamp, videosRef } from '../../config/firebase'
import { uploadFileToFirebase } from '../../helpers/uploadToFirebase'
import useDataFromRef from '../../hooks/useDataFromRef'
import Page from '../../layouts/Page'
import { Routes } from '../../navigation'
import { goBack, navigate } from '../../navigation/NavigationService'
import { useUserData, useUser } from '../../providers/UserDataProvider'
import { Palette } from '../../styles'
import { FONT_FAMILY } from '../../styles/Fonts'
import { size } from '../../styles/Style'
import { getBlobForUrl, releaseBlobUrl } from '../../utils/blobUrlCache'
import ClubAdvantagesCard from '../Profile/components/ClubAdvantagesCard'
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader'
import { Image as ExpoImage } from 'expo-image'
import SubscriptionConfirmModal from '../../components/SubscriptionConfirmModal'
import { isWeb } from '../../hooks/useLayoutType'
const triggerWebDownload = async (url, title) => {
if (Platform.OS !== "web") return;
if (!url) return;
if (typeof document === "undefined") return;
const baseName = (title || "Playback").toString().trim() || "Playback";
const sanitized = baseName.replace(/[\\/:*?"<>|]/g, "-");
const extension = guessExtension(url) || "mp4";
const filename = `${sanitized}.${extension}`;
if (Platform.OS !== 'web') return
if (!url) return
if (typeof document === 'undefined') return
const baseName = (title || 'Playback').toString().trim() || 'Playback'
const sanitized = baseName.replace(/[\\/:*?"<>|]/g, '-')
const extension = guessExtension(url) || 'mp4'
const filename = `${sanitized}.${extension}`
try {
const response = await fetch(url);
if (!response.ok || response.type === "opaque") {
throw new Error(`download_failed_${response.status || "opaque"}`);
const response = await fetch(url)
if (!response.ok || response.type === 'opaque') {
throw new Error(`download_failed_${response.status || 'opaque'}`)
}
const blob = await response.blob();
const blobUrl = URL.createObjectURL(blob);
const anchor = document.createElement("a");
anchor.href = blobUrl;
anchor.download = filename;
anchor.rel = "noopener noreferrer";
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
URL.revokeObjectURL(blobUrl);
const blob = await response.blob()
const blobUrl = URL.createObjectURL(blob)
const anchor = document.createElement('a')
anchor.href = blobUrl
anchor.download = filename
anchor.rel = 'noopener noreferrer'
document.body.appendChild(anchor)
anchor.click()
document.body.removeChild(anchor)
URL.revokeObjectURL(blobUrl)
} catch (error) {
console.log("[PlaybackDownload] web download fallback", {
console.log('[PlaybackDownload] web download fallback', {
message: error?.message,
});
})
try {
const anchor = document.createElement("a");
anchor.href = url;
anchor.download = filename;
anchor.rel = "noopener noreferrer";
anchor.target = "_blank";
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
return;
const anchor = document.createElement('a')
anchor.href = url
anchor.download = filename
anchor.rel = 'noopener noreferrer'
anchor.target = '_blank'
document.body.appendChild(anchor)
anchor.click()
document.body.removeChild(anchor)
return
} catch (fallbackError) {
console.log("[PlaybackDownload] anchor fallback failed", {
console.log('[PlaybackDownload] anchor fallback failed', {
message: fallbackError?.message,
});
})
}
try {
window.open(url, "_blank", "noopener,noreferrer");
} catch { }
window.open(url, '_blank', 'noopener,noreferrer')
} catch {}
}
};
}
const guessExtension = (inputUri = "") => {
const cleaned = inputUri.split("?")[0] || "";
const match = cleaned.match(/\.([a-z0-9]+)$/i);
if (match && match[1]) return match[1].toLowerCase();
if (Platform.OS === "web") return "webm";
return "mp4";
};
const guessExtension = (inputUri = '') => {
const cleaned = inputUri.split('?')[0] || ''
const match = cleaned.match(/\.([a-z0-9]+)$/i)
if (match && match[1]) return match[1].toLowerCase()
if (Platform.OS === 'web') return 'webm'
return 'mp4'
}
const uploadSourceRecording = async ({ uri, uid, projectId }) => {
console.log("[PlaybackDownload] uploadSourceRecording params", {
console.log('[PlaybackDownload] uploadSourceRecording params', {
hasUri: Boolean(uri),
uid,
projectId,
});
})
if (!uri) throw new Error("Aucune vidéo trouvée");
if (!uid) throw new Error("Utilisateur non authentifié");
if (!uri) throw new Error('Aucune vidéo trouvée')
if (!uid) throw new Error('Utilisateur non authentifié')
const extension = guessExtension(uri);
const sourcePath = `users/${uid}/projects/${projectId}/recordings/source-${Date.now()}.${extension}`;
console.log("source path : ", sourcePath);
console.log("[PlaybackDownload] uploadSourceRecording source path", {
const extension = guessExtension(uri)
const sourcePath = `users/${uid}/projects/${projectId}/recordings/source-${Date.now()}.${extension}`
console.log('source path : ', sourcePath)
console.log('[PlaybackDownload] uploadSourceRecording source path', {
extension,
sourcePath,
});
})
const cachedBlob = getBlobForUrl(uri);
console.log("[PlaybackDownload] uploadSourceRecording blob cache", {
const cachedBlob = getBlobForUrl(uri)
console.log('[PlaybackDownload] uploadSourceRecording blob cache', {
hasBlob: Boolean(cachedBlob),
});
})
const { resultURI: videoUrl } = await uploadFileToFirebase({
uri,
path: sourcePath,
shouldCompress: true,
fileType: "VIDEO",
fileType: 'VIDEO',
blob: cachedBlob || undefined,
});
})
return { sourcePath, videoUrl };
};
return { sourcePath, videoUrl }
}
const PlaybackDownload = ({ route }) => {
const { currentUID } = useUserData();
const { hasActiveSubscription } = useUser() || {};
const { action, uri, project } = route.params || {};
console.log("[PlaybackDownload] route params", {
const { currentUID } = useUserData()
const { hasActiveSubscription } = useUser() || {}
const { action, uri, project } = route.params || {}
console.log('[PlaybackDownload] route params', {
action,
projectId: project?.id,
hasUri: Boolean(uri),
currentUID,
});
const { setIsLoading, setTooltip } = useMinuit();
const [isAfterPlaybackVideoVisible, setIsAfterPlaybackVideoVisible] =
useState(false);
const [showConfirmModal, setShowConfirmModal] = useState(false);
const [pendingPlaybackUrl, setPendingPlaybackUrl] = useState(
project?.playbackUrl || null,
);
const [isPublishing, setIsPublishing] = useState(false);
const [hasAcceptedPublication, setHasAcceptedPublication] = useState(true);
const hasShownAfterPlaybackRef = useRef(false);
})
const { setIsLoading, setTooltip } = useMinuit()
const [isAfterPlaybackVideoVisible, setIsAfterPlaybackVideoVisible] = useState(false)
const [showConfirmModal, setShowConfirmModal] = useState(false)
const [pendingPlaybackUrl, setPendingPlaybackUrl] = useState(project?.playbackUrl || null)
const [isPublishing, setIsPublishing] = useState(false)
const [hasAcceptedPublication, setHasAcceptedPublication] = useState(true)
const hasShownAfterPlaybackRef = useRef(false)
const { data: video } = useDataFromRef({
ref: videosRef.doc("fr"),
ref: videosRef.doc('fr'),
simpleRef: true,
});
})
const afterPlaybackUrl = useMemo(() => {
if (!video) return null;
return video?.afterPlayback;
}, [video]);
if (!video) return null
return video?.afterPlayback
}, [video])
useEffect(() => {
if (action !== "playback") return;
if (!afterPlaybackUrl) return;
if (hasShownAfterPlaybackRef.current) return;
hasShownAfterPlaybackRef.current = true;
setIsAfterPlaybackVideoVisible(true);
}, [action, afterPlaybackUrl]);
if (action !== 'playback') return
if (!afterPlaybackUrl) return
if (hasShownAfterPlaybackRef.current) return
hasShownAfterPlaybackRef.current = true
setIsAfterPlaybackVideoVisible(true)
}, [action, afterPlaybackUrl])
useEffect(() => {
if (project?.playbackUrl) {
setPendingPlaybackUrl(project.playbackUrl);
setPendingPlaybackUrl(project.playbackUrl)
}
}, [project?.playbackUrl]);
}, [project?.playbackUrl])
useEffect(() => {
return () => {
releaseBlobUrl(uri || null);
};
}, [uri]);
releaseBlobUrl(uri || null)
}
}, [uri])
const handleDownloadUri = async () => {
if (isPublishing) return;
if (action === "playback" && project?.id) {
if (isPublishing) return
if (action === 'playback' && project?.id) {
if (pendingPlaybackUrl) {
await triggerWebDownload(pendingPlaybackUrl, project?.title);
return;
await triggerWebDownload(pendingPlaybackUrl, project?.title)
return
}
// Publication du playback
console.log("[PlaybackDownload] handleDownloadUri playback", {
console.log('[PlaybackDownload] handleDownloadUri playback', {
projectId: project.id,
uri,
currentUID,
});
const audioUrl = project?.songUrl || null;
})
const audioUrl = project?.songUrl || null
if (!audioUrl) {
setTooltip({
type: "error",
text: "Aucune piste audio disponible pour ce projet",
});
return;
type: 'error',
text: 'Aucune piste audio disponible pour ce projet',
})
return
}
let tempSourcePath = null;
let tempSourcePath = null
try {
setIsLoading(true);
setIsLoading(true)
const { sourcePath, videoUrl } = await uploadSourceRecording({
uri,
uid: currentUID,
projectId: project.id,
});
tempSourcePath = sourcePath;
})
tempSourcePath = sourcePath
const callable = firebase
.functions()
.httpsCallable("upload-mergeVideoAndAudio");
const callable = firebase.functions().httpsCallable('upload-mergeVideoAndAudio')
const payload = {
projectId: project?.id,
videoUrl,
audioUrl,
storagePath: `users/${currentUID}/projects/${project.id}/playback.mp4`,
};
}
console.log(
"[PlaybackDownload] calling upload-mergeVideoAndAudio",
payload,
);
console.log('[PlaybackDownload] calling upload-mergeVideoAndAudio', payload)
const { data: result } = await callable(payload);
const { data: result } = await callable(payload)
console.log(
"[PlaybackDownload] upload-mergeVideoAndAudio result",
result,
);
console.log('[PlaybackDownload] upload-mergeVideoAndAudio result', result)
const resultURI = result?.url || null;
const resultURI = result?.url || null
if (resultURI) {
setPendingPlaybackUrl(resultURI);
setPendingPlaybackUrl(resultURI)
setTooltip({
type: "success",
text: "Playback prêt à télécharger",
});
await triggerWebDownload(resultURI, project?.title);
type: 'success',
text: 'Playback prêt à télécharger',
})
await triggerWebDownload(resultURI, project?.title)
if (tempSourcePath) {
try {
await firebase.storage().ref(tempSourcePath).delete();
await firebase.storage().ref(tempSourcePath).delete()
} catch (cleanupError) {
console.log("[PlaybackDownload] unable to delete temp source", {
console.log('[PlaybackDownload] unable to delete temp source', {
message: cleanupError?.message,
code: cleanupError?.code,
});
})
}
}
} else {
setTooltip({
type: "error",
text: "Erreur lors de la publication du playback",
});
type: 'error',
text: 'Erreur lors de la publication du playback',
})
}
} catch (error) {
console.log("[PlaybackDownload] error upload playback", {
console.log('[PlaybackDownload] error upload playback', {
message: error?.message,
code: error?.code,
name: error?.name,
details: error?.details,
});
})
if (tempSourcePath) {
try {
await firebase.storage().ref(tempSourcePath).delete();
} catch { }
await firebase.storage().ref(tempSourcePath).delete()
} catch {}
}
setTooltip({
type: "error",
text: String(
error?.message || "Erreur lors de la publication du playback",
),
});
type: 'error',
text: String(error?.message || 'Erreur lors de la publication du playback'),
})
} finally {
releaseBlobUrl(uri || null);
setIsLoading(false);
releaseBlobUrl(uri || null)
setIsLoading(false)
}
// Handle playback download
} else {
// Handle song download
}
};
}
const handlePublish = async () => {
if (isPublishing) return;
if (isPublishing) return
if (!hasAcceptedPublication) {
setTooltip({
type: "error",
text: "Confirme la diffusion sur Musicland et YouTube avant de publier",
});
return;
type: 'error',
text: 'Confirme la diffusion sur Musicland et YouTube avant de publier',
})
return
}
if (!project?.id) {
if (action === "playback") {
if (action === 'playback') {
setTooltip({
type: "success",
text: "Playback publié !",
});
navigate(Routes.Home);
type: 'success',
text: 'Playback publié !',
})
navigate(Routes.Home)
} else {
navigate(Routes.SongRelease, { action });
navigate(Routes.SongRelease, { action })
}
return;
return
}
let tempSourcePath = null;
let playbackUrlToSave = pendingPlaybackUrl || project?.playbackUrl || null;
const audioUrl = project?.songUrl || null;
let tempSourcePath = null
let playbackUrlToSave = pendingPlaybackUrl || project?.playbackUrl || null
const audioUrl = project?.songUrl || null
if (!audioUrl && !playbackUrlToSave) {
setTooltip({
type: "error",
text: "Aucune piste audio disponible pour ce projet",
});
return;
type: 'error',
text: 'Aucune piste audio disponible pour ce projet',
})
return
}
try {
setIsPublishing(true);
setIsLoading(true);
setIsPublishing(true)
setIsLoading(true)
if (!playbackUrlToSave) {
const { sourcePath, videoUrl } = await uploadSourceRecording({
uri,
uid: currentUID,
projectId: project.id,
});
tempSourcePath = sourcePath;
})
tempSourcePath = sourcePath
const callable = firebase
.functions()
.httpsCallable("upload-mergeVideoAndAudio");
const callable = firebase.functions().httpsCallable('upload-mergeVideoAndAudio')
const payload = {
projectId: project?.id,
videoUrl,
audioUrl,
storagePath: `users/${currentUID}/projects/${project.id}/playback.mp4`,
};
console.log("[PlaybackDownload] publish: calling merge", payload);
const { data: result } = await callable(payload);
playbackUrlToSave = result?.url || null;
if (!playbackUrlToSave) {
throw new Error("merge_failed");
}
setPendingPlaybackUrl(playbackUrlToSave);
console.log('[PlaybackDownload] publish: calling merge', payload)
const { data: result } = await callable(payload)
playbackUrlToSave = result?.url || null
if (!playbackUrlToSave) {
throw new Error('merge_failed')
}
setPendingPlaybackUrl(playbackUrlToSave)
}
await projectsRef.doc(project.id).set(
@@ -355,47 +328,45 @@ const PlaybackDownload = ({ route }) => {
playbackUrl: playbackUrlToSave,
updatedAt: serverTimestamp(),
},
{ merge: true },
);
{ merge: true }
)
if (action === "playback") {
if (action === 'playback') {
setTooltip({
type: "success",
text: "Playback publié !",
});
navigate(Routes.Home);
type: 'success',
text: 'Playback publié !',
})
navigate(Routes.Home)
} else {
navigate(Routes.SongRelease, { action });
navigate(Routes.SongRelease, { action })
}
} catch (error) {
console.log("[PlaybackDownload] publish error", {
console.log('[PlaybackDownload] publish error', {
message: error?.message,
code: error?.code,
name: error?.name,
details: error?.details,
});
})
setTooltip({
type: "error",
text: String(error?.message || "Publication impossible"),
});
type: 'error',
text: String(error?.message || 'Publication impossible'),
})
} finally {
if (tempSourcePath) {
try {
await firebase.storage().ref(tempSourcePath).delete();
await firebase.storage().ref(tempSourcePath).delete()
} catch (cleanupError) {
console.log("[PlaybackDownload] unable to delete temp source", {
console.log('[PlaybackDownload] unable to delete temp source', {
message: cleanupError?.message,
code: cleanupError?.code,
});
})
}
}
setIsPublishing(false);
setIsLoading(false);
setIsPublishing(false)
setIsLoading(false)
}
};
const continueLabel = hasActiveSubscription
? "Publier"
: "Publier sans générer de revenus";
}
const continueLabel = hasActiveSubscription ? 'Publier' : 'Publier sans générer de revenus'
return (
<>
<Page
@@ -408,11 +379,8 @@ const PlaybackDownload = ({ route }) => {
headerType="NONE"
>
<MusicLandHeader onPressBack={goBack} progress={50} />
<ScrollView
contentContainerStyle={styles.container}
showsVerticalScrollIndicator={false}
>
<CreateLyricsHeader title={"Publier ton playback"} />
<ScrollView contentContainerStyle={styles.container} showsVerticalScrollIndicator={false}>
<CreateLyricsHeader title={'Publier ton playback'} />
<View style={styles.coverRow}>
{project?.coverUrl ? (
@@ -428,11 +396,7 @@ const PlaybackDownload = ({ route }) => {
)}
<Pressable style={styles.downloadTile} onPress={handleDownloadUri}>
<MaterialCommunityIcons
name="download"
size={22}
color={Palette.white}
/>
<MaterialCommunityIcons name="download" size={22} color={Palette.white} />
<Text style={styles.downloadText}>Télécharger le playback</Text>
</Pressable>
</View>
@@ -442,9 +406,7 @@ const PlaybackDownload = ({ route }) => {
<View style={styles.publishConsentContainer}>
<AppCheckbox
selected={hasAcceptedPublication}
onPress={() =>
setHasAcceptedPublication((prevState) => !prevState)
}
onPress={() => setHasAcceptedPublication((prevState) => !prevState)}
label="J'accepte la diffusion de mon playback sur Musicland et YouTube."
/>
<Text style={styles.publishConsentDescription}>
@@ -456,9 +418,9 @@ const PlaybackDownload = ({ route }) => {
title={continueLabel}
onPress={() => {
if (hasActiveSubscription) {
handlePublish();
handlePublish()
} else {
setShowConfirmModal(true);
setShowConfirmModal(true)
}
}}
disabled={isPublishing || !hasAcceptedPublication}
@@ -467,17 +429,17 @@ const PlaybackDownload = ({ route }) => {
</ScrollView>
</Page>
<SubscriptionConfirmModal
continueLabel={"Continuer vers la publication de playback"}
continueLabel={'Continuer vers la publication de playback'}
isVisible={showConfirmModal}
setIsVisible={setShowConfirmModal}
onJoinClub={() => navigate(Routes.Payments)}
onContinue={handlePublish}
/>
</>
);
};
)
}
export default PlaybackDownload;
export default PlaybackDownload
const styles = StyleSheet.create({
container: {
@@ -487,25 +449,25 @@ const styles = StyleSheet.create({
gap: 14,
},
coverRow: {
flexDirection: isWeb ? "row" : "column",
alignItems: "center",
justifyContent: isWeb ? "center" : "center",
flexDirection: isWeb ? 'row' : 'column',
alignItems: 'center',
justifyContent: isWeb ? 'center' : 'center',
gap: 12,
},
coverImage: {
...size({ size: 140 }),
borderRadius: 14,
borderWidth: 1,
borderColor: "rgba(255, 255, 255, 0.18)",
borderColor: 'rgba(255, 255, 255, 0.18)',
},
downloadTile: {
flexDirection: "row",
alignItems: "center",
flexDirection: 'row',
alignItems: 'center',
gap: 10,
paddingVertical: 10,
paddingHorizontal: 14,
borderRadius: 14,
backgroundColor: "#8C4BFF",
backgroundColor: '#8C4BFF',
},
downloadText: {
fontFamily: FONT_FAMILY.InterSemiBold,
@@ -528,4 +490,4 @@ const styles = StyleSheet.create({
continueButton: {
marginTop: 10,
},
});
})
+19 -21
View File
@@ -1,30 +1,28 @@
import { View, Text, Image } from "react-native";
import React from "react";
import Page from "../../layouts/Page";
import { background, img } from "../../assets";
import MusicLandHeader from "../../components/MusicLandHeader";
import { goBack, navigate } from "../../navigation/NavigationService";
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
import Slider from "../../components/Slider";
import { gutters } from "../../styles";
import GradientButton from "../../components/GradientButton";
import { Routes } from "../../navigation";
import { View, Text, Image } from 'react-native'
import React from 'react'
import Page from '../../layouts/Page'
import { background, img } from '../../assets'
import MusicLandHeader from '../../components/MusicLandHeader'
import { goBack, navigate } from '../../navigation/NavigationService'
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader'
import Slider from '../../components/Slider'
import { gutters } from '../../styles'
import GradientButton from '../../components/GradientButton'
import { Routes } from '../../navigation'
const PlaybackExample = () => {
return (
<Page backgroundImg={background.productionBG} headerType="NONE">
<MusicLandHeader progress={50} onPressBack={goBack} />
<View
style={{ flex: 1, paddingTop: 12, gap: 14, paddingBottom: gutters * 2 }}
>
<View style={{ flex: 1, paddingTop: 12, gap: 14, paddingBottom: gutters * 2 }}>
<CreateLyricsHeader title="Exemple" subTitle="Exemple de Playback" />
<View style={{ flex: 1, gap: 22 }}>
<Image
source={img.placeholder3}
style={{
width: "80%",
width: '80%',
flex: 1,
alignSelf: "center",
alignSelf: 'center',
borderRadius: 16,
}}
/>
@@ -33,15 +31,15 @@ const PlaybackExample = () => {
<GradientButton
title="Je veux devenir Playbacker"
containerStyle={{
width: "80%",
alignSelf: "center",
width: '80%',
alignSelf: 'center',
marginTop: 4,
}}
onPress={() => navigate(Routes.PlaybackOnboarding)}
/>
</View>
</Page>
);
};
)
}
export default PlaybackExample;
export default PlaybackExample
+14 -14
View File
@@ -1,13 +1,13 @@
import { View } from "react-native";
import React from "react";
import Page from "../../layouts/Page";
import { background } from "../../assets";
import { gutters } from "../../styles";
import GradientButton from "../../components/GradientButton";
import MusicLandHeader from "../../components/MusicLandHeader";
import { goBack, navigate } from "../../navigation/NavigationService";
import BorderGradientButton from "../../components/BorderGradientButton";
import { Routes } from "../../navigation";
import { View } from 'react-native'
import React from 'react'
import Page from '../../layouts/Page'
import { background } from '../../assets'
import { gutters } from '../../styles'
import GradientButton from '../../components/GradientButton'
import MusicLandHeader from '../../components/MusicLandHeader'
import { goBack, navigate } from '../../navigation/NavigationService'
import BorderGradientButton from '../../components/BorderGradientButton'
import { Routes } from '../../navigation'
const Production = () => {
return (
@@ -17,7 +17,7 @@ const Production = () => {
style={{
flex: 1,
paddingBottom: gutters * 2,
justifyContent: "flex-end",
justifyContent: 'flex-end',
gap: 37,
}}
>
@@ -38,7 +38,7 @@ const Production = () => {
</View>
</View>
</Page>
);
};
)
}
export default Production;
export default Production
+13 -16
View File
@@ -1,11 +1,11 @@
import { View, Text } from "react-native";
import React from "react";
import Page from "../../layouts/Page";
import { background } from "../../assets";
import { gutters } from "../../styles";
import GradientButton from "../../components/GradientButton";
import { navigate } from "../../navigation/NavigationService";
import { Routes } from "../../navigation";
import { View, Text } from 'react-native'
import React from 'react'
import Page from '../../layouts/Page'
import { background } from '../../assets'
import { gutters } from '../../styles'
import GradientButton from '../../components/GradientButton'
import { navigate } from '../../navigation/NavigationService'
import { Routes } from '../../navigation'
const ProductionOnboarding = () => {
return (
@@ -14,16 +14,13 @@ const ProductionOnboarding = () => {
style={{
flex: 1,
paddingBottom: gutters * 2,
justifyContent: "flex-end",
justifyContent: 'flex-end',
}}
>
<GradientButton
title="Commence"
onPress={() => navigate(Routes.Production)}
/>
<GradientButton title="Commence" onPress={() => navigate(Routes.Production)} />
</View>
</Page>
);
};
)
}
export default ProductionOnboarding;
export default ProductionOnboarding
+35 -41
View File
@@ -1,38 +1,36 @@
import { View, Text, Image } from 'react-native';
import React from 'react';
import Page from '../../layouts/Page';
import { background, img } from '../../assets';
import { goBack, navigate } from '../../navigation/NavigationService';
import MusicLandHeader from '../../components/MusicLandHeader';
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader';
import { size } from '../../styles/Style';
import BorderGradientButton from '../../components/BorderGradientButton';
import GradientButton from '../../components/GradientButton';
import { responsiveHeight } from 'react-native-responsive-dimensions';
import { Palette } from '../../styles';
import { FONT_FAMILY } from '../../styles/Fonts';
import { Routes } from '../../navigation';
import { useRoute } from '@react-navigation/native';
import { View, Text, Image } from 'react-native'
import React from 'react'
import Page from '../../layouts/Page'
import { background, img } from '../../assets'
import { goBack, navigate } from '../../navigation/NavigationService'
import MusicLandHeader from '../../components/MusicLandHeader'
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader'
import { size } from '../../styles/Style'
import BorderGradientButton from '../../components/BorderGradientButton'
import GradientButton from '../../components/GradientButton'
import { responsiveHeight } from 'react-native-responsive-dimensions'
import { Palette } from '../../styles'
import { FONT_FAMILY } from '../../styles/Fonts'
import { Routes } from '../../navigation'
import { useRoute } from '@react-navigation/native'
const SongDownloaded = () => {
const params = useRoute().params;
const action = params?.action;
const params = useRoute().params
const action = params?.action
return (
<Page
backgroundImg={
action === 'playback'
? background.playbackBG2
: background.productionBG2
}
headerType='NONE'>
backgroundImg={action === 'playback' ? background.playbackBG2 : background.productionBG2}
headerType="NONE"
>
<MusicLandHeader onPressBack={goBack} progress={100} />
<View style={{ flex: 1, paddingTop: responsiveHeight(15) }}>
<CreateLyricsHeader
gradientProps={{
start: { x: 0, y: 0 },
end: { x: 0, y: 1 },
}}>
}}
>
<Image
source={img.placeholder2}
style={{
@@ -48,7 +46,8 @@ const SongDownloaded = () => {
color: Palette.white,
fontFamily: FONT_FAMILY.InterSemiBold,
textAlign: 'center',
}}>
}}
>
{action === 'playback' ? 'Playback' : 'Chanson'} téléchargée !
</Text>
<Text
@@ -56,10 +55,10 @@ const SongDownloaded = () => {
fontSize: 16,
color: Palette.white,
fontFamily: FONT_FAMILY.InterMedium,
}}>
{action === 'playback' ? 'Ton Playback' : 'Ta Chanson'} est
désormais téléchargée dans tes fichiers. Nhésite pas à la
partager avec tes proches.
}}
>
{action === 'playback' ? 'Ton Playback' : 'Ta Chanson'} est désormais téléchargée dans
tes fichiers. Nhésite pas à la partager avec tes proches.
</Text>
</View>
<View
@@ -69,20 +68,15 @@ const SongDownloaded = () => {
paddingBottom: 12,
width: '80%',
alignSelf: 'center',
}}>
<BorderGradientButton
title='Partager'
onPress={() => navigate(Routes.Production)}
/>
<GradientButton
title="Page d'accueil"
onPress={() => navigate(Routes.BottomTab)}
/>
}}
>
<BorderGradientButton title="Partager" onPress={() => navigate(Routes.Production)} />
<GradientButton title="Page d'accueil" onPress={() => navigate(Routes.BottomTab)} />
</View>
</CreateLyricsHeader>
</View>
</Page>
);
};
)
}
export default SongDownloaded;
export default SongDownloaded
+31 -41
View File
@@ -1,30 +1,26 @@
import { View, Text, Image } from "react-native";
import React from "react";
import MusicLandHeader from "../../components/MusicLandHeader";
import Page from "../../layouts/Page";
import { background, img } from "../../assets";
import { goBack, navigate } from "../../navigation/NavigationService";
import { responsiveHeight } from "react-native-responsive-dimensions";
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
import { size } from "../../styles/Style";
import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import BorderGradientButton from "../../components/BorderGradientButton";
import GradientButton from "../../components/GradientButton";
import { Routes } from "../../navigation";
import { useRoute } from "@react-navigation/native";
import { View, Text, Image } from 'react-native'
import React from 'react'
import MusicLandHeader from '../../components/MusicLandHeader'
import Page from '../../layouts/Page'
import { background, img } from '../../assets'
import { goBack, navigate } from '../../navigation/NavigationService'
import { responsiveHeight } from 'react-native-responsive-dimensions'
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader'
import { size } from '../../styles/Style'
import { Palette } from '../../styles'
import { FONT_FAMILY } from '../../styles/Fonts'
import BorderGradientButton from '../../components/BorderGradientButton'
import GradientButton from '../../components/GradientButton'
import { Routes } from '../../navigation'
import { useRoute } from '@react-navigation/native'
const SongRelease = () => {
const params = useRoute().params;
const action = params?.action;
const params = useRoute().params
const action = params?.action
return (
<Page
backgroundImg={
action === "playback"
? background.playbackBG2
: background.productionBG2
}
backgroundImg={action === 'playback' ? background.playbackBG2 : background.productionBG2}
headerType="NONE"
>
<MusicLandHeader onPressBack={goBack} progress={100} />
@@ -40,7 +36,7 @@ const SongRelease = () => {
style={{
...size({ size: 158 }),
borderRadius: 13,
alignSelf: "center",
alignSelf: 'center',
}}
/>
<View style={{ gap: 2, marginTop: 12 }}>
@@ -49,10 +45,10 @@ const SongRelease = () => {
fontSize: 22,
color: Palette.white,
fontFamily: FONT_FAMILY.InterSemiBold,
textAlign: "center",
textAlign: 'center',
}}
>
{action === "playback" ? "Playback" : "Chanson"} publiée !
{action === 'playback' ? 'Playback' : 'Chanson'} publiée !
</Text>
<Text
style={{
@@ -61,9 +57,9 @@ const SongRelease = () => {
fontFamily: FONT_FAMILY.InterMedium,
}}
>
{action === "playback" ? "Ton Playback" : "Ta Chanson"} est
désormais accessible sur lapplication et les réseaux sociaux de
MusicLand. Nhésite pas à la partager avec tes proches.
{action === 'playback' ? 'Ton Playback' : 'Ta Chanson'} est désormais accessible sur
lapplication et les réseaux sociaux de MusicLand. Nhésite pas à la partager avec tes
proches.
</Text>
</View>
<View
@@ -71,23 +67,17 @@ const SongRelease = () => {
marginTop: 26,
gap: 12,
paddingBottom: 12,
width: "80%",
alignSelf: "center",
width: '80%',
alignSelf: 'center',
}}
>
<BorderGradientButton
title="Partager"
onPress={() => navigate(Routes.Production)}
/>
<GradientButton
title="Page d'accueil"
onPress={() => navigate(Routes.Production)}
/>
<BorderGradientButton title="Partager" onPress={() => navigate(Routes.Production)} />
<GradientButton title="Page d'accueil" onPress={() => navigate(Routes.Production)} />
</View>
</CreateLyricsHeader>
</View>
</Page>
);
};
)
}
export default SongRelease;
export default SongRelease
+59 -65
View File
@@ -1,73 +1,69 @@
import { View, Text, Image } from "react-native";
import React, { useCallback, useEffect, useState } from "react";
import AppCheckbox from "../../components/AppCheckbox";
import Page from "../../layouts/Page";
import MusicLandHeader from "../../components/MusicLandHeader";
import GradientButton from "../../components/GradientButton";
import { background, img } from "../../assets";
import { goBack, navigate } from "../../navigation/NavigationService";
import useMinuit from "react-native-minuit/src/hooks/useMinuit.js";
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
import { responsiveHeight } from "react-native-responsive-dimensions";
import { size } from "../../styles/Style";
import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import { Routes } from "../../navigation";
import { useRoute } from "@react-navigation/native";
import { useUser } from "../../providers/UserDataProvider";
import { View, Text, Image } from 'react-native'
import React, { useCallback, useEffect, useState } from 'react'
import AppCheckbox from '../../components/AppCheckbox'
import Page from '../../layouts/Page'
import MusicLandHeader from '../../components/MusicLandHeader'
import GradientButton from '../../components/GradientButton'
import { background, img } from '../../assets'
import { goBack, navigate } from '../../navigation/NavigationService'
import useMinuit from 'react-native-minuit/src/hooks/useMinuit.js'
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader'
import { responsiveHeight } from 'react-native-responsive-dimensions'
import { size } from '../../styles/Style'
import { Palette } from '../../styles'
import { FONT_FAMILY } from '../../styles/Fonts'
import { Routes } from '../../navigation'
import { useRoute } from '@react-navigation/native'
import { useUser } from '../../providers/UserDataProvider'
const StreamSong = () => {
const params = useRoute().params;
const action = params?.action;
const project = params?.project || null;
const { hasActiveSubscription } = useUser() || {};
const userHasActiveSubscription = hasActiveSubscription;
const { setTooltip } = useMinuit();
const [hasAcceptedPublication, setHasAcceptedPublication] = useState(true);
const params = useRoute().params
const action = params?.action
const project = params?.project || null
const { hasActiveSubscription } = useUser() || {}
const userHasActiveSubscription = hasActiveSubscription
const { setTooltip } = useMinuit()
const [hasAcceptedPublication, setHasAcceptedPublication] = useState(true)
useEffect(() => {
if (action && action !== "playback") {
navigate(Routes.SongDownload, { project });
if (action && action !== 'playback') {
navigate(Routes.SongDownload, { project })
}
}, [action, project]);
}, [action, project])
const headingText = "Publier ton playback";
const headingText = 'Publier ton playback'
const descriptionText = userHasActiveSubscription
? "Ton abonnement est actif, publie ton playback et participe au concours."
: "Pour publier ton playback sur la plateforme, rejoins le Club MusicLand.";
? 'Ton abonnement est actif, publie ton playback et participe au concours.'
: 'Pour publier ton playback sur la plateforme, rejoins le Club MusicLand.'
const ctaLabel = userHasActiveSubscription
? "Publier mon playback"
: "Rejoindre le Club MusicLand";
? 'Publier mon playback'
: 'Rejoindre le Club MusicLand'
const handlePrimaryAction = useCallback(() => {
if (!hasAcceptedPublication) {
setTooltip({
type: "error",
text: "Confirme la diffusion sur Musicland et YouTube avant de publier",
});
return;
type: 'error',
text: 'Confirme la diffusion sur Musicland et YouTube avant de publier',
})
return
}
if (userHasActiveSubscription) {
if (action === "playback") {
if (action === 'playback') {
setTooltip({
type: "success",
text: "Playback publié !",
});
navigate(Routes.Home);
return;
type: 'success',
text: 'Playback publié !',
})
navigate(Routes.Home)
return
}
navigate(Routes.SongRelease, { action });
return;
navigate(Routes.SongRelease, { action })
return
}
navigate(Routes.Payments);
}, [action, hasAcceptedPublication, setTooltip, userHasActiveSubscription]);
navigate(Routes.Payments)
}, [action, hasAcceptedPublication, setTooltip, userHasActiveSubscription])
return (
<Page
backgroundImg={
action === "playback"
? background.playbackBG2
: background.productionBG2
}
backgroundImg={action === 'playback' ? background.playbackBG2 : background.productionBG2}
headerType="NONE"
>
<MusicLandHeader onPressBack={goBack} progress={75} />
@@ -83,7 +79,7 @@ const StreamSong = () => {
style={{
...size({ size: 158 }),
borderRadius: 13,
alignSelf: "center",
alignSelf: 'center',
}}
/>
<View style={{ gap: 12 }}>
@@ -93,7 +89,7 @@ const StreamSong = () => {
fontSize: 22,
color: Palette.white,
fontFamily: FONT_FAMILY.InterSemiBold,
textAlign: "center",
textAlign: 'center',
}}
>
{headingText}
@@ -103,7 +99,7 @@ const StreamSong = () => {
fontSize: 16,
color: Palette.white,
fontFamily: FONT_FAMILY.InterMedium,
textAlign: "center",
textAlign: 'center',
}}
>
{descriptionText}
@@ -115,9 +111,7 @@ const StreamSong = () => {
<View style={{ gap: 6 }}>
<AppCheckbox
selected={hasAcceptedPublication}
onPress={() =>
setHasAcceptedPublication((prevState) => !prevState)
}
onPress={() => setHasAcceptedPublication((prevState) => !prevState)}
label="J'accepte la diffusion de mon contenu sur Musicland et YouTube."
/>
<Text
@@ -135,9 +129,9 @@ const StreamSong = () => {
title={ctaLabel}
onPress={handlePrimaryAction}
containerStyle={{
width: "100%",
width: '100%',
maxWidth: 320,
alignSelf: "center",
alignSelf: 'center',
}}
/>
{!userHasActiveSubscription && (
@@ -146,18 +140,18 @@ const StreamSong = () => {
fontSize: 14,
color: Palette.white,
fontFamily: FONT_FAMILY.InterRegular,
textAlign: "center",
textAlign: 'center',
opacity: 0.8,
}}
>
Rejoins le club pour débloquer la publication de tes créations et
tenter de grimper dans le classement.
Rejoins le club pour débloquer la publication de tes créations et tenter de grimper
dans le classement.
</Text>
)}
</View>
</View>
</Page>
);
};
)
}
export default StreamSong;
export default StreamSong