generate two covers

This commit is contained in:
2025-10-23 10:16:36 +02:00
parent b4375d04be
commit ebe26542fe
9 changed files with 754 additions and 436 deletions
+186 -73
View File
@@ -1,8 +1,8 @@
import React from "react";
import { Image, View } from "react-native";
import { Image as ExpoImage } from "expo-image";
import React, { useCallback, useMemo, useState } from "react";
import { Pressable, Text, View } from "react-native";
import useMinuit from "react-native-minuit/src/hooks/useMinuit.js";
import { background, img } from "../../assets";
import BorderGradientButton from "../../components/BorderGradientButton";
import { background } from "../../assets";
import GradientButton from "../../components/GradientButton";
import MusicLandHeader from "../../components/MusicLandHeader";
import { isWeb } from "../../hooks/useLayoutType";
@@ -10,14 +10,34 @@ import Page from "../../layouts/Page";
import { Routes } from "../../navigation";
import { goBack, navigate } from "../../navigation/NavigationService";
import { useUserData } from "../../providers/UserDataProvider";
import { gutters, Style } from "../../styles";
import { gutters, Palette, Style } from "../../styles";
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
const ValidateCover = () => {
const { selectedProject, updateProjectData } = useUserData();
const { setIsLoading } = useMinuit();
const coverOptions = useMemo(() => {
if (!Array.isArray(selectedProject?.cover?.options)) {
return [];
}
return selectedProject.cover.options.filter((option) => option);
}, [selectedProject]);
const selectedOptionId = selectedProject?.cover?.selectedOptionId || null;
const selectedOption = useMemo(() => {
if (!coverOptions.length) {
return null;
}
const found = coverOptions.find(
(option) => option?.id === selectedOptionId
);
return found || coverOptions[0] || null;
}, [coverOptions, selectedOptionId]);
const isMultipleOptions = coverOptions.length > 1;
const [isSelecting, setIsSelecting] = useState(false);
async function onStartAgain() {
try {
await setIsLoading(true);
@@ -32,11 +52,59 @@ const ValidateCover = () => {
await setIsLoading(false);
}
}
const handleSelectOption = useCallback(
async (option) => {
if (!option || option?.id === selectedOptionId || isSelecting) {
return;
}
const existingCover = selectedProject?.cover || {};
const finalUrl = option.finalUrl || option.generatedUrl || null;
setIsSelecting(true);
try {
await updateProjectData({
cover: {
...existingCover,
options: coverOptions,
selectedOptionId: option.id,
result: finalUrl,
generatedBackground: option.generatedUrl || option.finalUrl || null,
},
});
} catch (e) {
console.log("ValidateCover: unable to select cover", e?.message);
} finally {
setIsSelecting(false);
}
},
[
coverOptions,
isSelecting,
selectedOptionId,
selectedProject,
updateProjectData,
]
);
async function onValidatePicture() {
if (!selectedOption) {
return;
}
try {
await setIsLoading(true);
const existingCover = selectedProject?.cover || {};
const finalUrl =
selectedOption.finalUrl || selectedOption.generatedUrl || null;
await updateProjectData({
coverUrl: selectedProject?.cover?.result,
cover: {
...existingCover,
options: coverOptions,
selectedOptionId: selectedOption.id,
result: finalUrl,
generatedBackground:
selectedOption.generatedUrl || selectedOption.finalUrl || null,
},
coverUrl: finalUrl,
});
navigate(Routes.Home);
} catch (e) {
@@ -45,80 +113,124 @@ const ValidateCover = () => {
await setIsLoading(false);
}
}
return (
<Page backgroundImg={background.studioBG2} headerType="NONE">
<MusicLandHeader onPressBack={goBack} progress={90} />
<View style={{ flex: 1, marginTop: 16 }}>
<CreateLyricsHeader
title="Ta pochette est prête!"
subTitle="Quen penses-tu ?"
subTitle="Choisis ta version préférée."
/>
<View style={{ flex: 1, ...Style.containerCenter }}>
<View style={{ width: "80%", position: "relative" }}>
<Image
source={img.placeholder}
style={{
width: isWeb ? 300 : "100%",
height: 300,
borderRadius: 20,
transform: [{ rotateY: "180deg" }],
alignSelf: "center",
}}
/>
<View
style={{ position: "absolute", width: "100%", height: "100%" }}
>
<Image
source={{ uri: selectedProject?.cover?.result }}
<View
style={{
width: "90%",
gap: 16,
flexDirection: isMultipleOptions ? "row" : "column",
flexWrap: isMultipleOptions ? "wrap" : "nowrap",
justifyContent: "center",
alignItems: isMultipleOptions ? "stretch" : "center",
}}
>
{coverOptions.map((option, index) => {
const isSelected = option?.id === selectedOption?.id;
const imageSource =
option?.finalUrl || option?.generatedUrl || null;
if (!imageSource) {
return null;
}
return (
<Pressable
key={option?.id || index}
onPress={() => handleSelectOption(option)}
disabled={isSelecting}
style={{
borderRadius: 20,
borderWidth: isSelected ? 2 : 1,
borderColor: isSelected
? Palette.primary
: Palette.ultraLightWhite,
overflow: "hidden",
width: isMultipleOptions
? isWeb
? 280
: "48%"
: isWeb
? 300
: "90%",
maxWidth: isWeb ? 320 : "100%",
position: "relative",
}}
>
<ExpoImage
source={{ uri: imageSource }}
cachePolicy="memory-disk"
contentFit="cover"
transition={120}
style={{ width: "100%", aspectRatio: 1 }}
/>
<View
style={{
position: "absolute",
top: 12,
right: 12,
backgroundColor: Palette.transparentBlack,
paddingHorizontal: 10,
paddingVertical: 6,
borderRadius: 12,
}}
>
<Text
style={{
color: Palette.white,
fontWeight: "600",
fontSize: 12,
}}
>
{`Option ${index + 1}`}
</Text>
</View>
{isSelected && (
<View
style={{
position: "absolute",
bottom: 12,
left: 12,
backgroundColor: Palette.primary,
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 12,
}}
>
<Text
style={{
color: Palette.white,
fontWeight: "600",
fontSize: 12,
}}
>
Sélectionnée
</Text>
</View>
)}
</Pressable>
);
})}
{!coverOptions.length && (
<Text
style={{
width: isWeb ? 300 : "100%",
height: "100%",
borderRadius: 20,
alignSelf: "center",
color: Palette.white,
textAlign: "center",
opacity: 0.8,
}}
/>
</View>
{/*<View*/}
{/* style={{*/}
{/* position: "absolute",*/}
{/* alignSelf: "center",*/}
{/* alignItems: "center",*/}
{/* top: 10,*/}
{/* }}*/}
{/*>*/}
{/* <Text*/}
{/* style={{*/}
{/* fontSize: 22,*/}
{/* color: Palette.white,*/}
{/* fontFamily: FONT_FAMILY.InterSemiBold,*/}
{/* }}*/}
{/* >*/}
{/* Lust for Life*/}
{/* </Text>*/}
{/* <Text*/}
{/* style={{*/}
{/* fontSize: 14,*/}
{/* color: Palette.gray,*/}
{/* fontFamily: FONT_FAMILY.InterRegular,*/}
{/* }}*/}
{/* >*/}
{/* Lana del Rey*/}
{/* </Text>*/}
{/*</View>*/}
{/*<View*/}
{/* style={{*/}
{/* position: "absolute",*/}
{/* alignItems: "center",*/}
{/* bottom: 10,*/}
{/* width: "100%",*/}
{/* }}*/}
{/*>*/}
{/* <Image*/}
{/* source={icons.musicLandLogo}*/}
{/* style={{ width: "100%", height: 30 }}*/}
{/* resizeMode="contain"*/}
{/* />*/}
{/*</View>*/}
>
Les options de pochette seront disponibles à la fin de la
génération.
</Text>
)}
</View>
</View>
</View>
@@ -130,13 +242,14 @@ const ValidateCover = () => {
gap: 12,
}}
>
<BorderGradientButton
{/* <BorderGradientButton
title="Recommencer la création"
onPress={onStartAgain}
/>
/> */}
<GradientButton
title="Valider la pochette"
onPress={onValidatePicture}
disabled={!selectedOption || isSelecting}
/>
</View>
</Page>