This commit is contained in:
Philip Cesar Garay
2025-08-01 21:05:44 +08:00
parent 84fc719a10
commit e22b87bd49
18 changed files with 500 additions and 14 deletions
+2
View File
@@ -27,6 +27,7 @@ import Providers from "./src/providers";
import AppLayout from "./src/layouts/AppLayout"; import AppLayout from "./src/layouts/AppLayout";
import { import {
Inter_400Regular, Inter_400Regular,
Inter_400Regular_Italic,
Inter_500Medium, Inter_500Medium,
Inter_600SemiBold, Inter_600SemiBold,
Inter_700Bold, Inter_700Bold,
@@ -56,6 +57,7 @@ const App = () => {
InterMedium: Inter_500Medium, InterMedium: Inter_500Medium,
InterSemiBold: Inter_600SemiBold, InterSemiBold: Inter_600SemiBold,
InterBold: Inter_700Bold, InterBold: Inter_700Bold,
InterRegularItalic: Inter_400Regular_Italic,
}); });
useEffect(() => { useEffect(() => {
+1
View File
@@ -81,6 +81,7 @@
"react-native-store-review": "^0.3.0", "react-native-store-review": "^0.3.0",
"react-native-super-grid": "^5.0.0", "react-native-super-grid": "^5.0.0",
"react-native-svg": "15.8.0", "react-native-svg": "15.8.0",
"react-native-swiper-flatlist": "^3.2.5",
"react-native-web": "~0.19.6", "react-native-web": "~0.19.6",
"react-native-web-linear-gradient": "^1.1.2", "react-native-web-linear-gradient": "^1.1.2",
"react-native-webview": "13.12.5", "react-native-webview": "13.12.5",
@@ -2,19 +2,31 @@ import { StyleSheet, View } from "react-native";
import { GradientBorderView } from "../GradientBorderView"; import { GradientBorderView } from "../GradientBorderView";
const BorderGradient = ({ children, gradientProps, ...props }) => { const BorderGradient = ({
children,
contentContainerStyle,
gradientProps,
...props
}) => {
return ( return (
<GradientBorderView <GradientBorderView
gradientProps={{ gradientProps={{
start: { x: 0, y: 0 }, start: { x: 0, y: 0 },
end: { x: 1, y: 0 }, end: { x: 1, y: 0 },
...gradientProps ...gradientProps,
}} }}
{...props}> {...props}
>
<View style={styles.innerContainer}>{children}</View> <View
</GradientBorderView>); style={{
...styles.innerContainer,
...contentContainerStyle,
}}
>
{children}
</View>
</GradientBorderView>
);
}; };
export default BorderGradient; export default BorderGradient;
@@ -22,5 +34,5 @@ export default BorderGradient;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
innerContainer: { innerContainer: {
flex: 1, flex: 1,
} },
}); });
+7 -2
View File
@@ -5,7 +5,12 @@ import { Palette, Style } from "../styles";
import ProgressBar from "./ProgressBar"; import ProgressBar from "./ProgressBar";
import { FONT_FAMILY } from "../styles/Fonts"; import { FONT_FAMILY } from "../styles/Fonts";
const MusicLandHeader = ({ onPressBack, onPressSkip, showSkip = false }) => { const MusicLandHeader = ({
onPressBack,
onPressSkip,
showSkip = false,
progress = 1,
}) => {
return ( return (
<View style={{ alignItems: "center", gap: 16 }}> <View style={{ alignItems: "center", gap: 16 }}>
<Image source={icons.musicLandLogo} /> <Image source={icons.musicLandLogo} />
@@ -21,7 +26,7 @@ const MusicLandHeader = ({ onPressBack, onPressSkip, showSkip = false }) => {
/> />
</Pressable> </Pressable>
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<ProgressBar progress={10} /> <ProgressBar progress={progress} />
</View> </View>
{showSkip && ( {showSkip && (
<Pressable onPress={onPressSkip}> <Pressable onPress={onPressSkip}>
+9
View File
@@ -0,0 +1,9 @@
export const GOALS = [
"Envoyer un message universel",
"Souhaiter un joyeux anniversaire à un ami",
"Déclaration d'amour",
"Pour mon club sportif",
"Pour un mariage",
"Pour mon entreprise",
"Envoyer un message politique",
];
+6
View File
@@ -17,6 +17,7 @@ import TermsOfUse from "../screens/TermsOfUse";
import PrivacyPolicy from "../screens/PrivacyPolicy"; import PrivacyPolicy from "../screens/PrivacyPolicy";
import Writing from "../screens/Writing/Writing"; import Writing from "../screens/Writing/Writing";
import WritingLyrics from "../screens/Writing/WritingLyrics"; import WritingLyrics from "../screens/Writing/WritingLyrics";
import CreateLyricsWithAi from "../screens/Writing/CreateLyricsWithAi";
const screenOptions = { const screenOptions = {
headerShown: false, headerShown: false,
@@ -68,6 +69,11 @@ const screens = [
name: Routes.WritingLyrics, name: Routes.WritingLyrics,
component: WritingLyrics, component: WritingLyrics,
}, },
{
name: Routes.CreateLyricsWithAi,
component: CreateLyricsWithAi,
},
]; ];
export default function Main() { export default function Main() {
+1
View File
@@ -25,4 +25,5 @@ export const Routes = {
Writing: "Writing", Writing: "Writing",
WritingLyrics: "WritingLyrics", WritingLyrics: "WritingLyrics",
CreateLyricsWithAi: "CreateLyricsWithAi",
}; };
+1 -1
View File
@@ -51,7 +51,7 @@ export default ({ navigation }) => {
index: 0, index: 0,
routes: [ routes: [
{ {
name: isDesktop || isWeb ? Routes.Login : Routes.Onboarding, name: Routes.Onboarding,
}, },
], ],
}); });
+88
View File
@@ -0,0 +1,88 @@
import { View, Text, ScrollView, Dimensions } from "react-native";
import React, { useRef, useState } from "react";
import Page from "../../layouts/Page";
import MusicLandHeader from "../../components/MusicLandHeader";
import CreateLyricsHeader from "./components/CreateLyricsHeader";
import GradientButton from "../../components/GradientButton";
import { gutters } from "../../styles";
import { SwiperFlatList } from "react-native-swiper-flatlist";
import Goals from "./Goals";
import { goBack } from "../../navigation/NavigationService";
import SpecificityContext from "./SpecificityContext";
import EmotionConvey from "./EmotionConvey";
const { width } = Dimensions.get("window");
const CreateLyricsWithAi = () => {
const scrollRef = useRef(null);
const [selectedIndex, setSelectedIndex] = useState(0);
const [progress, setProgress] = useState(8);
const onPressNext = () => {
setSelectedIndex(selectedIndex + 1);
setProgress(progress + 8);
scrollRef.current.scrollToIndex({
index: selectedIndex + 1,
animated: true,
});
};
const onPressBack = () => {
if (selectedIndex > 0) {
setSelectedIndex(selectedIndex - 1);
setProgress(progress - 8);
scrollRef.current.scrollToIndex({
index: selectedIndex - 1,
animated: true,
});
} else {
goBack();
}
};
return (
<Page headerType="NONE">
<MusicLandHeader onPressBack={onPressBack} progress={progress} />
<View style={{ flex: 1, marginTop: 16, paddingBottom: gutters, gap: 48 }}>
<View style={{ flex: 1 }}>
<SwiperFlatList
style={{ width, left: -gutters }}
ref={scrollRef}
disableGesture
>
<View
style={{
width: width,
height: "100%",
paddingHorizontal: gutters,
}}
>
<Goals />
</View>
<View
style={{
width: width,
height: "100%",
paddingHorizontal: gutters,
}}
>
<SpecificityContext />
</View>
<View
style={{
width: width,
height: "100%",
paddingHorizontal: gutters,
}}
>
<EmotionConvey />
</View>
</SwiperFlatList>
</View>
<GradientButton title="Suivant" onPress={onPressNext} />
</View>
</Page>
);
};
export default CreateLyricsWithAi;
+104
View File
@@ -0,0 +1,104 @@
import { View, Text, StyleSheet, FlatList } from "react-native";
import React, { useState } from "react";
import CreateLyricsHeader from "./components/CreateLyricsHeader";
import BorderGradient from "../../components/BorderGradient/BorderGradient";
import { responsiveHeight } from "react-native-responsive-dimensions";
import { BlurView } from "expo-blur";
import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
const EmotionConvey = () => {
const [itemLayout, setItemLayout] = useState([]);
console.log("itemLayout: ", itemLayout);
return (
<View style={{ flex: 1, gap: 10 }}>
<CreateLyricsHeader
title={`Quel émotion veux-tu\ntransmettre ?`}
subTitle="Sélectionne tes intentions émotionnelles. (2 max)"
/>
<BorderGradient
gradientProps={{
colors: ["#FFFFFF00", "#FFFFFF"],
}}
style={styles.borderGradientStyle}
>
<View style={styles.blurContainer}>
<BlurView intensity={40} style={{ flex: 1, padding: 6 }}>
<FlatList
data={Array.from({ length: 3 })}
contentContainerStyle={{ gap: 16 }}
renderItem={({ item, index }) => (
<View>
<BorderGradient
gradientProps={{
colors: ["#FBFF00", "#99999900"],
}}
style={{
borderWidth: 1,
height: itemLayout[index]?.height,
borderRadius: 14,
position: "absolute",
width: "100%",
}}
/>
<View
style={{ borderRadius: 14, overflow: "hidden" }}
onLayout={(e) => {
e.persist();
setItemLayout((prev) => [...prev, e.nativeEvent.layout]);
}}
>
<BlurView
intensity={30}
style={{ paddingVertical: 14, paddingHorizontal: 12 }}
>
<Text
style={{
fontSize: 16,
color: Palette.white,
fontFamily: FONT_FAMILY.InterRegular,
}}
>
<Text style={{ fontFamily: FONT_FAMILY.InterBold }}>
La Joie
</Text>{" "}
: expose un bonheur profond, l'émerveillement, la
gratitude, satisfaction intense, énergie positive.
</Text>
</BlurView>
</View>
</View>
)}
/>
</BlurView>
</View>
</BorderGradient>
</View>
);
};
export default EmotionConvey;
const styles = StyleSheet.create({
borderGradientStyle: {
borderWidth: 1,
borderRadius: 20,
height: responsiveHeight(55),
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 100,
},
blurContainer: {
flex: 1,
borderRadius: 20,
overflow: "hidden",
zIndex: 1,
},
});
+105
View File
@@ -0,0 +1,105 @@
import { View, Text, FlatList, StyleSheet, Pressable } from "react-native";
import React from "react";
import CreateLyricsHeader from "./components/CreateLyricsHeader";
import BorderGradient from "../../components/BorderGradient/BorderGradient";
import { Palette, Style } from "../../styles";
import { BlurView } from "expo-blur";
import { GOALS } from "../../data/data";
import { FONT_FAMILY } from "../../styles/Fonts";
import CustomInput from "./components/CustomInput";
import { responsiveHeight } from "react-native-responsive-dimensions";
const Goals = () => {
return (
<View style={{ flex: 1, gap: 16 }}>
<View style={{ gap: 10 }}>
<CreateLyricsHeader title="Quels sont tes objectifs ?" />
<BorderGradient
gradientProps={{
colors: ["#FFFFFF00", "#FFFFFF"],
}}
style={styles.borderGradientStyle}
>
<View style={styles.blurContainer}>
<BlurView intensity={40} style={{ flex: 1, padding: 6 }}>
<FlatList
data={GOALS}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.contentContainer}
renderItem={({ item }) => (
<Pressable style={styles.itemContainer}>
<BlurView
intensity={30}
style={{
flex: 1,
backgroundColor: Palette.glass,
...Style.containerCenter,
}}
>
<Text style={styles.itemText}>{item}</Text>
</BlurView>
</Pressable>
)}
/>
</BlurView>
</View>
</BorderGradient>
</View>
<CustomInput
label="Tu as un autre objectif?"
placeholder="Décrire lobjectif"
/>
</View>
);
};
export default Goals;
const styles = StyleSheet.create({
borderGradientStyle: {
borderWidth: 1,
borderRadius: 20,
height: responsiveHeight(40),
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 100,
},
blurContainer: {
flex: 1,
borderRadius: 20,
overflow: "hidden",
zIndex: 1,
},
contentContainer: {
flexGrow: 1,
padding: 8,
gap: 10,
backgroundColor: "#FFFFFF00",
paddingBottom: 100,
},
itemContainer: {
height: 54,
backgroundColor: Palette.glass,
borderRadius: 14,
overflow: "hidden",
shadowColor: "#00000040",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
},
itemText: {
fontSize: 16,
color: Palette.white,
fontFamily: FONT_FAMILY.InterRegular,
},
});
+19
View File
@@ -0,0 +1,19 @@
import { View, Text } from "react-native";
import React from "react";
import CreateLyricsHeader from "./components/CreateLyricsHeader";
import CustomInput from "./components/CustomInput";
import { gutters } from "../../styles";
const SpecificityContext = () => {
return (
<View style={{ flex: 1, gap: 10 }}>
<CreateLyricsHeader
title="Spécificité du contexte"
subTitle="Dis-nous en un peu plus pour quon puisse mieux taider."
/>
<CustomInput placeholder="Ecrire mon contexte" height={283} />
</View>
);
};
export default SpecificityContext;
+6 -2
View File
@@ -6,7 +6,8 @@ import GradientButton from "../../components/GradientButton";
import { gutters } from "../../styles"; import { gutters } from "../../styles";
import BorderGradientButton from "../../components/BorderGradientButton"; import BorderGradientButton from "../../components/BorderGradientButton";
import MusicLandHeader from "../../components/MusicLandHeader"; import MusicLandHeader from "../../components/MusicLandHeader";
import { goBack } from "../../navigation/NavigationService"; import { goBack, navigate } from "../../navigation/NavigationService";
import { Routes } from "../../navigation";
const WritingLyrics = () => { const WritingLyrics = () => {
return ( return (
@@ -24,7 +25,10 @@ const WritingLyrics = () => {
}} }}
> >
<BorderGradientButton /> <BorderGradientButton />
<GradientButton title="Écrire des paroles avec une IA" /> <GradientButton
title="Écrire des paroles avec une IA"
onPress={() => navigate(Routes.CreateLyricsWithAi)}
/>
</View> </View>
</View> </View>
</Page> </Page>
@@ -0,0 +1,69 @@
import { View, Text } from "react-native";
import React, { useState } from "react";
import { BlurView } from "expo-blur";
import BorderGradient from "../../../components/BorderGradient/BorderGradient";
import { Palette } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
const CreateLyricsHeader = ({ title = "", subTitle = "", height = 45 }) => {
const [onLayout, setOnLayout] = useState(null);
return (
<>
<BorderGradient
gradientProps={{
colors: ["#FFFFFF00", "#FFFFFF"],
}}
style={{
height: onLayout?.height,
borderWidth: 1,
borderRadius: 18,
position: "absolute",
width: "100%",
}}
/>
<View
style={{
backgroundColor: Palette.glass,
borderRadius: 18,
overflow: "hidden",
}}
onLayout={(event) => {
setOnLayout(event.nativeEvent.layout);
}}
>
<BlurView
intensity={20}
style={{
paddingHorizontal: 12,
paddingVertical: 8,
gap: 4,
}}
>
<Text
style={{
fontSize: 22,
color: Palette.white,
fontFamily: FONT_FAMILY.InterSemiBold,
}}
>
{title}
</Text>
{subTitle && (
<Text
style={{
fontSize: 12,
color: Palette.white,
fontFamily: FONT_FAMILY.InterRegular,
}}
>
{subTitle}
</Text>
)}
</BlurView>
</View>
</>
);
};
export default CreateLyricsHeader;
@@ -0,0 +1,52 @@
import { View, Text, TextInput } from "react-native";
import React from "react";
import { Palette } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
const CustomInput = ({
label = "",
placeholder = "",
value,
setValue,
height = 65,
}) => {
return (
<View style={{ gap: 8 }}>
{label && (
<Text
style={{
fontSize: 16,
color: Palette.white,
fontFamily: FONT_FAMILY.InterMedium,
}}
>
{label}
</Text>
)}
<View
style={{
backgroundColor: Palette.white,
height,
padding: 12,
borderRadius: 12,
}}
>
<TextInput
placeholder={placeholder}
placeholderTextColor={Palette.grayMid}
value={value}
onChangeText={setValue}
style={{
height: "100%",
fontSize: 16,
color: Palette.black,
fontFamily: FONT_FAMILY.InterRegularItalic,
}}
textAlignVertical="top"
/>
</View>
</View>
);
};
export default CustomInput;
+1
View File
@@ -35,6 +35,7 @@ export const FONT_FAMILY = {
InterMedium: "InterMedium", InterMedium: "InterMedium",
InterSemiBold: "InterSemiBold", InterSemiBold: "InterSemiBold",
InterBold: "InterBold", InterBold: "InterBold",
InterRegularItalic: "InterRegularItalic",
}; };
const Fonts = ({ const Fonts = ({
+3
View File
@@ -32,6 +32,9 @@ const Palette = {
ultraLightBlack: "rgba(0, 0, 0, 0.6)", ultraLightBlack: "rgba(0, 0, 0, 0.6)",
tran: "transparent", tran: "transparent",
glass: "#73737324",
grayMid: "#8C8C8C",
}; };
export default Palette; export default Palette;
+5
View File
@@ -7453,6 +7453,11 @@ react-native-swipe-gestures@^1.0.5:
resolved "https://registry.yarnpkg.com/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.5.tgz#a172cb0f3e7478ccd681fd36b8bfbcdd098bde7c" resolved "https://registry.yarnpkg.com/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.5.tgz#a172cb0f3e7478ccd681fd36b8bfbcdd098bde7c"
integrity sha512-Ns7Bn9H/Tyw278+5SQx9oAblDZ7JixyzeOczcBK8dipQk2pD7Djkcfnf1nB/8RErAmMLL9iXgW0QHqiII8AhKw== integrity sha512-Ns7Bn9H/Tyw278+5SQx9oAblDZ7JixyzeOczcBK8dipQk2pD7Djkcfnf1nB/8RErAmMLL9iXgW0QHqiII8AhKw==
react-native-swiper-flatlist@^3.2.5:
version "3.2.5"
resolved "https://registry.yarnpkg.com/react-native-swiper-flatlist/-/react-native-swiper-flatlist-3.2.5.tgz#d69030e2986b8a322ff274ad2896ca809c1ebe0e"
integrity sha512-pP6Yioo//PgxBf1Ac/utPaAMaCTXF0QW/Q55hQofGtE7B5/hL0nb9u4cXEdgGJslx0oBCkir4AJAbbD5FpWnaA==
react-native-web-linear-gradient@^1.1.2: react-native-web-linear-gradient@^1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/react-native-web-linear-gradient/-/react-native-web-linear-gradient-1.1.2.tgz#33f85f7085a0bb5ffa5106faf02ed105b92a9ed7" resolved "https://registry.yarnpkg.com/react-native-web-linear-gradient/-/react-native-web-linear-gradient-1.1.2.tgz#33f85f7085a0bb5ffa5106faf02ed105b92a9ed7"