feat: pouch
This commit is contained in:
+589
-60
@@ -1,9 +1,19 @@
|
|||||||
|
import { BlurView } from "expo-blur";
|
||||||
import { Image as ExpoImage } from "expo-image";
|
import { Image as ExpoImage } from "expo-image";
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { ActivityIndicator, Text, View } from "react-native";
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
Image,
|
||||||
|
Pressable,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
View,
|
||||||
|
} from "react-native";
|
||||||
import useMinuit from "react-native-minuit/src/hooks/useMinuit.js";
|
import useMinuit from "react-native-minuit/src/hooks/useMinuit.js";
|
||||||
import { background, icons } from "../../assets";
|
import { background, icons } from "../../assets";
|
||||||
import alert from "../../components/Alert";
|
import alert from "../../components/Alert";
|
||||||
|
import AppCheckbox from "../../components/AppCheckbox";
|
||||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||||
import GradientButton from "../../components/GradientButton";
|
import GradientButton from "../../components/GradientButton";
|
||||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||||
@@ -15,27 +25,69 @@ import { Routes } from "../../navigation";
|
|||||||
import { goBack, navigate } from "../../navigation/NavigationService";
|
import { goBack, navigate } from "../../navigation/NavigationService";
|
||||||
import { useUser } from "../../providers/UserDataProvider";
|
import { useUser } from "../../providers/UserDataProvider";
|
||||||
import { gutters, Palette, Style } from "../../styles";
|
import { gutters, Palette, Style } from "../../styles";
|
||||||
|
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||||
|
import { getStageAction } from "../../utils/projectStages";
|
||||||
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
|
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
|
||||||
import CustomInput from "../Writing/components/CustomInput";
|
|
||||||
|
const COVER_STYLE_PRESETS = [
|
||||||
|
"Néo-Néon / Cyberpunk",
|
||||||
|
"Photographie Minimaliste & Éditoriale",
|
||||||
|
"Illustration & Collage Surréaliste",
|
||||||
|
"Anti-Design / Maximalisme (Tendance Actuelle)",
|
||||||
|
];
|
||||||
|
|
||||||
const PouchReady = () => {
|
const PouchReady = () => {
|
||||||
const { selectedProjectId, selectedProject, updateProjectData } = useUser();
|
const { selectedProjectId, selectedProject, updateProjectData } = useUser();
|
||||||
const { setIsLoading } = useMinuit();
|
const { setIsLoading } = useMinuit();
|
||||||
const isGenerating = selectedProject?.coverStatus === "GENERATING";
|
const isGenerating = selectedProject?.coverStatus === "GENERATING";
|
||||||
const coverOptions = Array.isArray(selectedProject?.cover?.options)
|
const coverOptions = useMemo(() => {
|
||||||
? selectedProject.cover.options
|
if (!Array.isArray(selectedProject?.cover?.options)) {
|
||||||
: [];
|
return [];
|
||||||
|
}
|
||||||
|
return selectedProject.cover.options.filter(Boolean);
|
||||||
|
}, [selectedProject?.cover?.options]);
|
||||||
const hasGeneratedOptions = coverOptions.length > 0;
|
const hasGeneratedOptions = coverOptions.length > 0;
|
||||||
|
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 coverBackgroundMessage = isWeb
|
const coverBackgroundMessage = isWeb
|
||||||
? loaderMessages.pouchReadyGenerationWeb
|
? loaderMessages.pouchReadyGenerationWeb
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
const [coverStyle, setCoverStyle] = useState(
|
const [styleMode, setStyleMode] = useState("preset");
|
||||||
selectedProject?.coverStyle || ""
|
const [selectedPresetStyle, setSelectedPresetStyle] = useState(
|
||||||
|
COVER_STYLE_PRESETS[0]
|
||||||
);
|
);
|
||||||
|
const [customStyle, setCustomStyle] = useState("");
|
||||||
|
const [isPresetDropdownOpen, setIsPresetDropdownOpen] = useState(false);
|
||||||
|
const [isSelecting, setIsSelecting] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCoverStyle(selectedProject?.coverStyle || "");
|
const projectStyle = (selectedProject?.coverStyle || "").trim();
|
||||||
|
const isPresetStyle = COVER_STYLE_PRESETS.includes(projectStyle);
|
||||||
|
|
||||||
|
if (!projectStyle) {
|
||||||
|
setStyleMode("preset");
|
||||||
|
setSelectedPresetStyle(COVER_STYLE_PRESETS[0]);
|
||||||
|
setCustomStyle("");
|
||||||
|
setIsPresetDropdownOpen(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPresetStyle) {
|
||||||
|
setStyleMode("preset");
|
||||||
|
setSelectedPresetStyle(projectStyle);
|
||||||
|
setCustomStyle("");
|
||||||
|
} else {
|
||||||
|
setStyleMode("custom");
|
||||||
|
setCustomStyle(projectStyle);
|
||||||
|
}
|
||||||
|
setIsPresetDropdownOpen(false);
|
||||||
}, [selectedProject?.coverStyle]);
|
}, [selectedProject?.coverStyle]);
|
||||||
|
|
||||||
const generateCover = useCallback(
|
const generateCover = useCallback(
|
||||||
@@ -75,7 +127,9 @@ const PouchReady = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const requestCoverGeneration = useCallback(() => {
|
const requestCoverGeneration = useCallback(() => {
|
||||||
const trimmedStyle = (coverStyle || "").trim();
|
const selectedStyle =
|
||||||
|
styleMode === "preset" ? selectedPresetStyle : customStyle;
|
||||||
|
const trimmedStyle = (selectedStyle || "").trim();
|
||||||
if (!trimmedStyle) {
|
if (!trimmedStyle) {
|
||||||
alert("Attention", "Merci de renseigner un style pour la pochette.", [
|
alert("Attention", "Merci de renseigner un style pour la pochette.", [
|
||||||
{ text: "OK" },
|
{ text: "OK" },
|
||||||
@@ -92,17 +146,16 @@ const PouchReady = () => {
|
|||||||
onPress: () => generateCover(trimmedStyle),
|
onPress: () => generateCover(trimmedStyle),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}, [coverStyle, generateCover]);
|
}, [customStyle, generateCover, selectedPresetStyle, styleMode]);
|
||||||
|
|
||||||
const validateCover = () => {
|
|
||||||
navigate(Routes.ValidateCover);
|
|
||||||
};
|
|
||||||
|
|
||||||
const coverPreviewUrl =
|
const coverPreviewUrl =
|
||||||
selectedProject?.cover?.result ||
|
selectedProject?.cover?.result ||
|
||||||
selectedProject?.cover?.generatedBackground ||
|
selectedProject?.cover?.generatedBackground ||
|
||||||
null;
|
null;
|
||||||
|
|
||||||
|
const shouldShowStylePanel =
|
||||||
|
!isGenerating && !hasGeneratedOptions && !coverPreviewUrl;
|
||||||
|
|
||||||
const displayOptions = hasGeneratedOptions
|
const displayOptions = hasGeneratedOptions
|
||||||
? coverOptions
|
? coverOptions
|
||||||
: coverPreviewUrl
|
: coverPreviewUrl
|
||||||
@@ -115,10 +168,120 @@ const PouchReady = () => {
|
|||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
const handleSelectOption = useCallback(
|
||||||
|
async (option) => {
|
||||||
|
if (
|
||||||
|
!option ||
|
||||||
|
option?.id === selectedOptionId ||
|
||||||
|
isSelecting ||
|
||||||
|
!option?.id
|
||||||
|
) {
|
||||||
|
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("PouchReady: unable to select cover", e?.message);
|
||||||
|
} finally {
|
||||||
|
setIsSelecting(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
coverOptions,
|
||||||
|
isSelecting,
|
||||||
|
selectedOptionId,
|
||||||
|
selectedProject?.cover,
|
||||||
|
updateProjectData,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onValidatePicture = useCallback(async () => {
|
||||||
|
if (!selectedOption) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await setIsLoading(true);
|
||||||
|
const existingCover = selectedProject?.cover || {};
|
||||||
|
const finalUrl =
|
||||||
|
selectedOption.finalUrl || selectedOption.generatedUrl || null;
|
||||||
|
const nextCoverData = {
|
||||||
|
...existingCover,
|
||||||
|
options: coverOptions,
|
||||||
|
selectedOptionId: selectedOption.id,
|
||||||
|
result: finalUrl,
|
||||||
|
generatedBackground:
|
||||||
|
selectedOption.generatedUrl || selectedOption.finalUrl || null,
|
||||||
|
};
|
||||||
|
await updateProjectData({
|
||||||
|
cover: nextCoverData,
|
||||||
|
coverUrl: finalUrl,
|
||||||
|
});
|
||||||
|
const projectForStage = {
|
||||||
|
...selectedProject,
|
||||||
|
cover: nextCoverData,
|
||||||
|
coverUrl: finalUrl,
|
||||||
|
};
|
||||||
|
const playbackStage = getStageAction("director", projectForStage);
|
||||||
|
await setIsLoading(false);
|
||||||
|
alert(
|
||||||
|
"Malik",
|
||||||
|
"Super ! Ta pochette est validée. Tu peux retourner à l'accueil ou continuer avec John pour produire ton playback.",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: "Retour à l'accueil",
|
||||||
|
style: "cancel",
|
||||||
|
onPress: () => navigate(Routes.Home),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Continuer avec John",
|
||||||
|
onPress: () => {
|
||||||
|
const targetRoute = playbackStage?.route || Routes.Playback;
|
||||||
|
const params =
|
||||||
|
playbackStage?.params || { project: projectForStage };
|
||||||
|
navigate(targetRoute, params);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("PouchReady: unable to validate cover", e?.message);
|
||||||
|
} finally {
|
||||||
|
await setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
coverOptions,
|
||||||
|
navigate,
|
||||||
|
selectedOption,
|
||||||
|
selectedProject,
|
||||||
|
setIsLoading,
|
||||||
|
updateProjectData,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const primaryActionTitle = hasGeneratedOptions
|
||||||
|
? "Valider la pochette"
|
||||||
|
: isGenerating
|
||||||
|
? "Veuillez patienter..."
|
||||||
|
: "En attente de la génération";
|
||||||
|
|
||||||
|
const isPrimaryActionDisabled =
|
||||||
|
isGenerating ||
|
||||||
|
(hasGeneratedOptions ? !selectedOption || isSelecting : true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page backgroundImg={background.studioBG2} headerType="NONE">
|
<Page backgroundImg={background.studioBG2} headerType="NONE">
|
||||||
<MusicLandHeader onPressBack={goBack} progress={72} />
|
<MusicLandHeader onPressBack={goBack} progress={72} />
|
||||||
<View style={{ flex: 1, marginTop: 16 }}>
|
<View style={{ flex: 1, marginTop: 0 }}>
|
||||||
<CreateLyricsHeader
|
<CreateLyricsHeader
|
||||||
title={
|
title={
|
||||||
selectedProject?.cover?.backgroundGenerated
|
selectedProject?.cover?.backgroundGenerated
|
||||||
@@ -143,14 +306,55 @@ const PouchReady = () => {
|
|||||||
const optionUri =
|
const optionUri =
|
||||||
option?.finalUrl || option?.generatedUrl || "";
|
option?.finalUrl || option?.generatedUrl || "";
|
||||||
if (!optionUri) return null;
|
if (!optionUri) return null;
|
||||||
|
if (hasGeneratedOptions) {
|
||||||
|
const isSelected = option?.id === selectedOption?.id;
|
||||||
|
const cardWidthStyle = isWeb
|
||||||
|
? styles.coverOptionCardWeb
|
||||||
|
: styles.coverOptionCardMobile;
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
key={option?.id || index}
|
||||||
|
onPress={() => handleSelectOption(option)}
|
||||||
|
disabled={isSelecting}
|
||||||
|
style={[
|
||||||
|
styles.coverOptionCard,
|
||||||
|
cardWidthStyle,
|
||||||
|
isSelected ? styles.coverOptionCardSelected : null,
|
||||||
|
isSelecting ? styles.coverOptionCardDisabled : null,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<ExpoImage
|
||||||
|
source={{ uri: optionUri }}
|
||||||
|
cachePolicy="memory-disk"
|
||||||
|
priority="high"
|
||||||
|
contentFit="cover"
|
||||||
|
transition={120}
|
||||||
|
style={styles.coverOptionImage}
|
||||||
|
/>
|
||||||
|
<View style={styles.coverOptionBadge}>
|
||||||
|
<Text style={styles.coverOptionBadgeLabel}>
|
||||||
|
{`Option ${index + 1}`}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
{isSelected && (
|
||||||
|
<View style={styles.coverOptionSelectedBadge}>
|
||||||
|
<Text style={styles.coverOptionSelectedLabel}>
|
||||||
|
Sélectionnée
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</Pressable>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
key={option?.id || index}
|
key={option?.id || index}
|
||||||
style={{
|
style={[
|
||||||
width: isWeb ? 280 : "100%",
|
styles.coverPreviewCard,
|
||||||
alignItems: "center",
|
isWeb
|
||||||
gap: 8,
|
? styles.coverPreviewCardWeb
|
||||||
}}
|
: styles.coverPreviewCardMobile,
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<ExpoImage
|
<ExpoImage
|
||||||
source={{ uri: optionUri }}
|
source={{ uri: optionUri }}
|
||||||
@@ -158,38 +362,36 @@ const PouchReady = () => {
|
|||||||
priority="high"
|
priority="high"
|
||||||
contentFit="cover"
|
contentFit="cover"
|
||||||
transition={120}
|
transition={120}
|
||||||
style={{
|
style={styles.coverPreviewImage}
|
||||||
width: "100%",
|
|
||||||
height: 260,
|
|
||||||
borderRadius: 20,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
{hasGeneratedOptions && (
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
color: Palette.white,
|
|
||||||
opacity: 0.7,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Pochette {index + 1}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<View
|
// <View
|
||||||
style={{
|
// style={{
|
||||||
width: isWeb ? 300 : "100%",
|
// width: isWeb ? 300 : "100%",
|
||||||
height: 300,
|
// height: 300,
|
||||||
borderRadius: 20,
|
// borderRadius: 20,
|
||||||
backgroundColor: "#00000040",
|
// backgroundColor: "#00000040",
|
||||||
alignSelf: "center",
|
// alignSelf: "center",
|
||||||
...Style.containerCenter,
|
// ...Style.containerCenter,
|
||||||
}}
|
// }}
|
||||||
>
|
// >
|
||||||
|
<>
|
||||||
{isGenerating && (
|
{isGenerating && (
|
||||||
<View style={{ alignItems: "center", gap: 8 }}>
|
<View
|
||||||
|
style={{
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 8,
|
||||||
|
width: isWeb ? 300 : "100%",
|
||||||
|
height: 300,
|
||||||
|
borderRadius: 20,
|
||||||
|
backgroundColor: "#00000040",
|
||||||
|
alignSelf: "center",
|
||||||
|
...Style.containerCenter,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ActivityIndicator color={Palette.white} />
|
<ActivityIndicator color={Palette.white} />
|
||||||
{isWeb ? (
|
{isWeb ? (
|
||||||
coverBackgroundMessage ? (
|
coverBackgroundMessage ? (
|
||||||
@@ -218,18 +420,138 @@ const PouchReady = () => {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</>
|
||||||
|
// </View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<CustomInput
|
{shouldShowStylePanel && (
|
||||||
label="Style de la pochette"
|
<BlurView
|
||||||
placeholder="Exemple : Collage rétro futuriste lumineux"
|
tint="dark"
|
||||||
value={coverStyle}
|
intensity={isWeb ? 40 : 30}
|
||||||
setValue={setCoverStyle}
|
style={[styles.stylePanel, isWeb && styles.stylePanelWeb]}
|
||||||
multiline={false}
|
>
|
||||||
height={55}
|
<View style={styles.stylePanelHeader}>
|
||||||
maxLength={120}
|
<Text style={styles.panelTitle}>Style de la pochette</Text>
|
||||||
/>
|
<Text style={styles.panelSubtitle}>
|
||||||
|
Choisis une ambiance ou écris ton propre brief créatif.
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.modeContainer}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.modeCard,
|
||||||
|
styleMode === "preset" ? styles.modeCardActive : null,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<AppCheckbox
|
||||||
|
label="Choisir un style prédéfini"
|
||||||
|
selected={styleMode === "preset"}
|
||||||
|
onPress={() => {
|
||||||
|
setStyleMode("preset");
|
||||||
|
setIsPresetDropdownOpen(false);
|
||||||
|
if (!selectedPresetStyle) {
|
||||||
|
setSelectedPresetStyle(COVER_STYLE_PRESETS[0]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
{styleMode === "preset" && (
|
||||||
|
<View style={styles.dropdownArea}>
|
||||||
|
<Pressable
|
||||||
|
style={styles.dropdownTrigger}
|
||||||
|
onPress={() => setIsPresetDropdownOpen((prev) => !prev)}
|
||||||
|
>
|
||||||
|
<Text style={styles.dropdownTriggerLabel} numberOfLines={2}>
|
||||||
|
{selectedPresetStyle}
|
||||||
|
</Text>
|
||||||
|
<Image
|
||||||
|
source={icons.chevronDown}
|
||||||
|
style={[
|
||||||
|
styles.dropdownArrow,
|
||||||
|
isPresetDropdownOpen ? styles.dropdownArrowOpen : null,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Pressable>
|
||||||
|
{isPresetDropdownOpen && (
|
||||||
|
<View style={styles.dropdownList}>
|
||||||
|
{COVER_STYLE_PRESETS.map((styleOption, index) => {
|
||||||
|
const isActive = selectedPresetStyle === styleOption;
|
||||||
|
const isLast =
|
||||||
|
index === COVER_STYLE_PRESETS.length - 1;
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
key={styleOption}
|
||||||
|
style={[
|
||||||
|
styles.dropdownOption,
|
||||||
|
!isLast ? styles.dropdownOptionDivider : null,
|
||||||
|
isActive ? styles.dropdownOptionActive : null,
|
||||||
|
]}
|
||||||
|
onPress={() => {
|
||||||
|
setSelectedPresetStyle(styleOption);
|
||||||
|
setIsPresetDropdownOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.dropdownOptionLabel,
|
||||||
|
isActive
|
||||||
|
? styles.dropdownOptionLabelActive
|
||||||
|
: null,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{styleOption}
|
||||||
|
</Text>
|
||||||
|
{isActive && (
|
||||||
|
<Image
|
||||||
|
source={icons.check}
|
||||||
|
style={styles.dropdownOptionIcon}
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Pressable>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<View style={styles.modeColumn}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.modeCard,
|
||||||
|
styleMode === "custom" ? styles.modeCardActive : null,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<AppCheckbox
|
||||||
|
label="Style personnalisé"
|
||||||
|
selected={styleMode === "custom"}
|
||||||
|
onPress={() => {
|
||||||
|
setStyleMode("custom");
|
||||||
|
setIsPresetDropdownOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
{styleMode === "custom" && (
|
||||||
|
<View style={styles.customInputWrapper}>
|
||||||
|
<TextInput
|
||||||
|
placeholder="Exemple : Collage rétro futuriste lumineux"
|
||||||
|
placeholderTextColor={Palette.grayMid}
|
||||||
|
value={customStyle}
|
||||||
|
onChangeText={setCustomStyle}
|
||||||
|
style={styles.customInput}
|
||||||
|
multiline={false}
|
||||||
|
maxLength={120}
|
||||||
|
textAlignVertical="center"
|
||||||
|
autoCapitalize="sentences"
|
||||||
|
autoCorrect
|
||||||
|
selectionColor={Palette.primary}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</BlurView>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -252,9 +574,9 @@ const PouchReady = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<GradientButton
|
<GradientButton
|
||||||
title={isGenerating ? "Veuillez patienter..." : "Valider"}
|
title={primaryActionTitle}
|
||||||
disabled={isGenerating || !coverPreviewUrl}
|
disabled={isPrimaryActionDisabled}
|
||||||
onPress={() => validateCover()}
|
onPress={onValidatePicture}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</Page>
|
</Page>
|
||||||
@@ -262,3 +584,210 @@ const PouchReady = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default PouchReady;
|
export default PouchReady;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
coverOptionCard: {
|
||||||
|
position: "relative",
|
||||||
|
borderRadius: 20,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "rgba(255,255,255,0.12)",
|
||||||
|
overflow: "hidden",
|
||||||
|
backgroundColor: "rgba(15,12,20,0.4)",
|
||||||
|
},
|
||||||
|
coverOptionCardWeb: {
|
||||||
|
width: 280,
|
||||||
|
maxWidth: 320,
|
||||||
|
},
|
||||||
|
coverOptionCardMobile: {
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
coverOptionCardSelected: {
|
||||||
|
borderColor: Palette.primary,
|
||||||
|
},
|
||||||
|
coverOptionCardDisabled: {
|
||||||
|
opacity: 0.85,
|
||||||
|
},
|
||||||
|
coverOptionImage: {
|
||||||
|
width: "100%",
|
||||||
|
aspectRatio: 1,
|
||||||
|
},
|
||||||
|
coverOptionBadge: {
|
||||||
|
position: "absolute",
|
||||||
|
top: 12,
|
||||||
|
right: 12,
|
||||||
|
backgroundColor: Palette.transparentBlack,
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
paddingVertical: 6,
|
||||||
|
borderRadius: 12,
|
||||||
|
},
|
||||||
|
coverOptionBadgeLabel: {
|
||||||
|
color: Palette.white,
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||||
|
},
|
||||||
|
coverOptionSelectedBadge: {
|
||||||
|
position: "absolute",
|
||||||
|
bottom: 12,
|
||||||
|
left: 12,
|
||||||
|
backgroundColor: Palette.primary,
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
paddingVertical: 6,
|
||||||
|
borderRadius: 12,
|
||||||
|
},
|
||||||
|
coverOptionSelectedLabel: {
|
||||||
|
color: Palette.white,
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||||
|
},
|
||||||
|
coverPreviewCard: {
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
coverPreviewCardWeb: {
|
||||||
|
width: 300,
|
||||||
|
},
|
||||||
|
coverPreviewCardMobile: {
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
coverPreviewImage: {
|
||||||
|
width: "100%",
|
||||||
|
height: 260,
|
||||||
|
borderRadius: 20,
|
||||||
|
},
|
||||||
|
stylePanel: {
|
||||||
|
gap: 20,
|
||||||
|
paddingHorizontal: 18,
|
||||||
|
paddingVertical: 20,
|
||||||
|
borderRadius: 24,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "rgba(255,255,255,0.08)",
|
||||||
|
backgroundColor: "rgba(15, 12, 20, 0.78)",
|
||||||
|
overflow: "hidden",
|
||||||
|
},
|
||||||
|
stylePanelWeb: {
|
||||||
|
paddingHorizontal: 26,
|
||||||
|
},
|
||||||
|
stylePanelHeader: {
|
||||||
|
gap: 6,
|
||||||
|
},
|
||||||
|
panelTitle: {
|
||||||
|
fontSize: 18,
|
||||||
|
color: Palette.white,
|
||||||
|
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||||
|
},
|
||||||
|
panelSubtitle: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: Palette.gray,
|
||||||
|
fontFamily: FONT_FAMILY.InterRegular,
|
||||||
|
lineHeight: 18,
|
||||||
|
},
|
||||||
|
modeContainer: {
|
||||||
|
gap: 18,
|
||||||
|
},
|
||||||
|
modeCard: {
|
||||||
|
alignSelf: "stretch",
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 12,
|
||||||
|
borderRadius: 14,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "rgba(255,255,255,0.1)",
|
||||||
|
backgroundColor: "rgba(15,12,20,0.6)",
|
||||||
|
},
|
||||||
|
modeCardActive: {
|
||||||
|
borderColor: Palette.primary,
|
||||||
|
backgroundColor: "rgba(251,104,168,0.12)",
|
||||||
|
},
|
||||||
|
dropdownArea: {
|
||||||
|
alignSelf: "stretch",
|
||||||
|
gap: 12,
|
||||||
|
},
|
||||||
|
dropdownTrigger: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 14,
|
||||||
|
borderRadius: 16,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "rgba(255,255,255,0.1)",
|
||||||
|
backgroundColor: "rgba(15,12,20,0.65)",
|
||||||
|
},
|
||||||
|
dropdownTriggerLabel: {
|
||||||
|
flex: 1,
|
||||||
|
fontSize: 15,
|
||||||
|
color: Palette.white,
|
||||||
|
fontFamily: FONT_FAMILY.InterMedium,
|
||||||
|
},
|
||||||
|
dropdownArrow: {
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
tintColor: Palette.gray,
|
||||||
|
marginLeft: 10,
|
||||||
|
},
|
||||||
|
dropdownArrowOpen: {
|
||||||
|
transform: [
|
||||||
|
{
|
||||||
|
rotate: "180deg",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
dropdownList: {
|
||||||
|
borderRadius: 16,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "rgba(255,255,255,0.08)",
|
||||||
|
backgroundColor: "rgba(12,10,18,0.95)",
|
||||||
|
overflow: "hidden",
|
||||||
|
},
|
||||||
|
dropdownOption: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 14,
|
||||||
|
},
|
||||||
|
dropdownOptionDivider: {
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: "rgba(255,255,255,0.06)",
|
||||||
|
},
|
||||||
|
dropdownOptionActive: {
|
||||||
|
backgroundColor: "rgba(251,104,168,0.12)",
|
||||||
|
},
|
||||||
|
dropdownOptionLabel: {
|
||||||
|
flex: 1,
|
||||||
|
fontSize: 15,
|
||||||
|
color: Palette.white,
|
||||||
|
fontFamily: FONT_FAMILY.InterRegular,
|
||||||
|
},
|
||||||
|
dropdownOptionLabelActive: {
|
||||||
|
color: Palette.primary,
|
||||||
|
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||||
|
},
|
||||||
|
dropdownOptionIcon: {
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
tintColor: Palette.primary,
|
||||||
|
marginLeft: 12,
|
||||||
|
},
|
||||||
|
modeColumn: {
|
||||||
|
flex: 1,
|
||||||
|
gap: 12,
|
||||||
|
minWidth: 240,
|
||||||
|
},
|
||||||
|
customInputWrapper: {
|
||||||
|
alignSelf: "stretch",
|
||||||
|
borderRadius: 18,
|
||||||
|
padding: 1,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "rgba(255,255,255,0.1)",
|
||||||
|
backgroundColor: "rgba(15,12,20,0.4)",
|
||||||
|
},
|
||||||
|
customInput: {
|
||||||
|
minHeight: 55,
|
||||||
|
borderRadius: 16,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 12,
|
||||||
|
fontSize: 15,
|
||||||
|
color: Palette.white,
|
||||||
|
backgroundColor: "rgba(15,12,20,0.85)",
|
||||||
|
fontFamily: FONT_FAMILY.InterRegular,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user