From a33e2ed555ca41d0b922032ee5f5ddf539bbd9cb Mon Sep 17 00:00:00 2001 From: leon-morival Date: Wed, 22 Oct 2025 12:01:32 +0200 Subject: [PATCH] firtname and lastname before username --- .../bottomsheets/CommentsBottomSheet.js | 9 +- src/providers/UserDataProvider.js | 6 +- src/screens/CreatePassword.js | 2 +- src/screens/Home/HomeSave.js | 14 +- src/screens/Library/MusicDetails.js | 7 +- src/screens/Library/MusicDetails.web.js | 7 +- .../Library/components/SearchResultsList.js | 3 +- src/screens/Login.js | 10 +- .../Playbacks/components/CommentsPanel.web.js | 9 +- src/screens/Profile/Follows.js | 58 +-- src/screens/Profile/Profile.js | 33 +- src/screens/Register.js | 4 +- src/screens/Splash.js | 3 +- src/screens/Studio/SongReady.js | 4 +- src/screens/cover/ChooseCoverType.js | 362 +++++++++++++++++- src/utils/artistName.js | 39 ++ 16 files changed, 495 insertions(+), 75 deletions(-) create mode 100644 src/utils/artistName.js diff --git a/src/components/bottomsheets/CommentsBottomSheet.js b/src/components/bottomsheets/CommentsBottomSheet.js index 88fbf87..a8eed22 100644 --- a/src/components/bottomsheets/CommentsBottomSheet.js +++ b/src/components/bottomsheets/CommentsBottomSheet.js @@ -27,6 +27,7 @@ import { icons } from '../../assets'; import { increment, projectsRef, serverTimestamp } from '../../config/firebase'; import useDataFromRef from '../../hooks/useDataFromRef'; 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'; @@ -62,6 +63,10 @@ const CommentsBottomSheet = () => { const modalRef = useRef(null); const insets = useSafeAreaInsets(); const { currentUID, currentUserData } = useUser(); + const currentUserDisplayName = useMemo( + () => getArtistDisplayName(currentUserData, ''), + [currentUserData] + ); const scrollRef = useRef(null); const [projectId, setProjectId] = useState(null); @@ -131,7 +136,7 @@ const CommentsBottomSheet = () => { .collection('comments') .add({ userId: currentUID, - userName: currentUserData?.userName || '', + userName: currentUserDisplayName, profilePicture: currentUserData?.profilePictureURL || '', text: value, createdAt: serverTimestamp(), @@ -145,7 +150,7 @@ const CommentsBottomSheet = () => { const optimistic = { id: docRef?.id || Math.random().toString(36).slice(2), userId: currentUID, - userName: currentUserData?.userName || '', + userName: currentUserDisplayName, profilePicture: currentUserData?.profilePictureURL || '', text: value, createdAt: new Date(), diff --git a/src/providers/UserDataProvider.js b/src/providers/UserDataProvider.js index 1c1e4a2..a0efcb5 100644 --- a/src/providers/UserDataProvider.js +++ b/src/providers/UserDataProvider.js @@ -13,6 +13,7 @@ import firebase, { projectsRef, usersRef, } from "../config/firebase"; +import { getUserPreferredArtistName } from "../utils/artistName"; export const UserDataContext = createContext(); @@ -182,9 +183,12 @@ export default ({ children }) => { try { await setIsLoading(true); + const artistDisplayName = getUserPreferredArtistName( + currentUserDoc || currentUserData || {} + ); const payload = { userId: currentUID || null, - userName: currentUserData?.userName || null, + userName: artistDisplayName, createdAt: firebase.firestore.FieldValue.serverTimestamp(), updatedAt: firebase.firestore.FieldValue.serverTimestamp(), }; diff --git a/src/screens/CreatePassword.js b/src/screens/CreatePassword.js index ae583e1..7f64ad1 100644 --- a/src/screens/CreatePassword.js +++ b/src/screens/CreatePassword.js @@ -84,7 +84,7 @@ const CreatePassword = () => { { merge: true } ); setTooltip({ text: "Compte créé, continuons", type: "success" }); - navigate(Routes.CreatePseudo); + navigate(Routes.BottomTab); } catch (e) { console.log("Register error", e?.message); const message = diff --git a/src/screens/Home/HomeSave.js b/src/screens/Home/HomeSave.js index ed05873..86605d8 100644 --- a/src/screens/Home/HomeSave.js +++ b/src/screens/Home/HomeSave.js @@ -12,16 +12,26 @@ import Page from "../../layouts/Page"; import { Routes } from "../../navigation"; import { navigate } from "../../navigation/NavigationService"; import { useUser } from "../../providers/UserDataProvider"; +import { getArtistDisplayName } from "../../utils/artistName"; import { Palette } from "../../styles"; import { FONT_FAMILY } from "../../styles/Fonts"; import MusicCard from "../Library/components/MusicCard"; const HomeSave = () => { - const { userProjects = [], resetSelectedProject, selectProject } = useUser(); + const { + userProjects = [], + resetSelectedProject, + selectProject, + currentUserData, + } = useUser(); const projects = useMemo( () => (Array.isArray(userProjects) ? userProjects : []), [userProjects] ); + const ownerDisplayName = useMemo( + () => getArtistDisplayName(currentUserData, "MusicLand"), + [currentUserData] + ); const [, setTooltip] = useGlobal("_tooltip"); const [menuPosition, setMenuPosition] = useState(null); const [showMenu, setShowMenu] = useState(false); @@ -73,7 +83,7 @@ const HomeSave = () => { renderItem={({ item }) => ( { }, [project?.likedBy, currentUID]); 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 songUrl = project?.songUrl || null; diff --git a/src/screens/Library/MusicDetails.web.js b/src/screens/Library/MusicDetails.web.js index ca486f1..82a4750 100644 --- a/src/screens/Library/MusicDetails.web.js +++ b/src/screens/Library/MusicDetails.web.js @@ -37,6 +37,7 @@ import Page from "../../layouts/Page"; import { Palette } from "../../styles"; import { FONT_FAMILY } from "../../styles/Fonts"; import Style, { gutters, size } from "../../styles/Style"; +import { getArtistDisplayName } from "../../utils/artistName"; import { formatStructureLabel, getPromptLabelForStructure, @@ -86,7 +87,11 @@ const MusicDetails = ({ route }) => { }, [project?.likedBy, currentUID]); 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 songUrl = project?.songUrl || null; diff --git a/src/screens/Library/components/SearchResultsList.js b/src/screens/Library/components/SearchResultsList.js index b96125c..29c3cf2 100644 --- a/src/screens/Library/components/SearchResultsList.js +++ b/src/screens/Library/components/SearchResultsList.js @@ -9,6 +9,7 @@ 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"; @@ -232,7 +233,7 @@ const SearchResultsList = ({ fontFamily: FONT_FAMILY.InterRegular, }} > - {user?.userName || "Utilisateur"} + {getArtistDisplayName(user, "Utilisateur")} ))} diff --git a/src/screens/Login.js b/src/screens/Login.js index 54708c7..62592ff 100644 --- a/src/screens/Login.js +++ b/src/screens/Login.js @@ -20,7 +20,7 @@ import { background } from "../assets"; import GradientButton from "../components/GradientButton.js"; import { Input } from "../components/Input.js"; import ItemContainer from "../components/ItemContainer/ItemContainer"; -import firebase, { usersRef } from "../config/firebase"; +import firebase from "../config/firebase"; import { GOOGLE_ANDROID_CLIENT_ID, GOOGLE_IOS_CLIENT_ID, @@ -63,13 +63,7 @@ export default ({ navigation }) => { const afterLoginNavigate = async () => { const uid = firebase.auth().currentUser?.uid; 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 }] }); - } else { - navigation.reset({ index: 0, routes: [{ name: Routes.CreatePseudo }] }); - } + navigation.reset({ index: 0, routes: [{ name: Routes.BottomTab }] }); }; const onLogin = async () => { diff --git a/src/screens/Playbacks/components/CommentsPanel.web.js b/src/screens/Playbacks/components/CommentsPanel.web.js index 0ee5897..301ce25 100644 --- a/src/screens/Playbacks/components/CommentsPanel.web.js +++ b/src/screens/Playbacks/components/CommentsPanel.web.js @@ -27,6 +27,7 @@ import { } from "../../../config/firebase"; import useDataFromRef from "../../../hooks/useDataFromRef"; 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"; @@ -51,6 +52,10 @@ const CommentsPanel = ({ panelHeight, }) => { const { currentUID, currentUserData } = useUser() || {}; + const currentUserDisplayName = useMemo( + () => getArtistDisplayName(currentUserData, ""), + [currentUserData] + ); const [text, setText] = useState(""); const scrollRef = useRef(null); @@ -114,7 +119,7 @@ const CommentsPanel = ({ .collection("comments") .add({ userId: currentUID, - userName: currentUserData?.userName || "", + userName: currentUserDisplayName, profilePicture: currentUserData?.profilePictureURL || "", text: value, createdAt: serverTimestamp(), @@ -127,7 +132,7 @@ const CommentsPanel = ({ const optimistic = { id: docRef?.id || Math.random().toString(36).slice(2), userId: currentUID, - userName: currentUserData?.userName || "", + userName: currentUserDisplayName, profilePicture: currentUserData?.profilePictureURL || "", text: value, createdAt: new Date(), diff --git a/src/screens/Profile/Follows.js b/src/screens/Profile/Follows.js index 07a389e..a14b13d 100644 --- a/src/screens/Profile/Follows.js +++ b/src/screens/Profile/Follows.js @@ -13,6 +13,7 @@ import useDataFromRef from "../../hooks/useDataFromRef"; import { Routes } from "../../navigation"; import { useRoute } from "@react-navigation/native"; import { navigate } from "../../navigation/NavigationService"; +import { getArtistDisplayName } from "../../utils/artistName"; const Follows = () => { const { params } = useRoute(); @@ -84,34 +85,37 @@ const Follows = () => { ); - const UserRow = ({ user }) => ( - navigate(Routes.SingerProfile, { userId: user?.id })} - > - - { + const displayName = getArtistDisplayName(user, "Utilisateur"); + return ( + navigate(Routes.SingerProfile, { userId: user?.id })} > - {user?.userName || "Utilisateur"} - - - ); + + + {displayName} + + + ); + }; const list = selected === "Abonnés" ? followers : following; const loading = selected === "Abonnés" ? followersLoading : followingLoading; diff --git a/src/screens/Profile/Profile.js b/src/screens/Profile/Profile.js index 5900d0d..65c8af6 100644 --- a/src/screens/Profile/Profile.js +++ b/src/screens/Profile/Profile.js @@ -34,6 +34,7 @@ import Page from "../../layouts/Page"; import { Routes } from "../../navigation"; import { push } from "../../navigation/NavigationService"; import { useUser } from "../../providers/UserDataProvider"; +import { getArtistDisplayName } from "../../utils/artistName"; import { Palette } from "../../styles"; import { FONT_FAMILY } from "../../styles/Fonts"; import Style, { gutters, size } from "../../styles/Style"; @@ -64,6 +65,18 @@ const Profile = () => { const [selectedProjectId, setSelectedProjectId] = useState(null); const [updatingPhoto, setUpdatingPhoto] = useState(false); 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 onPressMenu = (item) => { setSelected(item); @@ -191,10 +204,7 @@ const Profile = () => { renderItem={({ item }) => ( { {webUploadMessage} ) : null} - {(isSelf ? currentUserData?.userName : userData?.userName) || - "Profil"} + style={{ + fontSize: 22, + color: Palette.white, + fontFamily: FONT_FAMILY.InterSemiBold, + }} + > + {profileTitle} { title="Inscription" > - + Renseigne tes informations {preferredLanguage && ( @@ -287,7 +287,7 @@ const styles = StyleSheet.create({ width: "100%", maxWidth: 420, maxHeight: "80%", - backgroundColor: Palette.darkPurple, + backgroundColor: Palette.ultraLightWhite, borderRadius: 20, padding: 16, gap: 12, diff --git a/src/screens/Splash.js b/src/screens/Splash.js index 0aa14c5..6a398b4 100644 --- a/src/screens/Splash.js +++ b/src/screens/Splash.js @@ -24,12 +24,11 @@ export default ({ navigation }) => { const user = userSnap?.data() || {}; if (userSnap.exists) setCurrentUserData(user); - const hasUserName = !!user?.userName; navigation.reset({ index: 0, routes: [ { - name: hasUserName ? Routes.BottomTab : Routes.CreatePseudo, + name: Routes.BottomTab, }, ], }); diff --git a/src/screens/Studio/SongReady.js b/src/screens/Studio/SongReady.js index 7fe922a..fdba00f 100644 --- a/src/screens/Studio/SongReady.js +++ b/src/screens/Studio/SongReady.js @@ -78,11 +78,11 @@ const SongReady = () => { // Sync progression depuis les players 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 n = Number(t || 0); if (!isFinite(n) || n <= 0) return 0; - return Platform.OS === "web" ? n : n * 1000; + return n >= 1000 ? n : n * 1000; }; const id = setInterval(() => { diff --git a/src/screens/cover/ChooseCoverType.js b/src/screens/cover/ChooseCoverType.js index a37584b..37859aa 100644 --- a/src/screens/cover/ChooseCoverType.js +++ b/src/screens/cover/ChooseCoverType.js @@ -1,26 +1,128 @@ import * as ImagePicker from "expo-image-picker"; -import React, { useState } from "react"; -import { Image, View } from "react-native"; +import { BlurView } from "expo-blur"; +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 { ai, background } from "../../assets"; import BorderGradientButton from "../../components/BorderGradientButton"; import FullscreenIntroVideo from "../../components/FullscreenIntroVideo"; import GradientButton from "../../components/GradientButton"; import MusicLandHeader from "../../components/MusicLandHeader"; +import { Input } from "../../components/Input"; +import firebase, { projectsRef, usersRef } from "../../config/firebase"; import { uploadFileToFirebase } from "../../helpers/uploadToFirebase"; import Page from "../../layouts/Page"; import { Routes } from "../../navigation"; import { goBack, navigate } from "../../navigation/NavigationService"; 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 = () => { const [showIntro, setShowIntro] = useState(true); - const { selectedProject, selectedProjectId, updateProjectData } = - useUserData(); - const { setIsLoading } = useMinuit(); + const [choiceVisible, setChoiceVisible] = useState(false); + const [pseudoVisible, setPseudoVisible] = useState(false); + 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 { await setIsLoading(true); const result = await ImagePicker.launchImageLibraryAsync({ @@ -33,7 +135,7 @@ export const ChooseCoverType = () => { const uri = result?.assets?.[0]?.uri || null; if (!uri) return; - const projectId = selectedProject?.id || selectedProjectId; + if (!projectId) return; const path = `musics/${projectId}/userSelectedCover.png`; const { resultURI } = await uploadFileToFirebase({ @@ -57,7 +159,120 @@ export const ChooseCoverType = () => { } finally { 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 ( @@ -85,11 +300,11 @@ export const ChooseCoverType = () => { > navigate(Routes.PouchReady)} + onPress={handleGenerateCover} /> @@ -97,6 +312,131 @@ export const ChooseCoverType = () => { visible={showIntro} onClose={() => setShowIntro(false)} /> + + + + + + Nom d'artiste + + Veux-tu utiliser ton prénom et nom pour tes musiques ou créer un + nom d'artiste ? + + + + + + + + + + + + + + + Créer un nom d'artiste + + Choisis le nom qui sera visible sur tes musiques. + + + + + + + + + + + + ); }; + +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, + }, +}); diff --git a/src/utils/artistName.js b/src/utils/artistName.js new file mode 100644 index 0000000..2b5cdb7 --- /dev/null +++ b/src/utils/artistName.js @@ -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; +};