modal transition

This commit is contained in:
2025-10-23 16:07:52 +02:00
parent bd26a2fb29
commit 7501b818c6
11 changed files with 227 additions and 162 deletions
+65
View File
@@ -0,0 +1,65 @@
import { Image } from "expo-image";
import { memo } from "react";
import { Platform, StyleSheet, View } from "react-native";
import { Palette } from "../styles";
const DEFAULT_INTENSITY = Platform.select({ ios: 22, default: 14 });
const ProfilePicture = ({
uri = null,
size = 48,
style,
blurIntensity = DEFAULT_INTENSITY,
blurTint = "dark",
imageProps = {},
transitionDuration = 120,
cachePolicy = "memory-disk",
contentFit = "cover",
accessibilityLabel,
...rest
}) => {
const dimensionStyle = {
width: size,
height: size,
borderRadius: size / 2,
};
return (
<View
accessible
accessibilityRole="image"
accessibilityLabel={accessibilityLabel}
style={[styles.container, dimensionStyle, style]}
{...rest}
>
{uri ? (
<Image
source={{ uri: uri }}
contentFit={contentFit}
cachePolicy={cachePolicy}
transition={transitionDuration}
style={StyleSheet.absoluteFill}
{...imageProps}
/>
) : (
<>
<View style={[styles.fallback, dimensionStyle]} />
</>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
overflow: "hidden",
backgroundColor: "transparent",
},
fallback: {
backgroundColor: Palette.glass,
borderColor: Palette.ultraLightWhite,
borderWidth: 2,
},
});
export default memo(ProfilePicture);
@@ -6,7 +6,6 @@ import {
BottomSheetTextInput,
} from '@gorhom/bottom-sheet';
import { BlurView } from 'expo-blur';
import { Image as ExpoImage } from 'expo-image';
import React, {
useCallback,
useEffect,
@@ -30,7 +29,7 @@ import { useUser } from '../../providers/UserDataProvider';
import { getArtistDisplayName } from '../../utils/artistName';
import { Palette } from '../../styles';
import { FONT_FAMILY } from '../../styles/Fonts';
import { size } from '../../styles/Style';
import ProfilePicture from '../ProfilePicture';
let externalOpen;
let externalClose;
@@ -273,24 +272,7 @@ const CommentsBottomSheet = () => {
<View
key={c.id}
style={{ flexDirection: 'row', gap: 12, marginBottom: 14 }}>
{c?.profilePicture ? (
<ExpoImage
source={{ uri: c.profilePicture }}
cachePolicy='memory-disk'
priority='high'
contentFit='cover'
transition={100}
style={{ ...size({ size: 42 }), borderRadius: 100 }}
/>
) : (
<View
style={{
...size({ size: 42 }),
borderRadius: 100,
backgroundColor: '#FFFFFF22',
}}
/>
)}
<ProfilePicture uri={c?.profilePicture || null} size={42} />
<View style={{ flex: 1 }}>
<View
style={{
+34 -4
View File
@@ -21,6 +21,7 @@ import useDataFromRef from "../../hooks/useDataFromRef";
import { isWeb } from "../../hooks/useLayoutType.js";
import Page from "../../layouts/Page";
import { navigate } from "../../navigation/NavigationService";
import { Routes } from "../../navigation/Routes";
import { useUser } from "../../providers/UserDataProvider";
import { Palette } from "../../styles";
import {
@@ -217,14 +218,43 @@ const Home = ({ navigation, route }) => {
}, [handleDeleteProject, menuProject?.id]);
useEffect(() => {
if (route?.params?.showLyricsCongrats) {
if (!route?.params?.showLyricsCongrats) {
return;
}
const beatmakerStage = currentProject
? getStageAction("beatmaker", currentProject)
: null;
Alert(
"Céline",
"Bravo ! Vous venez de terminer de créer les paroles de votre musique ! Maintenant vous pouvez passer à la prochaine étape : le Studio avec Malik. Dans cette étape vous allez pouvoir donner vie à votre chanson !"
"Bravo ! Vous venez de terminer de créer les paroles de votre musique ! Maintenant vous pouvez passer à la prochaine étape : le Studio avec Malik. Dans cette étape vous allez pouvoir donner vie à votre chanson !",
[
{
text: "Retour à l'accueil",
style: "cancel",
},
{
text: "Continuer avec Malik",
onPress: () => {
if (!currentProject?.id) {
return;
}
if (selectedProject?.id !== currentProject.id) {
selectProject(currentProject.id);
}
const targetRoute = beatmakerStage?.route || Routes.Compose;
navigate(targetRoute, beatmakerStage?.params);
},
},
]
);
navigation?.setParams?.({ showLyricsCongrats: false });
}
}, [navigation, route?.params?.showLyricsCongrats]);
}, [
currentProject,
navigation,
selectProject,
selectedProject?.id,
route?.params?.showLyricsCongrats,
]);
const activeStage =
stageStates[activeStageIndex] || stageStates[preferredStageIndex];
@@ -1,14 +1,12 @@
import { Image as ExpoImage } from "expo-image";
import React, { useState } from "react";
import { Pressable, ScrollView, Text, View } from "react-native";
import { img } from "../../../assets";
import MoreMenu from "../../../components/MoreMenu";
import ProfilePicture from "../../../components/ProfilePicture";
import useNavigateToMusicDetails from "../../../hooks/useNavigateToMusicDetails";
import { Routes } from "../../../navigation";
import { navigate } from "../../../navigation/NavigationService";
import { Palette, Style } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
import { size } from "../../../styles/Style";
import { getArtistDisplayName } from "../../../utils/artistName";
import CreateLyricsHeader from "../../Writing/components/CreateLyricsHeader";
import MusicCard from "./MusicCard";
@@ -190,27 +188,15 @@ const SearchResultsList = ({
key={user.id}
onPress={() => handleProfilePress(user.id)}
>
<ExpoImage
source={
<ProfilePicture
uri={
user?.pictureUrl ||
user?.profilePictureURL ||
user?.photoURL
? {
uri:
user?.pictureUrl ||
user?.profilePictureURL ||
user?.photoURL,
user?.photoURL ||
null
}
: img.profile
}
cachePolicy="memory-disk"
priority="high"
contentFit="cover"
transition={100}
style={{
...size({ size: 60 }),
borderRadius: 100,
}}
size={60}
imageProps={{ priority: "high" }}
/>
<Text
style={{
+5 -16
View File
@@ -14,6 +14,7 @@ import { responsiveHeight } from "react-native-responsive-dimensions";
import { icons, img } from "../../assets";
import { openComments } from "../../components/bottomsheets/CommentsBottomSheet";
import KaraokeLyrics from "../../components/KaraokeLyrics";
import ProfilePicture from "../../components/ProfilePicture";
import {
arrayRemove,
arrayUnion,
@@ -213,23 +214,11 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
navigate(Routes.SingerProfile, { userId: item?.userId });
}}
>
{owner?.profilePictureURL ? (
<Image
source={{ uri: owner.profilePictureURL }}
style={{
...size({ size: 45 }),
borderRadius: 100,
}}
<ProfilePicture
uri={owner?.profilePictureURL || null}
size={45}
imageProps={{ priority: "high" }}
/>
) : (
<Image
source={img.profile}
style={{
...size({ size: 45 }),
borderRadius: 100,
}}
/>
)}
</Pressable>
{owner?.id && currentUID && owner.id !== currentUID && (
<Pressable
@@ -1,5 +1,4 @@
import { BlurView } from "expo-blur";
import { Image as ExpoImage } from "expo-image";
import moment from "moment";
import "moment/locale/fr";
import React, {
@@ -30,7 +29,7 @@ import { useUser } from "../../../providers/UserDataProvider";
import { getArtistDisplayName } from "../../../utils/artistName";
import { Palette } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
import { size } from "../../../styles/Style";
import ProfilePicture from "../../../components/ProfilePicture";
moment.locale("fr");
@@ -198,18 +197,11 @@ const CommentsPanel = ({
index > 0 && styles.commentRowSpacing,
]}
>
{c?.profilePicture ? (
<ExpoImage
source={{ uri: c.profilePicture }}
cachePolicy="memory-disk"
priority="high"
contentFit="cover"
transition={100}
<ProfilePicture
uri={c?.profilePicture || null}
size={48}
style={styles.commentAvatar}
/>
) : (
<View style={styles.commentAvatarFallback} />
)}
<View style={styles.commentBubble}>
<View style={styles.commentHeader}>
<Text style={styles.commentAuthor}>
@@ -315,14 +307,6 @@ const styles = StyleSheet.create({
marginTop: 16,
},
commentAvatar: {
...size({ size: 48 }),
borderRadius: 100,
marginRight: 12,
},
commentAvatarFallback: {
...size({ size: 48 }),
borderRadius: 100,
backgroundColor: "#FFFFFF33",
marginRight: 12,
},
commentBubble: {
+6 -19
View File
@@ -1,12 +1,12 @@
import { BlurView } from "expo-blur";
import { Image as ExpoImage } from "expo-image";
import * as ImagePicker from "expo-image-picker";
import React, { useEffect, useState } from "react";
import { Platform, Pressable, Text, View } from "react-native";
import { responsiveHeight } from "react-native-responsive-dimensions";
import { useGlobal } from "reactn";
import { background, img } from "../../assets";
import { background } from "../../assets";
import GradientButton from "../../components/GradientButton";
import ProfilePicture from "../../components/ProfilePicture";
import firebase, { serverTimestamp, usersRef } from "../../config/firebase";
import { uploadFileToFirebase } from "../../helpers/uploadToFirebase";
import { isWeb } from "../../hooks/useLayoutType";
@@ -15,7 +15,6 @@ import { goBack } from "../../navigation/NavigationService";
import { useUser } from "../../providers/UserDataProvider";
import { gutters, Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import { size } from "../../styles/Style";
import EditInput from "./components/EditInput";
const EditProfile = () => {
@@ -147,22 +146,10 @@ const EditProfile = () => {
// }
>
<Pressable style={{ alignItems: "center" }} onPress={onChangePicture}>
<ExpoImage
source={
currentUserData?.profilePictureURL
? {
uri: currentUserData?.profilePictureURL,
}
: img.profile
}
cachePolicy="memory-disk"
priority="high"
contentFit="cover"
transition={100}
style={{
...size({ size: 108 }),
borderRadius: 100,
}}
<ProfilePicture
uri={currentUserData?.profilePictureURL || null}
size={108}
imageProps={{ priority: "high" }}
/>
<Text
style={{
+6 -14
View File
@@ -1,11 +1,9 @@
import React, { useMemo, useState } from "react";
import { Pressable, Text, View } from "react-native";
import { Image as ExpoImage } from "expo-image";
import Page from "../../layouts/Page";
import { background, img } from "../../assets";
import { background } from "../../assets";
import { Palette, Style } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import { size } from "../../styles/Style";
import { useUser } from "../../providers/UserDataProvider";
import { usersRef } from "../../config/firebase";
import useDataFromArrayId from "react-native-minuit/src/hooks/useDataFromArrayId";
@@ -14,6 +12,7 @@ import { Routes } from "../../navigation";
import { useRoute } from "@react-navigation/native";
import { navigate } from "../../navigation/NavigationService";
import { getArtistDisplayName } from "../../utils/artistName";
import ProfilePicture from "../../components/ProfilePicture";
const Follows = () => {
const { params } = useRoute();
@@ -92,17 +91,10 @@ const Follows = () => {
style={{ gap: 14, ...Style.containerRow }}
onPress={() => navigate(Routes.SingerProfile, { userId: user?.id })}
>
<ExpoImage
source={
user?.profilePictureURL
? { uri: user.profilePictureURL }
: img.profile
}
cachePolicy="memory-disk"
priority="high"
contentFit="cover"
transition={100}
style={{ ...size({ size: 60 }), borderRadius: 100 }}
<ProfilePicture
uri={user?.profilePictureURL || null}
size={60}
imageProps={{ priority: "high" }}
/>
<Text
style={{
+8 -22
View File
@@ -1,6 +1,5 @@
import { useRoute } from "@react-navigation/native";
import { BlurView } from "expo-blur";
import { Image as ExpoImage } from "expo-image";
import * as ImagePicker from "expo-image-picker";
import React, { useEffect, useMemo, useState } from "react";
import {
@@ -16,10 +15,11 @@ import {
import { SheetManager } from "react-native-actions-sheet";
import { responsiveHeight } from "react-native-responsive-dimensions";
import { useGlobal } from "reactn";
import { background, icons, img } from "../../assets";
import { background, icons } from "../../assets";
import BorderGradient from "../../components/BorderGradient/BorderGradient";
import MoreMenu from "../../components/MoreMenu";
import PressableScale from "../../components/PressableScale";
import ProfilePicture from "../../components/ProfilePicture";
import firebase, {
projectsRef,
serverTimestamp,
@@ -361,28 +361,14 @@ const Profile = () => {
<View style={{ alignItems: "center" }}>
<View style={styles.avatarWrapper}>
<ExpoImage
source={
(
<ProfilePicture
uri={
isSelf
? currentUserData?.profilePictureURL
: userData?.profilePictureURL
)
? {
uri: isSelf
? currentUserData?.profilePictureURL
: userData?.profilePictureURL,
? currentUserData?.profilePictureURL || null
: userData?.profilePictureURL || null
}
: img.profile
}
cachePolicy="memory-disk"
priority="high"
contentFit="cover"
transition={100}
style={{
...size({ size: 108 }),
borderRadius: 100,
}}
size={108}
imageProps={{ priority: "high" }}
/>
{isSelf && (
<Pressable
+36 -2
View File
@@ -23,6 +23,7 @@ import {
sanitizeStructureList,
segmentRequiresLyrics,
} from "../../utils/songStructure";
import { getStageAction } from "../../utils/projectStages";
import CustomInput from "./components/CustomInput";
const Lyrics = ({ navigation }) => {
@@ -264,17 +265,49 @@ const Lyrics = ({ navigation }) => {
if (!isSame) {
updateData.musicUrls = firebase.firestore.FieldValue.delete();
updateData.musicStatus = firebase.firestore.FieldValue.delete();
}
await projectsRef
.doc(selectedProjectId)
.set(updateData, { merge: true });
const projectForStage = {
...selectedProject,
title: titleTrimmed,
lyrics: normalizedNewLyrics,
config: sanitize(projectConfig),
selections: sanitize(projectSelections),
hasLyrics: true,
};
if (!isSame) {
projectForStage.musicUrls = undefined;
projectForStage.musicStatus = undefined;
}
const beatmakerStage = getStageAction("beatmaker", projectForStage);
await setIsLoading(false);
alert(
"Céline",
"Bravo ! Vous venez de terminer de créer les paroles de votre musique ! Maintenant vous pouvez passer à la prochaine étape : le Studio avec Malik. Dans cette étape vous allez pouvoir donner vie à votre chanson !",
[
{
text: "Retour à l'accueil",
style: "cancel",
onPress: () =>
navigate(Routes.BottomTab, {
screen: Routes.HomeStack,
params: {
screen: Routes.Home,
params: { showLyricsCongrats: true },
},
});
}),
},
{
text: "Continuer avec Malik",
onPress: () => {
const targetRoute = beatmakerStage?.route || Routes.Compose;
navigate(targetRoute, beatmakerStage?.params);
},
},
]
);
return;
} catch (e) {
console.log(e);
alertMessage("Erreur", "Échec de l'enregistrement dans le projet.");
@@ -293,6 +326,7 @@ const Lyrics = ({ navigation }) => {
selectedProjectId,
projectHasLyrics,
projectLyrics,
selectedProject,
]);
const typeOccurrences = {};
+34 -4
View File
@@ -3,6 +3,7 @@ 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 } from "../../assets";
import alert from "../../components/Alert";
import GradientButton from "../../components/GradientButton";
import MusicLandHeader from "../../components/MusicLandHeader";
import { isWeb } from "../../hooks/useLayoutType";
@@ -11,6 +12,7 @@ import { Routes } from "../../navigation";
import { goBack, navigate } from "../../navigation/NavigationService";
import { useUserData } from "../../providers/UserDataProvider";
import { gutters, Palette, Style } from "../../styles";
import { getStageAction } from "../../utils/projectStages";
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
const ValidateCover = () => {
@@ -95,18 +97,46 @@ const ValidateCover = () => {
const existingCover = selectedProject?.cover || {};
const finalUrl =
selectedOption.finalUrl || selectedOption.generatedUrl || null;
await updateProjectData({
cover: {
const nextCoverData = {
...existingCover,
options: coverOptions,
selectedOptionId: selectedOption.id,
result: finalUrl,
generatedBackground:
selectedOption.generatedUrl || selectedOption.finalUrl || null,
},
};
await updateProjectData({
cover: nextCoverData,
coverUrl: finalUrl,
});
navigate(Routes.Home);
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);
},
},
]
);
return;
} catch (e) {
console.log(e);
} finally {