firtname and lastname before username

This commit is contained in:
2025-10-22 12:01:32 +02:00
parent 91163b8a75
commit a33e2ed555
16 changed files with 495 additions and 75 deletions
@@ -27,6 +27,7 @@ import { icons } from '../../assets';
import { increment, projectsRef, serverTimestamp } from '../../config/firebase'; import { increment, projectsRef, serverTimestamp } from '../../config/firebase';
import useDataFromRef from '../../hooks/useDataFromRef'; import useDataFromRef from '../../hooks/useDataFromRef';
import { useUser } from '../../providers/UserDataProvider'; import { useUser } from '../../providers/UserDataProvider';
import { getArtistDisplayName } from '../../utils/artistName';
import { Palette } from '../../styles'; import { Palette } from '../../styles';
import { FONT_FAMILY } from '../../styles/Fonts'; import { FONT_FAMILY } from '../../styles/Fonts';
import { size } from '../../styles/Style'; import { size } from '../../styles/Style';
@@ -62,6 +63,10 @@ const CommentsBottomSheet = () => {
const modalRef = useRef(null); const modalRef = useRef(null);
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const { currentUID, currentUserData } = useUser(); const { currentUID, currentUserData } = useUser();
const currentUserDisplayName = useMemo(
() => getArtistDisplayName(currentUserData, ''),
[currentUserData]
);
const scrollRef = useRef(null); const scrollRef = useRef(null);
const [projectId, setProjectId] = useState(null); const [projectId, setProjectId] = useState(null);
@@ -131,7 +136,7 @@ const CommentsBottomSheet = () => {
.collection('comments') .collection('comments')
.add({ .add({
userId: currentUID, userId: currentUID,
userName: currentUserData?.userName || '', userName: currentUserDisplayName,
profilePicture: currentUserData?.profilePictureURL || '', profilePicture: currentUserData?.profilePictureURL || '',
text: value, text: value,
createdAt: serverTimestamp(), createdAt: serverTimestamp(),
@@ -145,7 +150,7 @@ const CommentsBottomSheet = () => {
const optimistic = { const optimistic = {
id: docRef?.id || Math.random().toString(36).slice(2), id: docRef?.id || Math.random().toString(36).slice(2),
userId: currentUID, userId: currentUID,
userName: currentUserData?.userName || '', userName: currentUserDisplayName,
profilePicture: currentUserData?.profilePictureURL || '', profilePicture: currentUserData?.profilePictureURL || '',
text: value, text: value,
createdAt: new Date(), createdAt: new Date(),
+5 -1
View File
@@ -13,6 +13,7 @@ import firebase, {
projectsRef, projectsRef,
usersRef, usersRef,
} from "../config/firebase"; } from "../config/firebase";
import { getUserPreferredArtistName } from "../utils/artistName";
export const UserDataContext = createContext(); export const UserDataContext = createContext();
@@ -182,9 +183,12 @@ export default ({ children }) => {
try { try {
await setIsLoading(true); await setIsLoading(true);
const artistDisplayName = getUserPreferredArtistName(
currentUserDoc || currentUserData || {}
);
const payload = { const payload = {
userId: currentUID || null, userId: currentUID || null,
userName: currentUserData?.userName || null, userName: artistDisplayName,
createdAt: firebase.firestore.FieldValue.serverTimestamp(), createdAt: firebase.firestore.FieldValue.serverTimestamp(),
updatedAt: firebase.firestore.FieldValue.serverTimestamp(), updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
}; };
+1 -1
View File
@@ -84,7 +84,7 @@ const CreatePassword = () => {
{ merge: true } { merge: true }
); );
setTooltip({ text: "Compte créé, continuons", type: "success" }); setTooltip({ text: "Compte créé, continuons", type: "success" });
navigate(Routes.CreatePseudo); navigate(Routes.BottomTab);
} catch (e) { } catch (e) {
console.log("Register error", e?.message); console.log("Register error", e?.message);
const message = const message =
+12 -2
View File
@@ -12,16 +12,26 @@ import Page from "../../layouts/Page";
import { Routes } from "../../navigation"; import { Routes } from "../../navigation";
import { navigate } from "../../navigation/NavigationService"; import { navigate } from "../../navigation/NavigationService";
import { useUser } from "../../providers/UserDataProvider"; import { useUser } from "../../providers/UserDataProvider";
import { getArtistDisplayName } from "../../utils/artistName";
import { Palette } from "../../styles"; import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts"; import { FONT_FAMILY } from "../../styles/Fonts";
import MusicCard from "../Library/components/MusicCard"; import MusicCard from "../Library/components/MusicCard";
const HomeSave = () => { const HomeSave = () => {
const { userProjects = [], resetSelectedProject, selectProject } = useUser(); const {
userProjects = [],
resetSelectedProject,
selectProject,
currentUserData,
} = useUser();
const projects = useMemo( const projects = useMemo(
() => (Array.isArray(userProjects) ? userProjects : []), () => (Array.isArray(userProjects) ? userProjects : []),
[userProjects] [userProjects]
); );
const ownerDisplayName = useMemo(
() => getArtistDisplayName(currentUserData, "MusicLand"),
[currentUserData]
);
const [, setTooltip] = useGlobal("_tooltip"); const [, setTooltip] = useGlobal("_tooltip");
const [menuPosition, setMenuPosition] = useState(null); const [menuPosition, setMenuPosition] = useState(null);
const [showMenu, setShowMenu] = useState(false); const [showMenu, setShowMenu] = useState(false);
@@ -73,7 +83,7 @@ const HomeSave = () => {
renderItem={({ item }) => ( renderItem={({ item }) => (
<MusicCard <MusicCard
title={item?.title || "Sans titre"} title={item?.title || "Sans titre"}
subtitle={item?.userName} subtitle={item?.userName || ownerDisplayName}
imageUri={item?.coverUrl || null} imageUri={item?.coverUrl || null}
projectId={item?.id} projectId={item?.id}
likedBy={item?.likedBy || []} likedBy={item?.likedBy || []}
+6 -1
View File
@@ -30,6 +30,7 @@ import Page from "../../layouts/Page";
import { Palette } from "../../styles"; import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts"; import { FONT_FAMILY } from "../../styles/Fonts";
import Style, { gutters, size } from "../../styles/Style"; import Style, { gutters, size } from "../../styles/Style";
import { getArtistDisplayName } from "../../utils/artistName";
// 20 secondes // 20 secondes
const timeBeforeIncrement = 20000; const timeBeforeIncrement = 20000;
@@ -69,7 +70,11 @@ const MusicDetails = ({ route }) => {
}, [project?.likedBy, currentUID]); }, [project?.likedBy, currentUID]);
const title = project?.title || "Sans titre"; const title = project?.title || "Sans titre";
const artist = owner?.userName || "MusicLand"; const artist = useMemo(() => {
const ownerName = getArtistDisplayName(owner, "");
if (ownerName) return ownerName;
return getArtistDisplayName(project, "MusicLand");
}, [owner, project]);
const coverUrl = project?.coverUrl || null; const coverUrl = project?.coverUrl || null;
const songUrl = project?.songUrl || null; const songUrl = project?.songUrl || null;
+6 -1
View File
@@ -37,6 +37,7 @@ import Page from "../../layouts/Page";
import { Palette } from "../../styles"; import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts"; import { FONT_FAMILY } from "../../styles/Fonts";
import Style, { gutters, size } from "../../styles/Style"; import Style, { gutters, size } from "../../styles/Style";
import { getArtistDisplayName } from "../../utils/artistName";
import { import {
formatStructureLabel, formatStructureLabel,
getPromptLabelForStructure, getPromptLabelForStructure,
@@ -86,7 +87,11 @@ const MusicDetails = ({ route }) => {
}, [project?.likedBy, currentUID]); }, [project?.likedBy, currentUID]);
const title = project?.title || "Sans titre"; const title = project?.title || "Sans titre";
const artist = owner?.userName || "MusicLand"; const artist = useMemo(() => {
const ownerName = getArtistDisplayName(owner, "");
if (ownerName) return ownerName;
return getArtistDisplayName(project, "MusicLand");
}, [owner, project]);
const coverUrl = project?.coverUrl || null; const coverUrl = project?.coverUrl || null;
const songUrl = project?.songUrl || null; const songUrl = project?.songUrl || null;
@@ -9,6 +9,7 @@ import { navigate } from "../../../navigation/NavigationService";
import { Palette, Style } from "../../../styles"; import { Palette, Style } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts"; import { FONT_FAMILY } from "../../../styles/Fonts";
import { size } from "../../../styles/Style"; import { size } from "../../../styles/Style";
import { getArtistDisplayName } from "../../../utils/artistName";
import CreateLyricsHeader from "../../Writing/components/CreateLyricsHeader"; import CreateLyricsHeader from "../../Writing/components/CreateLyricsHeader";
import MusicCard from "./MusicCard"; import MusicCard from "./MusicCard";
@@ -232,7 +233,7 @@ const SearchResultsList = ({
fontFamily: FONT_FAMILY.InterRegular, fontFamily: FONT_FAMILY.InterRegular,
}} }}
> >
{user?.userName || "Utilisateur"} {getArtistDisplayName(user, "Utilisateur")}
</Text> </Text>
</Pressable> </Pressable>
))} ))}
+1 -7
View File
@@ -20,7 +20,7 @@ import { background } from "../assets";
import GradientButton from "../components/GradientButton.js"; import GradientButton from "../components/GradientButton.js";
import { Input } from "../components/Input.js"; import { Input } from "../components/Input.js";
import ItemContainer from "../components/ItemContainer/ItemContainer"; import ItemContainer from "../components/ItemContainer/ItemContainer";
import firebase, { usersRef } from "../config/firebase"; import firebase from "../config/firebase";
import { import {
GOOGLE_ANDROID_CLIENT_ID, GOOGLE_ANDROID_CLIENT_ID,
GOOGLE_IOS_CLIENT_ID, GOOGLE_IOS_CLIENT_ID,
@@ -63,13 +63,7 @@ export default ({ navigation }) => {
const afterLoginNavigate = async () => { const afterLoginNavigate = async () => {
const uid = firebase.auth().currentUser?.uid; const uid = firebase.auth().currentUser?.uid;
if (!uid) throw new Error("Aucun utilisateur après connexion"); if (!uid) throw new Error("Aucun utilisateur après connexion");
const snap = await usersRef.doc(uid).get();
const hasUserName = !!snap.data()?.userName;
if (hasUserName) {
navigation.reset({ index: 0, routes: [{ name: Routes.BottomTab }] }); navigation.reset({ index: 0, routes: [{ name: Routes.BottomTab }] });
} else {
navigation.reset({ index: 0, routes: [{ name: Routes.CreatePseudo }] });
}
}; };
const onLogin = async () => { const onLogin = async () => {
@@ -27,6 +27,7 @@ import {
} from "../../../config/firebase"; } from "../../../config/firebase";
import useDataFromRef from "../../../hooks/useDataFromRef"; import useDataFromRef from "../../../hooks/useDataFromRef";
import { useUser } from "../../../providers/UserDataProvider"; import { useUser } from "../../../providers/UserDataProvider";
import { getArtistDisplayName } from "../../../utils/artistName";
import { Palette } from "../../../styles"; import { Palette } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts"; import { FONT_FAMILY } from "../../../styles/Fonts";
import { size } from "../../../styles/Style"; import { size } from "../../../styles/Style";
@@ -51,6 +52,10 @@ const CommentsPanel = ({
panelHeight, panelHeight,
}) => { }) => {
const { currentUID, currentUserData } = useUser() || {}; const { currentUID, currentUserData } = useUser() || {};
const currentUserDisplayName = useMemo(
() => getArtistDisplayName(currentUserData, ""),
[currentUserData]
);
const [text, setText] = useState(""); const [text, setText] = useState("");
const scrollRef = useRef(null); const scrollRef = useRef(null);
@@ -114,7 +119,7 @@ const CommentsPanel = ({
.collection("comments") .collection("comments")
.add({ .add({
userId: currentUID, userId: currentUID,
userName: currentUserData?.userName || "", userName: currentUserDisplayName,
profilePicture: currentUserData?.profilePictureURL || "", profilePicture: currentUserData?.profilePictureURL || "",
text: value, text: value,
createdAt: serverTimestamp(), createdAt: serverTimestamp(),
@@ -127,7 +132,7 @@ const CommentsPanel = ({
const optimistic = { const optimistic = {
id: docRef?.id || Math.random().toString(36).slice(2), id: docRef?.id || Math.random().toString(36).slice(2),
userId: currentUID, userId: currentUID,
userName: currentUserData?.userName || "", userName: currentUserDisplayName,
profilePicture: currentUserData?.profilePictureURL || "", profilePicture: currentUserData?.profilePictureURL || "",
text: value, text: value,
createdAt: new Date(), createdAt: new Date(),
+6 -2
View File
@@ -13,6 +13,7 @@ import useDataFromRef from "../../hooks/useDataFromRef";
import { Routes } from "../../navigation"; import { Routes } from "../../navigation";
import { useRoute } from "@react-navigation/native"; import { useRoute } from "@react-navigation/native";
import { navigate } from "../../navigation/NavigationService"; import { navigate } from "../../navigation/NavigationService";
import { getArtistDisplayName } from "../../utils/artistName";
const Follows = () => { const Follows = () => {
const { params } = useRoute(); const { params } = useRoute();
@@ -84,7 +85,9 @@ const Follows = () => {
</View> </View>
); );
const UserRow = ({ user }) => ( const UserRow = ({ user }) => {
const displayName = getArtistDisplayName(user, "Utilisateur");
return (
<Pressable <Pressable
style={{ gap: 14, ...Style.containerRow }} style={{ gap: 14, ...Style.containerRow }}
onPress={() => navigate(Routes.SingerProfile, { userId: user?.id })} onPress={() => navigate(Routes.SingerProfile, { userId: user?.id })}
@@ -108,10 +111,11 @@ const Follows = () => {
fontFamily: FONT_FAMILY.InterRegular, fontFamily: FONT_FAMILY.InterRegular,
}} }}
> >
{user?.userName || "Utilisateur"} {displayName}
</Text> </Text>
</Pressable> </Pressable>
); );
};
const list = selected === "Abonnés" ? followers : following; const list = selected === "Abonnés" ? followers : following;
const loading = selected === "Abonnés" ? followersLoading : followingLoading; const loading = selected === "Abonnés" ? followersLoading : followingLoading;
+15 -6
View File
@@ -34,6 +34,7 @@ import Page from "../../layouts/Page";
import { Routes } from "../../navigation"; import { Routes } from "../../navigation";
import { push } from "../../navigation/NavigationService"; import { push } from "../../navigation/NavigationService";
import { useUser } from "../../providers/UserDataProvider"; import { useUser } from "../../providers/UserDataProvider";
import { getArtistDisplayName } from "../../utils/artistName";
import { Palette } from "../../styles"; import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts"; import { FONT_FAMILY } from "../../styles/Fonts";
import Style, { gutters, size } from "../../styles/Style"; import Style, { gutters, size } from "../../styles/Style";
@@ -64,6 +65,18 @@ const Profile = () => {
const [selectedProjectId, setSelectedProjectId] = useState(null); const [selectedProjectId, setSelectedProjectId] = useState(null);
const [updatingPhoto, setUpdatingPhoto] = useState(false); const [updatingPhoto, setUpdatingPhoto] = useState(false);
const [webUploadMessage, setWebUploadMessage] = useState(""); const [webUploadMessage, setWebUploadMessage] = useState("");
const selfDisplayName = useMemo(
() => getArtistDisplayName(currentUserData, "MusicLand"),
[currentUserData]
);
const targetDisplayName = useMemo(
() => getArtistDisplayName(userData, "MusicLand"),
[userData]
);
const profileTitle = useMemo(
() => getArtistDisplayName(isSelf ? currentUserData : userData, "Profil"),
[currentUserData, isSelf, userData]
);
const navigateToMusicDetails = useNavigateToMusicDetails(); const navigateToMusicDetails = useNavigateToMusicDetails();
const onPressMenu = (item) => { const onPressMenu = (item) => {
setSelected(item); setSelected(item);
@@ -191,10 +204,7 @@ const Profile = () => {
renderItem={({ item }) => ( renderItem={({ item }) => (
<MusicCard <MusicCard
title={item?.title || "Sans titre"} title={item?.title || "Sans titre"}
subtitle={ subtitle={isSelf ? selfDisplayName : targetDisplayName}
(isSelf ? currentUserData?.userName : userData?.userName) ||
"MusicLand"
}
imageUri={item?.coverUrl || null} imageUri={item?.coverUrl || null}
projectId={item?.id} projectId={item?.id}
likedBy={item?.likedBy || []} likedBy={item?.likedBy || []}
@@ -409,8 +419,7 @@ const Profile = () => {
fontFamily: FONT_FAMILY.InterSemiBold, fontFamily: FONT_FAMILY.InterSemiBold,
}} }}
> >
{(isSelf ? currentUserData?.userName : userData?.userName) || {profileTitle}
"Profil"}
</Text> </Text>
</View> </View>
<View <View
+2 -2
View File
@@ -79,7 +79,7 @@ const Register = () => {
title="Inscription" title="Inscription"
> >
<View style={styles.pageContent}> <View style={styles.pageContent}>
<ItemContainer height={520} disableKeyboardHeight> <ItemContainer height={620} disableKeyboardHeight>
<View style={styles.formContent}> <View style={styles.formContent}>
<Text style={styles.title}>Renseigne tes informations</Text> <Text style={styles.title}>Renseigne tes informations</Text>
{preferredLanguage && ( {preferredLanguage && (
@@ -287,7 +287,7 @@ const styles = StyleSheet.create({
width: "100%", width: "100%",
maxWidth: 420, maxWidth: 420,
maxHeight: "80%", maxHeight: "80%",
backgroundColor: Palette.darkPurple, backgroundColor: Palette.ultraLightWhite,
borderRadius: 20, borderRadius: 20,
padding: 16, padding: 16,
gap: 12, gap: 12,
+1 -2
View File
@@ -24,12 +24,11 @@ export default ({ navigation }) => {
const user = userSnap?.data() || {}; const user = userSnap?.data() || {};
if (userSnap.exists) setCurrentUserData(user); if (userSnap.exists) setCurrentUserData(user);
const hasUserName = !!user?.userName;
navigation.reset({ navigation.reset({
index: 0, index: 0,
routes: [ routes: [
{ {
name: hasUserName ? Routes.BottomTab : Routes.CreatePseudo, name: Routes.BottomTab,
}, },
], ],
}); });
+2 -2
View File
@@ -78,11 +78,11 @@ const SongReady = () => {
// Sync progression depuis les players // Sync progression depuis les players
useEffect(() => { useEffect(() => {
// expo-audio returns seconds on native, but on web values can be milliseconds // Ensure we consistently work with milliseconds whatever the platform
const toMs = (t) => { const toMs = (t) => {
const n = Number(t || 0); const n = Number(t || 0);
if (!isFinite(n) || n <= 0) return 0; if (!isFinite(n) || n <= 0) return 0;
return Platform.OS === "web" ? n : n * 1000; return n >= 1000 ? n : n * 1000;
}; };
const id = setInterval(() => { const id = setInterval(() => {
+350 -10
View File
@@ -1,26 +1,128 @@
import * as ImagePicker from "expo-image-picker"; import * as ImagePicker from "expo-image-picker";
import React, { useState } from "react"; import { BlurView } from "expo-blur";
import { Image, View } from "react-native"; import React, { useCallback, useEffect, useMemo, useState } from "react";
import {
Image,
Modal,
Pressable,
StyleSheet,
Text,
View,
} from "react-native";
import useMinuit from "react-native-minuit/src/hooks/useMinuit"; import useMinuit from "react-native-minuit/src/hooks/useMinuit";
import { ai, background } from "../../assets"; import { ai, background } from "../../assets";
import BorderGradientButton from "../../components/BorderGradientButton"; import BorderGradientButton from "../../components/BorderGradientButton";
import FullscreenIntroVideo from "../../components/FullscreenIntroVideo"; import FullscreenIntroVideo from "../../components/FullscreenIntroVideo";
import GradientButton from "../../components/GradientButton"; import GradientButton from "../../components/GradientButton";
import MusicLandHeader from "../../components/MusicLandHeader"; import MusicLandHeader from "../../components/MusicLandHeader";
import { Input } from "../../components/Input";
import firebase, { projectsRef, usersRef } from "../../config/firebase";
import { uploadFileToFirebase } from "../../helpers/uploadToFirebase"; import { uploadFileToFirebase } from "../../helpers/uploadToFirebase";
import Page from "../../layouts/Page"; import Page from "../../layouts/Page";
import { Routes } from "../../navigation"; import { Routes } from "../../navigation";
import { goBack, navigate } from "../../navigation/NavigationService"; import { goBack, navigate } from "../../navigation/NavigationService";
import { useUserData } from "../../providers/UserDataProvider"; import { useUserData } from "../../providers/UserDataProvider";
import { gutters } from "../../styles"; import { buildFullName } from "../../utils/artistName";
import { gutters, Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
export const ChooseCoverType = () => { export const ChooseCoverType = () => {
const [showIntro, setShowIntro] = useState(true); const [showIntro, setShowIntro] = useState(true);
const { selectedProject, selectedProjectId, updateProjectData } = const [choiceVisible, setChoiceVisible] = useState(false);
useUserData(); const [pseudoVisible, setPseudoVisible] = useState(false);
const { setIsLoading } = useMinuit(); const [pseudo, setPseudo] = useState("");
const [pendingAction, setPendingAction] = useState(null);
const [savingChoice, setSavingChoice] = useState(false);
const [savingPseudo, setSavingPseudo] = useState(false);
const onPickUserImage = async () => { const {
selectedProject,
selectedProjectId,
updateProjectData,
currentUserData,
currentUID,
} = useUserData();
const { setIsLoading, setTooltip } = useMinuit();
const projectId = selectedProject?.id || selectedProjectId || null;
const hasArtistPreference = useMemo(() => {
if (currentUserData?.userName) return true;
const pref = currentUserData?.artistNamePreference;
return pref === "REAL_NAME" || pref === "CUSTOM";
}, [currentUserData?.artistNamePreference, currentUserData?.userName]);
const realName = useMemo(() => buildFullName(currentUserData), [
currentUserData?.firstName,
currentUserData?.lastName,
currentUserData?.displayName,
]);
useEffect(() => {
if (pseudoVisible) {
setPseudo(currentUserData?.userName || "");
}
}, [currentUserData?.userName, pseudoVisible]);
const ensureArtistPreference = useCallback(
(nextAction) => {
if (hasArtistPreference) {
if (typeof nextAction === "function") {
nextAction();
}
return;
}
setPendingAction(() => nextAction);
setChoiceVisible(true);
},
[hasArtistPreference]
);
const runPendingAction = useCallback(async () => {
if (typeof pendingAction === "function") {
const action = pendingAction;
setPendingAction(null);
await action();
} else {
setPendingAction(null);
}
}, [pendingAction]);
const applyDisplayNameToProjects = useCallback(
async (displayName) => {
if (!currentUID) return;
const safeName =
typeof displayName === "string" && displayName.trim().length > 0
? displayName.trim()
: null;
try {
const snapshot = await projectsRef
.where("userId", "==", currentUID)
.get();
if (!snapshot.empty) {
const batch = firebase.firestore().batch();
snapshot.docs.forEach((doc) => {
batch.set(doc.ref, { userName: safeName }, { merge: true });
});
await batch.commit();
}
} catch (error) {
console.log("applyDisplayNameToProjects error", error?.message);
}
if (!projectId) return;
try {
await projectsRef
.doc(projectId)
.set({ userName: safeName }, { merge: true });
} catch (error) {
console.log("update current project userName error", error?.message);
}
},
[currentUID, projectId]
);
const pickUserImage = useCallback(async () => {
try { try {
await setIsLoading(true); await setIsLoading(true);
const result = await ImagePicker.launchImageLibraryAsync({ const result = await ImagePicker.launchImageLibraryAsync({
@@ -33,7 +135,7 @@ export const ChooseCoverType = () => {
const uri = result?.assets?.[0]?.uri || null; const uri = result?.assets?.[0]?.uri || null;
if (!uri) return; if (!uri) return;
const projectId = selectedProject?.id || selectedProjectId; if (!projectId) return;
const path = `musics/${projectId}/userSelectedCover.png`; const path = `musics/${projectId}/userSelectedCover.png`;
const { resultURI } = await uploadFileToFirebase({ const { resultURI } = await uploadFileToFirebase({
@@ -57,7 +159,120 @@ export const ChooseCoverType = () => {
} finally { } finally {
await setIsLoading(false); await setIsLoading(false);
} }
}, [projectId, setIsLoading, updateProjectData]);
const handlePickUserImage = useCallback(() => {
ensureArtistPreference(pickUserImage);
}, [ensureArtistPreference, pickUserImage]);
const handleGenerateCover = useCallback(() => {
ensureArtistPreference(() => navigate(Routes.PouchReady));
}, [ensureArtistPreference]);
const closeChoiceModal = useCallback(() => {
setChoiceVisible(false);
setPendingAction(null);
}, []);
const openPseudoModal = useCallback(() => {
setChoiceVisible(false);
setPseudoVisible(true);
}, []);
const handleUseRealName = useCallback(async () => {
if (!currentUID) return;
if (!realName) {
setTooltip({
type: "error",
text:
"Renseigne ton prénom et nom dans ton profil ou crée un nom d'artiste.",
});
setChoiceVisible(false);
setPseudoVisible(true);
return;
}
try {
setSavingChoice(true);
const updates = {
artistNamePreference: "REAL_NAME",
userName: realName,
userNameLower: realName.toLowerCase(),
updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
}; };
await usersRef.doc(currentUID).set(updates, { merge: true });
await applyDisplayNameToProjects(realName);
setTooltip({ type: "success", text: "Nom d'artiste mis à jour" });
setChoiceVisible(false);
await runPendingAction();
} catch (e) {
console.log("handleUseRealName error", e?.message);
setTooltip({
type: "error",
text: e?.message || "Impossible de mettre à jour le nom",
});
} finally {
setSavingChoice(false);
}
}, [
applyDisplayNameToProjects,
currentUID,
currentUserData?.userName,
currentUserData?.userNameLower,
realName,
runPendingAction,
setTooltip,
]);
const handleCancelPseudo = useCallback(() => {
setPseudoVisible(false);
setPendingAction(null);
}, []);
const handleSavePseudo = useCallback(async () => {
const value = (pseudo || "").trim();
if (!value || !currentUID) return;
try {
setSavingPseudo(true);
const lower = value.toLowerCase();
const existing = await usersRef
.where("userNameLower", "==", lower)
.limit(1)
.get();
if (!existing.empty && existing.docs[0].id !== currentUID) {
setTooltip({ text: "Ce pseudo est déjà pris", type: "error" });
return;
}
await usersRef.doc(currentUID).set(
{
userName: value,
userNameLower: lower,
artistNamePreference: "CUSTOM",
updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
},
{ merge: true }
);
await applyDisplayNameToProjects(value);
setTooltip({ type: "success", text: "Pseudo enregistré" });
setPseudoVisible(false);
await runPendingAction();
} catch (e) {
console.log("handleSavePseudo error", e?.message);
setTooltip({
type: "error",
text: e?.message || "Enregistrement impossible",
});
} finally {
setSavingPseudo(false);
}
}, [
applyDisplayNameToProjects,
currentUID,
pseudo,
runPendingAction,
setTooltip,
]);
return ( return (
<Page backgroundImg={background.studioBG2} headerType="NONE"> <Page backgroundImg={background.studioBG2} headerType="NONE">
@@ -85,11 +300,11 @@ export const ChooseCoverType = () => {
> >
<BorderGradientButton <BorderGradientButton
title="Choisir une image" title="Choisir une image"
onPress={onPickUserImage} onPress={handlePickUserImage}
/> />
<GradientButton <GradientButton
title="Générer une pochette" title="Générer une pochette"
onPress={() => navigate(Routes.PouchReady)} onPress={handleGenerateCover}
/> />
</View> </View>
</View> </View>
@@ -97,6 +312,131 @@ export const ChooseCoverType = () => {
visible={showIntro} visible={showIntro}
onClose={() => setShowIntro(false)} onClose={() => setShowIntro(false)}
/> />
<Modal
visible={choiceVisible}
transparent
animationType="fade"
onRequestClose={closeChoiceModal}
>
<View style={styles.modalBackdrop}>
<Pressable
style={styles.modalOverlay}
onPress={closeChoiceModal}
accessibilityRole="button"
/>
<BlurView intensity={20} style={styles.modalCard}>
<View style={styles.modalContent}>
<Text style={styles.modalTitle}>Nom d'artiste</Text>
<Text style={styles.modalDescription}>
Veux-tu utiliser ton prénom et nom pour tes musiques ou créer un
nom d'artiste ?
</Text>
<View style={styles.modalButtons}>
<BorderGradientButton
title={
savingChoice ? "Chargement..." : "Utiliser prénom/nom"
}
onPress={handleUseRealName}
disabled={savingChoice}
/>
<GradientButton
title="Créer un nom d'artiste"
onPress={openPseudoModal}
disabled={savingChoice}
/>
</View>
</View>
</BlurView>
</View>
</Modal>
<Modal
visible={pseudoVisible}
transparent
animationType="fade"
onRequestClose={handleCancelPseudo}
>
<View style={styles.modalBackdrop}>
<Pressable
style={styles.modalOverlay}
onPress={handleCancelPseudo}
accessibilityRole="button"
/>
<BlurView intensity={20} style={styles.modalCard}>
<View style={styles.modalContent}>
<Text style={styles.modalTitle}>Créer un nom d'artiste</Text>
<Text style={styles.modalDescription}>
Choisis le nom qui sera visible sur tes musiques.
</Text>
<View style={styles.inputWrapper}>
<Input
placeholder="Nom d'artiste"
label="Nom d'artiste"
value={pseudo}
setValue={setPseudo}
isBlur
/>
</View>
<View style={styles.modalButtons}>
<BorderGradientButton
title="Annuler"
onPress={handleCancelPseudo}
disabled={savingPseudo}
/>
<GradientButton
title={savingPseudo ? "Enregistrement..." : "Valider"}
onPress={handleSavePseudo}
disabled={savingPseudo || !(pseudo || "").trim()}
/>
</View>
</View>
</BlurView>
</View>
</Modal>
</Page> </Page>
); );
}; };
const styles = StyleSheet.create({
modalBackdrop: {
flex: 1,
backgroundColor: "rgba(0,0,0,0.65)",
justifyContent: "center",
alignItems: "center",
padding: gutters,
},
modalOverlay: {
...StyleSheet.absoluteFillObject,
},
modalCard: {
width: "100%",
maxWidth: 420,
borderRadius: 24,
overflow: "hidden",
backgroundColor: Palette.glass,
},
modalContent: {
paddingHorizontal: 24,
paddingVertical: 28,
gap: 18,
},
modalTitle: {
fontSize: 22,
color: Palette.white,
fontFamily: FONT_FAMILY.InterSemiBold,
textAlign: "center",
},
modalDescription: {
fontSize: 15,
color: Palette.gray,
fontFamily: FONT_FAMILY.InterRegular,
textAlign: "center",
},
modalButtons: {
width: "80%",
alignSelf: "center",
gap: 12,
},
inputWrapper: {
gap: 12,
},
});
+39
View File
@@ -0,0 +1,39 @@
export const buildFullName = ({ firstName, lastName, displayName } = {}) => {
const first =
typeof firstName === "string" && firstName.trim().length > 0
? firstName.trim()
: "";
const last =
typeof lastName === "string" && lastName.trim().length > 0
? lastName.trim()
: "";
const nameFromParts = [first, last].filter(Boolean).join(" ").trim();
if (nameFromParts) return nameFromParts;
const fallbackDisplay =
typeof displayName === "string" && displayName.trim().length > 0
? displayName.trim()
: "";
return fallbackDisplay || null;
};
export const getArtistDisplayName = (source, fallback = "") => {
if (!source) return fallback;
const rawUserName =
typeof source?.userName === "string" ? source.userName.trim() : "";
if (rawUserName) return rawUserName;
const nameFromParts = buildFullName({
firstName: source?.firstName,
lastName: source?.lastName,
displayName: source?.displayName,
});
if (nameFromParts) return nameFromParts;
return fallback;
};
export const getUserPreferredArtistName = (user) => {
const display = getArtistDisplayName(user, "");
return display || null;
};