feat: fixes and formatter

This commit is contained in:
2026-01-12 16:01:32 +01:00
parent 85c6084351
commit 11e632acff
353 changed files with 23315 additions and 27361 deletions
+71 -100
View File
@@ -1,26 +1,22 @@
import { useRoute } from "@react-navigation/native";
import React, { useMemo, useState } from "react";
import { ActivityIndicator, Text, View } from "react-native";
import { background } from "../../assets";
import GradientButton from "../../components/GradientButton";
import MoreMenu from "../../components/MoreMenu";
import useNavigateToMusicDetails from "../../hooks/useNavigateToMusicDetails";
import Page from "../../layouts/Page";
import { Routes } from "../../navigation";
import { navigate } from "../../navigation/NavigationService";
import { useUser } from "../../providers/UserDataProvider";
import { Palette, Style, gutters } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import { getProjectLikes, LIKE_TARGET } from "../../utils/likes";
import MusicCard from "./components/MusicCard";
import { useRoute } from '@react-navigation/native'
import React, { useMemo, useState } from 'react'
import { ActivityIndicator, Text, View } from 'react-native'
import { background } from '../../assets'
import GradientButton from '../../components/GradientButton'
import MoreMenu from '../../components/MoreMenu'
import useNavigateToMusicDetails from '../../hooks/useNavigateToMusicDetails'
import Page from '../../layouts/Page'
import { Routes } from '../../navigation'
import { navigate } from '../../navigation/NavigationService'
import { useUser } from '../../providers/UserDataProvider'
import { Palette, Style, gutters } from '../../styles'
import { FONT_FAMILY } from '../../styles/Fonts'
import { getProjectLikes, LIKE_TARGET } from '../../utils/likes'
import MusicCard from './components/MusicCard'
export default function AllMyList() {
const route = useRoute();
const {
title = "Mes éléments",
scope = "music",
liked = false,
} = route?.params || {};
const route = useRoute()
const { title = 'Mes éléments', scope = 'music', liked = false } = route?.params || {}
const {
userProjects,
@@ -30,102 +26,85 @@ export default function AllMyList() {
userPlaybacks,
userLikedPlaybacks,
userLikedPlaybacksLoading,
} = useUser();
const navigateToMusicDetails = useNavigateToMusicDetails();
} = useUser()
const navigateToMusicDetails = useNavigateToMusicDetails()
const items = useMemo(() => {
if (scope === "playback") {
if (scope === 'playback') {
return Array.isArray(liked ? userLikedPlaybacks : userPlaybacks)
? liked
? userLikedPlaybacks
: userPlaybacks
: [];
: []
}
// music (projects)
return Array.isArray(liked ? userLikedProjects : userProjects)
? liked
? userLikedProjects
: userProjects
: [];
}, [
liked,
scope,
userLikedPlaybacks,
userPlaybacks,
userLikedProjects,
userProjects,
]);
: []
}, [liked, scope, userLikedPlaybacks, userPlaybacks, userLikedProjects, userProjects])
const sanitizedItems = Array.isArray(items) ? items : [];
const sanitizedItems = Array.isArray(items) ? items : []
const itemsLoading = useMemo(() => {
if (scope === "playback") {
return liked ? userLikedPlaybacksLoading : userProjectsLoading;
if (scope === 'playback') {
return liked ? userLikedPlaybacksLoading : userProjectsLoading
}
return liked ? userLikedProjectsLoading : userProjectsLoading;
}, [
liked,
scope,
userLikedPlaybacksLoading,
userLikedProjectsLoading,
userProjectsLoading,
]);
return liked ? userLikedProjectsLoading : userProjectsLoading
}, [liked, scope, userLikedPlaybacksLoading, userLikedProjectsLoading, userProjectsLoading])
const emptyStateContent = useMemo(() => {
const variant = liked ? "liked" : "default";
const scopeKey = scope === "playback" ? "playback" : "music";
const variant = liked ? 'liked' : 'default'
const scopeKey = scope === 'playback' ? 'playback' : 'music'
const content = {
music: {
default: {
title: "Aucune musique pour le moment",
title: 'Aucune musique pour le moment',
description:
"Créez un nouveau titre ou explorez la bibliothèque pour alimenter votre collection.",
'Créez un nouveau titre ou explorez la bibliothèque pour alimenter votre collection.',
cta: {
label: "Créer une musique",
label: 'Créer une musique',
action: () => navigate(Routes.WritingLyrics),
},
},
liked: {
title: "Aucune musique likée pour le moment",
description:
"Likez les morceaux qui vous inspirent pour les garder à portée de main.",
title: 'Aucune musique likée pour le moment',
description: 'Likez les morceaux qui vous inspirent pour les garder à portée de main.',
cta: {
label: "Explorer la bibliothèque",
label: 'Explorer la bibliothèque',
action: () => navigate(Routes.Research),
},
},
},
playback: {
default: {
title: "Aucun playback pour le moment",
description:
"Enregistrez ou importez un playback pour le retrouver facilement ici.",
title: 'Aucun playback pour le moment',
description: 'Enregistrez ou importez un playback pour le retrouver facilement ici.',
cta: {
label: "Créer un playback",
label: 'Créer un playback',
action: () => navigate(Routes.PlaybackOnboarding),
},
},
liked: {
title: "Aucun playback liké pour le moment",
title: 'Aucun playback liké pour le moment',
description:
"Ajoutez vos playback favoris à vos likes pour les retrouver instantanément.",
'Ajoutez vos playback favoris à vos likes pour les retrouver instantanément.',
cta: {
label: "Voir les playback",
label: 'Voir les playback',
action: () => navigate(Routes.Playbacks),
},
},
},
};
}
return content[scopeKey][variant];
}, [liked, scope]);
return content[scopeKey][variant]
}, [liked, scope])
const loadingMessage = useMemo(() => {
const target = scope === "playback" ? "playback" : "musiques";
return liked
? `Chargement des ${target} likés…`
: `Chargement des ${target}`;
}, [liked, scope]);
const target = scope === 'playback' ? 'playback' : 'musiques'
return liked ? `Chargement des ${target} likés…` : `Chargement des ${target}`
}, [liked, scope])
const placeholderBaseStyle = {
gap: 16,
@@ -133,40 +112,40 @@ export default function AllMyList() {
paddingHorizontal: 32,
backgroundColor: Palette.glass,
borderRadius: 16,
alignSelf: "stretch",
};
alignSelf: 'stretch',
}
const placeholderTitleStyle = {
fontSize: 20,
color: Palette.white,
fontFamily: FONT_FAMILY.InterSemiBold,
textAlign: "center",
};
textAlign: 'center',
}
const placeholderDescriptionStyle = {
fontSize: 14,
color: Palette.white,
fontFamily: FONT_FAMILY.InterRegular,
opacity: 0.8,
textAlign: "center",
textAlign: 'center',
maxWidth: 420,
};
}
const [menuPosition, setMenuPosition] = useState(null);
const [showMenu, setShowMenu] = useState(false);
const [selectedProjectId, setSelectedProjectId] = useState(null);
const [menuPosition, setMenuPosition] = useState(null)
const [showMenu, setShowMenu] = useState(false)
const [selectedProjectId, setSelectedProjectId] = useState(null)
const onPressItem = (item) => {
if (scope === "playback") {
navigate(Routes.Playbacks, { projectId: item.id });
if (scope === 'playback') {
navigate(Routes.Playbacks, { projectId: item.id })
} else {
navigateToMusicDetails({
projectId: item.id,
songUrl: item?.songUrl,
project: item,
});
})
}
};
}
return (
<Page
@@ -184,12 +163,11 @@ export default function AllMyList() {
</View>
) : sanitizedItems.length > 0 ? (
sanitizedItems.map((item) => {
const likeTarget =
scope === "playback" ? LIKE_TARGET.PLAYBACK : LIKE_TARGET.SONG;
const likeTarget = scope === 'playback' ? LIKE_TARGET.PLAYBACK : LIKE_TARGET.SONG
return (
<MusicCard
key={item.id}
title={item?.title || "Sans titre"}
title={item?.title || 'Sans titre'}
imageUri={item?.coverUrl || null}
subtitle={item?.userName}
projectId={item?.id}
@@ -197,30 +175,23 @@ export default function AllMyList() {
likeTarget={likeTarget}
onPress={() => onPressItem(item)}
onPressMore={(posTop) => {
setSelectedProjectId(item.id);
setMenuPosition(posTop);
setShowMenu(
(prev) =>
!prev || posTop?.top !== (menuPosition?.top ?? null)
);
setSelectedProjectId(item.id)
setMenuPosition(posTop)
setShowMenu((prev) => !prev || posTop?.top !== (menuPosition?.top ?? null))
}}
/>
);
)
})
) : (
<View style={[Style.containerCenter, placeholderBaseStyle]}>
<Text style={placeholderTitleStyle}>
{emptyStateContent.title}
</Text>
<Text style={placeholderDescriptionStyle}>
{emptyStateContent.description}
</Text>
<Text style={placeholderTitleStyle}>{emptyStateContent.title}</Text>
<Text style={placeholderDescriptionStyle}>{emptyStateContent.description}</Text>
{emptyStateContent?.cta ? (
<GradientButton
title={emptyStateContent.cta.label}
onPress={emptyStateContent.cta.action}
containerStyle={{
alignSelf: "center",
alignSelf: 'center',
minWidth: 200,
}}
/>
@@ -238,5 +209,5 @@ export default function AllMyList() {
</View>
</View>
</Page>
);
)
}
+23 -31
View File
@@ -1,17 +1,17 @@
import { BlurView } from "expo-blur";
import React from "react";
import { Image, Platform, Pressable, Text, View } from "react-native";
import { SheetManager } from "react-native-actions-sheet";
import { background, icons } from "../../assets";
import Page from "../../layouts/Page";
import { Routes } from "../../navigation";
import { navigate } from "../../navigation/NavigationService";
import { useUser } from "../../providers/UserDataProvider";
import { gutters, Palette, Style } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import { size } from "../../styles/Style";
import { BlurView } from 'expo-blur'
import React from 'react'
import { Image, Platform, Pressable, Text, View } from 'react-native'
import { SheetManager } from 'react-native-actions-sheet'
import { background, icons } from '../../assets'
import Page from '../../layouts/Page'
import { Routes } from '../../navigation'
import { navigate } from '../../navigation/NavigationService'
import { useUser } from '../../providers/UserDataProvider'
import { gutters, Palette, Style } from '../../styles'
import { FONT_FAMILY } from '../../styles/Fonts'
import { size } from '../../styles/Style'
const AllMyPlaylist = () => {
const { userPlaylists = [] } = useUser();
const { userPlaylists = [] } = useUser()
return (
<Page
headerType="NAVIGATION"
@@ -19,15 +19,9 @@ const AllMyPlaylist = () => {
title="Mes Playlists"
rightComponent={() => (
<Pressable
onPress={() =>
SheetManager.show("Playlist", { payload: { startAtCreate: true } })
}
onPress={() => SheetManager.show('Playlist', { payload: { startAtCreate: true } })}
>
<Image
source={icons.add}
style={size({ size: 20 })}
resizeMode="contain"
/>
<Image source={icons.add} style={size({ size: 20 })} resizeMode="contain" />
</Pressable>
)}
contentContainerStyle={{
@@ -39,13 +33,11 @@ const AllMyPlaylist = () => {
{(Array.isArray(userPlaylists) ? userPlaylists : []).map((item) => (
<Pressable
key={item?.id}
onPress={() =>
navigate(Routes.PlaylistDetails, { playlistId: item?.id })
}
onPress={() => navigate(Routes.PlaylistDetails, { playlistId: item?.id })}
>
<View style={{ borderRadius: 12, overflow: "hidden" }}>
<View style={{ borderRadius: 12, overflow: 'hidden' }}>
<BlurView
intensity={Platform.OS !== "ios" ? 10 : 20}
intensity={Platform.OS !== 'ios' ? 10 : 20}
style={{
...Style.containerSpaceBetween,
minHeight: 59,
@@ -66,14 +58,14 @@ const AllMyPlaylist = () => {
flexShrink: 1,
}}
>
{item?.name || "Sans nom"}
{item?.name || 'Sans nom'}
</Text>
<Pressable>
<Image
source={icons.chevronDown}
style={{
...size({ size: 15 }),
transform: [{ rotate: "-90deg" }],
transform: [{ rotate: '-90deg' }],
}}
resizeMode="contain"
/>
@@ -85,7 +77,7 @@ const AllMyPlaylist = () => {
</View>
</View>
</Page>
);
};
)
}
export default AllMyPlaylist;
export default AllMyPlaylist
+113 -133
View File
@@ -1,37 +1,35 @@
import { BlurView } from "expo-blur";
import React, { useCallback, useMemo, useState } from "react";
import { Image, Pressable, Text, View } from "react-native";
import { SheetManager } from "react-native-actions-sheet";
import { useGlobal } from "reactn";
import { background, icons } from "../../assets";
import MoreMenu from "../../components/MoreMenu";
import { playlistsRef, projectsRef } from "../../config/firebase";
import useDataFromArrayDocId from "../../hooks/useDataFromArrayId";
import useDataFromRef from "../../hooks/useDataFromRef";
import useNavigateToMusicDetails from "../../hooks/useNavigateToMusicDetails";
import usePlayer from "../../hooks/usePlayer";
import Page from "../../layouts/Page";
import { useUser } from "../../providers/UserDataProvider";
import { gutters, Palette, Style } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import { size } from "../../styles/Style";
import { getProjectLikes, LIKE_TARGET } from "../../utils/likes";
import MusicCard from "./components/MusicCard";
import { BlurView } from 'expo-blur'
import React, { useCallback, useMemo, useState } from 'react'
import { Image, Pressable, Text, View } from 'react-native'
import { SheetManager } from 'react-native-actions-sheet'
import { useGlobal } from 'reactn'
import { background, icons } from '../../assets'
import MoreMenu from '../../components/MoreMenu'
import { playlistsRef, projectsRef } from '../../config/firebase'
import useDataFromArrayDocId from '../../hooks/useDataFromArrayId'
import useDataFromRef from '../../hooks/useDataFromRef'
import useNavigateToMusicDetails from '../../hooks/useNavigateToMusicDetails'
import usePlayer from '../../hooks/usePlayer'
import Page from '../../layouts/Page'
import { useUser } from '../../providers/UserDataProvider'
import { gutters, Palette, Style } from '../../styles'
import { FONT_FAMILY } from '../../styles/Fonts'
import { size } from '../../styles/Style'
import { getProjectLikes, LIKE_TARGET } from '../../utils/likes'
import MusicCard from './components/MusicCard'
const AllMyPlaylist = ({ route }) => {
const { playListId } = route?.params || {};
const [, setTooltip] = useGlobal("_tooltip");
const { userPlaylists = [] } = useUser();
const [selectedPlaylistId, setSelectedPlaylistId] = useState(
playListId || null
);
const { playListId } = route?.params || {}
const [, setTooltip] = useGlobal('_tooltip')
const { userPlaylists = [] } = useUser()
const [selectedPlaylistId, setSelectedPlaylistId] = useState(playListId || null)
const { data: playlist } = useDataFromRef({
ref: selectedPlaylistId ? playlistsRef.doc(selectedPlaylistId) : null,
simpleRef: true,
listener: true,
condition: !!selectedPlaylistId,
refreshArray: [selectedPlaylistId],
});
})
// Fetch projects by ids from playlist.musics
const { data: musics } = useDataFromArrayDocId({
@@ -39,31 +37,31 @@ const AllMyPlaylist = ({ route }) => {
arrayId: Array.isArray(playlist?.musics) ? playlist.musics : [],
condition: Array.isArray(playlist?.musics) && playlist.musics.length > 0,
refreshArray: [selectedPlaylistId, playlist?.musics?.length || 0],
});
const [menuPosition, setMenuPosition] = useState(null);
const [showMenu, setShowMenu] = useState(false);
const [selectedProjectId, setSelectedProjectId] = useState(null);
const navigateToMusicDetails = useNavigateToMusicDetails();
const { setQueue } = usePlayer() || {};
})
const [menuPosition, setMenuPosition] = useState(null)
const [showMenu, setShowMenu] = useState(false)
const [selectedProjectId, setSelectedProjectId] = useState(null)
const navigateToMusicDetails = useNavigateToMusicDetails()
const { setQueue } = usePlayer() || {}
const playlistQueueItems = useMemo(() => {
if (!Array.isArray(musics)) return [];
if (!Array.isArray(musics)) return []
return musics
.map((project) => {
const projectId =
typeof project?.id === "string" ? project.id : project?.projectId ?? null;
typeof project?.id === 'string' ? project.id : (project?.projectId ?? null)
const songUrl =
typeof project?.songUrl === "string" && project.songUrl.length > 0
typeof project?.songUrl === 'string' && project.songUrl.length > 0
? project.songUrl
: null;
if (!songUrl) return null;
const descriptorId = projectId ? `project-${projectId}` : songUrl;
: null
if (!songUrl) return null
const descriptorId = projectId ? `project-${projectId}` : songUrl
return {
id: descriptorId,
uri: songUrl,
songUrl,
title: project?.title || "Sans titre",
artist: project?.userName || "",
title: project?.title || 'Sans titre',
artist: project?.userName || '',
artwork: project?.coverUrl ?? null,
coverUrl: project?.coverUrl ?? null,
metadata: {
@@ -76,73 +74,67 @@ const AllMyPlaylist = ({ route }) => {
playlistId: selectedPlaylistId ?? null,
playlistName: playlist?.name ?? null,
},
};
}
})
.filter(Boolean);
}, [musics, playlist?.name, selectedPlaylistId]);
.filter(Boolean)
}, [musics, playlist?.name, selectedPlaylistId])
const handlePlayFromPlaylist = useCallback(
(project) => {
if (!project?.id) return;
if (!project?.id) return
if (Array.isArray(playlistQueueItems) && playlistQueueItems.length > 0) {
const queueIndex = playlistQueueItems.findIndex(
(item) => item?.metadata?.projectId === project.id
);
if (queueIndex >= 0 && typeof setQueue === "function") {
)
if (queueIndex >= 0 && typeof setQueue === 'function') {
setQueue({
items: playlistQueueItems,
id: selectedPlaylistId,
type: "playlist",
type: 'playlist',
name: playlist?.name ?? null,
index: queueIndex,
});
})
}
}
navigateToMusicDetails({
projectId: project.id,
songUrl: project?.songUrl,
project,
});
})
},
[
navigateToMusicDetails,
playlist?.name,
playlistQueueItems,
selectedPlaylistId,
setQueue,
]
);
[navigateToMusicDetails, playlist?.name, playlistQueueItems, selectedPlaylistId, setQueue]
)
// delete playlist
const confirmDelete = () => {
SheetManager.show("Delete", {
SheetManager.show('Delete', {
payload: {
title: "Supprimer la playlist",
message: "Es-tu sûr de vouloir supprimer cette playlist ?",
title: 'Supprimer la playlist',
message: 'Es-tu sûr de vouloir supprimer cette playlist ?',
onConfirm: async () => {
try {
if (selectedPlaylistId) {
await playlistsRef.doc(selectedPlaylistId).delete();
setTooltip({ type: "success", text: "Playlist supprimée" });
await playlistsRef.doc(selectedPlaylistId).delete()
setTooltip({ type: 'success', text: 'Playlist supprimée' })
}
} catch (e) {
console.log("Delete playlist error", e?.message);
console.log('Delete playlist error', e?.message)
setTooltip({
type: "error",
text: e?.message || "Suppression impossible",
});
type: 'error',
text: e?.message || 'Suppression impossible',
})
}
},
},
});
};
})
}
return (
<Page
headerType="NAVIGATION"
backgroundImg={background.libraryBG}
title="Mes Playlists"
width={"80%"}
width={'80%'}
contentContainerStyle={{
paddingBottom: gutters * 2,
}}
@@ -156,7 +148,7 @@ const AllMyPlaylist = ({ route }) => {
</>
)}
>
<View style={{ flexDirection: "row", gap: 20 }}>
<View style={{ flexDirection: 'row', gap: 20 }}>
<View
style={{
flex: 1,
@@ -168,67 +160,62 @@ const AllMyPlaylist = ({ route }) => {
>
<View style={{ flex: 1, gap: 12 }}>
<View style={{ gap: 8 }}>
{(Array.isArray(userPlaylists) ? userPlaylists : []).map(
(item) => {
const isSelected = selectedPlaylistId === item?.id;
{(Array.isArray(userPlaylists) ? userPlaylists : []).map((item) => {
const isSelected = selectedPlaylistId === item?.id
return (
<Pressable
key={item?.id}
onPress={() => setSelectedPlaylistId(item?.id)}
return (
<Pressable key={item?.id} onPress={() => setSelectedPlaylistId(item?.id)}>
<View
style={{
borderRadius: 14,
overflow: 'hidden',
borderWidth: 1,
borderColor: Palette.transparentWhite,
transform: [{ scaleX: isSelected ? 1.04 : 1 }],
}}
>
<View
<BlurView
intensity={30}
style={{
borderRadius: 14,
overflow: "hidden",
borderWidth: 1,
borderColor: Palette.transparentWhite,
transform: [{ scaleX: isSelected ? 1.04 : 1 }],
...Style.containerSpaceBetween,
minHeight: 59,
paddingHorizontal: 16,
paddingVertical: 12,
}}
>
<BlurView
intensity={30}
<Text
style={{
...Style.containerSpaceBetween,
minHeight: 59,
paddingHorizontal: 16,
paddingVertical: 12,
flex: 1,
fontSize: isSelected ? 18 : 16,
color: Palette.white,
fontFamily: isSelected
? FONT_FAMILY.InterSemiBold
: FONT_FAMILY.InterRegular,
marginRight: 12,
flexShrink: 1,
}}
>
<Text
{item?.name || 'Sans nom'}
</Text>
<Pressable>
<Image
source={icons.chevronDown}
style={{
flex: 1,
fontSize: isSelected ? 18 : 16,
color: Palette.white,
fontFamily: isSelected
? FONT_FAMILY.InterSemiBold
: FONT_FAMILY.InterRegular,
marginRight: 12,
flexShrink: 1,
...size({ size: 15 }),
transform: [{ rotate: '-90deg' }],
}}
>
{item?.name || "Sans nom"}
</Text>
<Pressable>
<Image
source={icons.chevronDown}
style={{
...size({ size: 15 }),
transform: [{ rotate: "-90deg" }],
}}
resizeMode="contain"
/>
</Pressable>
</BlurView>
</View>
</Pressable>
);
}
)}
resizeMode="contain"
/>
</Pressable>
</BlurView>
</View>
</Pressable>
)
})}
</View>
<Pressable
onPress={() =>
SheetManager.show("Playlist", {
SheetManager.show('Playlist', {
payload: { startAtCreate: true },
})
}
@@ -236,7 +223,7 @@ const AllMyPlaylist = ({ route }) => {
<View
style={{
borderRadius: 12,
overflow: "hidden",
overflow: 'hidden',
borderWidth: 1,
borderColor: Palette.transparentWhite,
}}
@@ -259,11 +246,7 @@ const AllMyPlaylist = ({ route }) => {
>
Ajouter une playlist
</Text>
<Image
source={icons.add}
style={size({ size: 20 })}
resizeMode="contain"
/>
<Image source={icons.add} style={size({ size: 20 })} resizeMode="contain" />
</BlurView>
</View>
</Pressable>
@@ -282,12 +265,9 @@ const AllMyPlaylist = ({ route }) => {
likeTarget={LIKE_TARGET.SONG}
onPress={() => handlePlayFromPlaylist(p)}
onPressMore={(posTop) => {
setSelectedProjectId(p.id);
setMenuPosition(posTop);
setShowMenu(
(prev) =>
!prev || posTop?.top !== (menuPosition?.top ?? null)
);
setSelectedProjectId(p.id)
setMenuPosition(posTop)
setShowMenu((prev) => !prev || posTop?.top !== (menuPosition?.top ?? null))
}}
/>
))}
@@ -304,7 +284,7 @@ const AllMyPlaylist = ({ route }) => {
</View>
</View>
</Page>
);
};
)
}
export default AllMyPlaylist;
export default AllMyPlaylist
+44 -54
View File
@@ -1,31 +1,24 @@
import { BlurView } from "expo-blur";
import React from "react";
import {
Image,
Platform,
Pressable,
ScrollView,
Text,
View,
} from "react-native";
import { responsiveHeight } from "react-native-responsive-dimensions";
import { background, icons } from "../../assets";
import SearchBar from "../../components/SearchBar";
import MobileCoinBadge from "../../components/MobileCoinBadge";
import ShareBtn from "../../components/ShareBtn/ShareBtn";
import Page from "../../layouts/Page";
import { Routes } from "../../navigation";
import { navigate } from "../../navigation/NavigationService";
import { useUser } from "../../providers/UserDataProvider";
import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import BackTracks from "./components/BackTracks";
import LikedClips from "./components/LikedClips";
import LikedMusic from "./components/LikedMusic";
import LikedPlayback from "./components/LikedPlayback";
import MyClips from "./components/MyClips";
import MyMusic from "./components/MyMusic";
import MyPlaylist from "./components/MyPlaylist";
import { BlurView } from 'expo-blur'
import React from 'react'
import { Image, Platform, Pressable, ScrollView, Text, View } from 'react-native'
import { responsiveHeight } from 'react-native-responsive-dimensions'
import { background, icons } from '../../assets'
import SearchBar from '../../components/SearchBar'
import MobileCoinBadge from '../../components/MobileCoinBadge'
import ShareBtn from '../../components/ShareBtn/ShareBtn'
import Page from '../../layouts/Page'
import { Routes } from '../../navigation'
import { navigate } from '../../navigation/NavigationService'
import { useUser } from '../../providers/UserDataProvider'
import { Palette } from '../../styles'
import { FONT_FAMILY } from '../../styles/Fonts'
import BackTracks from './components/BackTracks'
import LikedClips from './components/LikedClips'
import LikedMusic from './components/LikedMusic'
import LikedPlayback from './components/LikedPlayback'
import MyClips from './components/MyClips'
import MyMusic from './components/MyMusic'
import MyPlaylist from './components/MyPlaylist'
const Library = () => {
const {
@@ -34,19 +27,16 @@ const Library = () => {
userLikedProjects = [],
userLikedPlaybacks = [],
userPlaylists = [],
} = useUser() || {};
} = useUser() || {}
const hasMyMusic = Array.isArray(userProjects) && userProjects.length > 0;
const hasBackTracks =
Array.isArray(userPlaybacks) && userPlaybacks.length > 0;
const hasLikedMusic =
Array.isArray(userLikedProjects) && userLikedProjects.length > 0;
const hasLikedPlayback =
Array.isArray(userLikedPlaybacks) && userLikedPlaybacks.length > 0;
const hasPlaylists = Array.isArray(userPlaylists) && userPlaylists.length > 0;
const hasMyMusic = Array.isArray(userProjects) && userProjects.length > 0
const hasBackTracks = Array.isArray(userPlaybacks) && userPlaybacks.length > 0
const hasLikedMusic = Array.isArray(userLikedProjects) && userLikedProjects.length > 0
const hasLikedPlayback = Array.isArray(userLikedPlaybacks) && userLikedPlaybacks.length > 0
const hasPlaylists = Array.isArray(userPlaylists) && userPlaylists.length > 0
const hasMyClips = false; // À modifier quand les vraies données seront disponibles
const hasLikedClips = false; // À modifier quand les vraies données seront disponibles
const hasMyClips = false // À modifier quand les vraies données seront disponibles
const hasLikedClips = false // À modifier quand les vraies données seront disponibles
const hasAnySection =
hasMyMusic ||
@@ -55,17 +45,17 @@ const Library = () => {
hasLikedPlayback ||
hasPlaylists ||
hasMyClips ||
hasLikedClips;
hasLikedClips
return (
<Page backgroundImg={background.libraryBG} headerType="NONE">
{Platform.OS !== "web" ? (
{Platform.OS !== 'web' ? (
<View
style={{
width: "100%",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
width: '100%',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
gap: 12,
marginBottom: 6,
}}
@@ -77,7 +67,7 @@ const Library = () => {
<View style={{ gap: 17, paddingBottom: 20 }}>
<Image
source={icons.musicLandLogo}
style={{ alignSelf: "center", height: 150, resizeMode: "contain" }}
style={{ alignSelf: 'center', height: 150, resizeMode: 'contain' }}
/>
<Pressable onPress={() => navigate(Routes.Research)}>
<SearchBar
@@ -94,14 +84,14 @@ const Library = () => {
flexGrow: 1,
gap: 20,
paddingBottom: responsiveHeight(20),
paddingTop: Platform.OS !== "android" ? 20 : 0,
paddingTop: Platform.OS !== 'android' ? 20 : 0,
}}
showsVerticalScrollIndicator={false}
>
<BlurView
intensity={20}
experimentalBlurMethod="dimezisBlurView"
style={{ borderRadius: 12, padding: 12, overflow: "hidden" }}
style={{ borderRadius: 12, padding: 12, overflow: 'hidden' }}
>
<MyMusic />
</BlurView>
@@ -116,8 +106,8 @@ const Library = () => {
<View
style={{
flex: 1,
alignItems: "center",
justifyContent: "center",
alignItems: 'center',
justifyContent: 'center',
paddingTop: 40,
}}
>
@@ -127,7 +117,7 @@ const Library = () => {
color: Palette.white,
opacity: 0.8,
fontFamily: FONT_FAMILY.InterRegular,
textAlign: "center",
textAlign: 'center',
}}
>
Rien n'est disponible encore ici
@@ -137,7 +127,7 @@ const Library = () => {
</ScrollView>
</View>
</Page>
);
};
)
}
export default Library;
export default Library
+83 -107
View File
@@ -1,27 +1,21 @@
import { BlurView } from "expo-blur";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { Image, Platform, ScrollView, StyleSheet, Text, View } from "react-native";
import { responsiveHeight } from "react-native-responsive-dimensions";
import { background, icons } from "../../assets";
import SearchBar from "../../components/SearchBar";
import useSearch from "../../hooks/useSearch";
import Page from "../../layouts/Page";
import { useUser } from "../../providers/UserDataProvider";
import { Palette, Style } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import BackTracks from "./components/BackTracks";
import LikedMusic from "./components/LikedMusic";
import LikedPlayback from "./components/LikedPlayback";
import MyMusic from "./components/MyMusic";
import MyPlaylist from "./components/MyPlaylist";
import ResearchHeader from "./components/ResearchHeader";
import SearchResultsList from "./components/SearchResultsList";
import { BlurView } from 'expo-blur'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Image, Platform, ScrollView, StyleSheet, Text, View } from 'react-native'
import { responsiveHeight } from 'react-native-responsive-dimensions'
import { background, icons } from '../../assets'
import SearchBar from '../../components/SearchBar'
import useSearch from '../../hooks/useSearch'
import Page from '../../layouts/Page'
import { useUser } from '../../providers/UserDataProvider'
import { Palette, Style } from '../../styles'
import { FONT_FAMILY } from '../../styles/Fonts'
import BackTracks from './components/BackTracks'
import LikedMusic from './components/LikedMusic'
import LikedPlayback from './components/LikedPlayback'
import MyMusic from './components/MyMusic'
import MyPlaylist from './components/MyPlaylist'
import ResearchHeader from './components/ResearchHeader'
import SearchResultsList from './components/SearchResultsList'
const Library = () => {
const {
userProjects = [],
@@ -29,19 +23,16 @@ const Library = () => {
userLikedProjects = [],
userLikedPlaybacks = [],
userPlaylists = [],
} = useUser() || {};
} = useUser() || {}
const hasMyMusic = Array.isArray(userProjects) && userProjects.length > 0;
const hasBackTracks =
Array.isArray(userPlaybacks) && userPlaybacks.length > 0;
const hasLikedMusic =
Array.isArray(userLikedProjects) && userLikedProjects.length > 0;
const hasLikedPlayback =
Array.isArray(userLikedPlaybacks) && userLikedPlaybacks.length > 0;
const hasPlaylists = Array.isArray(userPlaylists) && userPlaylists.length > 0;
const hasMyMusic = Array.isArray(userProjects) && userProjects.length > 0
const hasBackTracks = Array.isArray(userPlaybacks) && userPlaybacks.length > 0
const hasLikedMusic = Array.isArray(userLikedProjects) && userLikedProjects.length > 0
const hasLikedPlayback = Array.isArray(userLikedPlaybacks) && userLikedPlaybacks.length > 0
const hasPlaylists = Array.isArray(userPlaylists) && userPlaylists.length > 0
const hasMyClips = false; // À modifier quand les vraies données seront disponibles
const hasLikedClips = false; // À modifier quand les vraies données seront disponibles
const hasMyClips = false // À modifier quand les vraies données seront disponibles
const hasLikedClips = false // À modifier quand les vraies données seront disponibles
const hasAnySection =
hasMyMusic ||
@@ -50,7 +41,7 @@ const Library = () => {
hasLikedPlayback ||
hasPlaylists ||
hasMyClips ||
hasLikedClips;
hasLikedClips
const {
search,
@@ -61,96 +52,81 @@ const Library = () => {
musics = [],
playbacks = [],
loading,
} = useSearch();
} = useSearch()
const toggleSelected = useCallback(
(value) => {
setSelected((previous) => (previous === value ? null : value));
setSelected((previous) => (previous === value ? null : value))
},
[setSelected]
);
)
const musicResults = useMemo(
() => (Array.isArray(musics) ? musics : []),
[musics]
);
const musicResults = useMemo(() => (Array.isArray(musics) ? musics : []), [musics])
const playbackResults = useMemo(
() => (Array.isArray(playbacks) ? playbacks : []),
[playbacks]
);
const playbackResults = useMemo(() => (Array.isArray(playbacks) ? playbacks : []), [playbacks])
const userResults = useMemo(
() => (Array.isArray(users) ? users : []),
[users]
);
const userResults = useMemo(() => (Array.isArray(users) ? users : []), [users])
const musicsLoading = loading;
const playbacksLoading = loading;
const usersLoading = loading;
const musicsLoading = loading
const playbacksLoading = loading
const usersLoading = loading
const [dropdownVisible, setDropdownVisible] = useState(false);
const searchWrapperRef = useRef(null);
const hasSearchQuery = search.trim().length > 0;
const [dropdownVisible, setDropdownVisible] = useState(false)
const searchWrapperRef = useRef(null)
const hasSearchQuery = search.trim().length > 0
const closeDropdown = useCallback(() => {
setDropdownVisible(false);
setSelected(null);
setSearch("");
}, [setSelected, setSearch]);
setDropdownVisible(false)
setSelected(null)
setSearch('')
}, [setSelected, setSearch])
const handleFocus = () => {
setDropdownVisible(true);
};
setDropdownVisible(true)
}
const shouldShowResults = dropdownVisible;
const shouldBlurContent = dropdownVisible;
const shouldShowResults = dropdownVisible
const shouldBlurContent = dropdownVisible
const handleChangeText = (value) => {
setSearch(value);
setDropdownVisible(true);
};
setSearch(value)
setDropdownVisible(true)
}
useEffect(() => {
if (!dropdownVisible) return undefined;
if (!dropdownVisible) return undefined
const handleClickOutside = (event) => {
if (searchWrapperRef.current?.contains(event.target)) return;
closeDropdown();
};
if (searchWrapperRef.current?.contains(event.target)) return
closeDropdown()
}
document.addEventListener("mousedown", handleClickOutside);
document.addEventListener("touchstart", handleClickOutside);
document.addEventListener('mousedown', handleClickOutside)
document.addEventListener('touchstart', handleClickOutside)
return () => {
document.removeEventListener("mousedown", handleClickOutside);
document.removeEventListener("touchstart", handleClickOutside);
};
}, [dropdownVisible, closeDropdown]);
document.removeEventListener('mousedown', handleClickOutside)
document.removeEventListener('touchstart', handleClickOutside)
}
}, [dropdownVisible, closeDropdown])
return (
<Page
backgroundImg={background.libraryBgWeb}
headerType="NONE"
width={"80%"}
>
<View
style={{ gap: 17, paddingBottom: 20, zIndex: dropdownVisible ? 40 : 1 }}
>
<Page backgroundImg={background.libraryBgWeb} headerType="NONE" width={'80%'}>
<View style={{ gap: 17, paddingBottom: 20, zIndex: dropdownVisible ? 40 : 1 }}>
<Image
source={icons.musicLandLogo}
style={{ alignSelf: "center", height: 150, resizeMode: "contain" }}
style={{ alignSelf: 'center', height: 150, resizeMode: 'contain' }}
/>
<View
ref={searchWrapperRef}
style={{
position: "relative",
alignSelf: "center",
width: "65%",
position: 'relative',
alignSelf: 'center',
width: '65%',
maxWidth: 520,
minWidth: 360,
zIndex: 40,
overflow: "visible",
overflow: 'visible',
}}
>
<SearchBar
@@ -164,12 +140,12 @@ const Library = () => {
{shouldShowResults && (
<View
style={{
position: "absolute",
position: 'absolute',
top: 60,
left: 0,
right: 0,
zIndex: 50,
width: "100%",
width: '100%',
}}
>
<View
@@ -179,7 +155,7 @@ const Library = () => {
padding: 16,
backgroundColor: Palette.ultraLightWhite,
...Style.defaultBorder,
width: "100%",
width: '100%',
zIndex: 50,
elevation: 12,
}}
@@ -205,7 +181,7 @@ const Library = () => {
)}
</View>
</View>
<View style={{ flex: 1, position: "relative" }}>
<View style={{ flex: 1, position: 'relative' }}>
{shouldBlurContent && (
<BlurView
intensity={35}
@@ -215,8 +191,8 @@ const Library = () => {
{
zIndex: 10,
borderRadius: 18,
backgroundColor: "rgba(0, 0, 0, 0.25)",
overflow: "hidden",
backgroundColor: 'rgba(0, 0, 0, 0.25)',
overflow: 'hidden',
},
]}
/>
@@ -226,15 +202,15 @@ const Library = () => {
flexGrow: 1,
gap: 20,
paddingBottom: responsiveHeight(20),
paddingTop: Platform.OS !== "android" ? 20 : 0,
paddingTop: Platform.OS !== 'android' ? 20 : 0,
}}
showsVerticalScrollIndicator={false}
style={{ flex: 1, position: "relative", zIndex: 5 }}
style={{ flex: 1, position: 'relative', zIndex: 5 }}
>
<BlurView
intensity={40}
style={{
flexDirection: "row",
flexDirection: 'row',
gap: 20,
padding: 10,
borderRadius: 12,
@@ -252,7 +228,7 @@ const Library = () => {
<BlurView
style={{
flexDirection: "row",
flexDirection: 'row',
gap: 20,
padding: 10,
borderRadius: 12,
@@ -275,8 +251,8 @@ const Library = () => {
<View
style={{
flex: 1,
alignItems: "center",
justifyContent: "center",
alignItems: 'center',
justifyContent: 'center',
paddingTop: 40,
}}
>
@@ -286,7 +262,7 @@ const Library = () => {
color: Palette.white,
opacity: 0.8,
fontFamily: FONT_FAMILY.InterRegular,
textAlign: "center",
textAlign: 'center',
}}
>
Rien n'est disponible encore ici
@@ -296,7 +272,7 @@ const Library = () => {
</ScrollView>
</View>
</Page>
);
};
)
}
export default Library;
export default Library
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+65 -73
View File
@@ -1,25 +1,25 @@
import { useRoute } from "@react-navigation/native";
import React, { useCallback, useMemo, useState } from "react";
import { Image, Pressable, View } from "react-native";
import { SheetManager } from "react-native-actions-sheet";
import { useDataFromRef } from "react-native-minuit/src/hooks";
import useDataFromArrayId from "react-native-minuit/src/hooks/useDataFromArrayId";
import { useGlobal } from "reactn";
import { background, icons } from "../../assets";
import MoreMenu from "../../components/MoreMenu";
import { playlistsRef, projectsRef } from "../../config/firebase";
import useNavigateToMusicDetails from "../../hooks/useNavigateToMusicDetails";
import usePlayer from "../../hooks/usePlayer";
import Page from "../../layouts/Page";
import { goBack } from "../../navigation/NavigationService";
import { gutters } from "../../styles";
import { getProjectLikes, LIKE_TARGET } from "../../utils/likes";
import MusicCard from "./components/MusicCard";
import { useRoute } from '@react-navigation/native'
import React, { useCallback, useMemo, useState } from 'react'
import { Image, Pressable, View } from 'react-native'
import { SheetManager } from 'react-native-actions-sheet'
import { useDataFromRef } from 'react-native-minuit/src/hooks'
import useDataFromArrayId from 'react-native-minuit/src/hooks/useDataFromArrayId'
import { useGlobal } from 'reactn'
import { background, icons } from '../../assets'
import MoreMenu from '../../components/MoreMenu'
import { playlistsRef, projectsRef } from '../../config/firebase'
import useNavigateToMusicDetails from '../../hooks/useNavigateToMusicDetails'
import usePlayer from '../../hooks/usePlayer'
import Page from '../../layouts/Page'
import { goBack } from '../../navigation/NavigationService'
import { gutters } from '../../styles'
import { getProjectLikes, LIKE_TARGET } from '../../utils/likes'
import MusicCard from './components/MusicCard'
const PlaylistDetails = () => {
const [, setTooltip] = useGlobal("_tooltip");
const route = useRoute();
const playlistId = route?.params?.playlistId;
const [, setTooltip] = useGlobal('_tooltip')
const route = useRoute()
const playlistId = route?.params?.playlistId
// Fetch the playlist document
const { data: playlist } = useDataFromRef({
@@ -28,7 +28,7 @@ const PlaylistDetails = () => {
listener: true,
condition: !!playlistId,
refreshArray: [playlistId],
});
})
// Fetch projects by ids from playlist.musics
const { data: musics } = useDataFromArrayId({
@@ -36,32 +36,32 @@ const PlaylistDetails = () => {
arrayId: Array.isArray(playlist?.musics) ? playlist.musics : [],
condition: Array.isArray(playlist?.musics) && playlist.musics.length > 0,
refreshArray: [playlistId, playlist?.musics?.length || 0],
});
})
const [menuPosition, setMenuPosition] = useState(null);
const [showMenu, setShowMenu] = useState(false);
const [selectedProjectId, setSelectedProjectId] = useState(null);
const navigateToMusicDetails = useNavigateToMusicDetails();
const { setQueue } = usePlayer() || {};
const [menuPosition, setMenuPosition] = useState(null)
const [showMenu, setShowMenu] = useState(false)
const [selectedProjectId, setSelectedProjectId] = useState(null)
const navigateToMusicDetails = useNavigateToMusicDetails()
const { setQueue } = usePlayer() || {}
const playlistQueueItems = useMemo(() => {
if (!Array.isArray(musics)) return [];
if (!Array.isArray(musics)) return []
return musics
.map((project) => {
const projectId =
typeof project?.id === "string" ? project.id : project?.projectId ?? null;
typeof project?.id === 'string' ? project.id : (project?.projectId ?? null)
const songUrl =
typeof project?.songUrl === "string" && project.songUrl.length > 0
typeof project?.songUrl === 'string' && project.songUrl.length > 0
? project.songUrl
: null;
if (!songUrl) return null;
const descriptorId = projectId ? `project-${projectId}` : songUrl;
: null
if (!songUrl) return null
const descriptorId = projectId ? `project-${projectId}` : songUrl
return {
id: descriptorId,
uri: songUrl,
songUrl,
title: project?.title || "Sans titre",
artist: project?.userName || "",
title: project?.title || 'Sans titre',
artist: project?.userName || '',
artwork: project?.coverUrl ?? null,
coverUrl: project?.coverUrl ?? null,
metadata: {
@@ -74,73 +74,67 @@ const PlaylistDetails = () => {
playlistId: playlistId ?? null,
playlistName: playlist?.name ?? null,
},
};
}
})
.filter(Boolean);
}, [musics, playlist?.name, playlistId]);
.filter(Boolean)
}, [musics, playlist?.name, playlistId])
const handlePlayFromPlaylist = useCallback(
(project) => {
if (!project?.id) return;
if (!project?.id) return
if (Array.isArray(playlistQueueItems) && playlistQueueItems.length > 0) {
const queueIndex = playlistQueueItems.findIndex(
(item) => item?.metadata?.projectId === project.id
);
if (queueIndex >= 0 && typeof setQueue === "function") {
)
if (queueIndex >= 0 && typeof setQueue === 'function') {
setQueue({
items: playlistQueueItems,
id: playlistId,
type: "playlist",
type: 'playlist',
name: playlist?.name ?? null,
index: queueIndex,
});
})
}
}
navigateToMusicDetails({
projectId: project.id,
songUrl: project?.songUrl,
project,
});
})
},
[
navigateToMusicDetails,
playlist?.name,
playlistId,
playlistQueueItems,
setQueue,
]
);
[navigateToMusicDetails, playlist?.name, playlistId, playlistQueueItems, setQueue]
)
const confirmDelete = () => {
SheetManager.show("Delete", {
SheetManager.show('Delete', {
payload: {
title: "Supprimer la playlist",
message: "Es-tu sûr de vouloir supprimer cette playlist ?",
title: 'Supprimer la playlist',
message: 'Es-tu sûr de vouloir supprimer cette playlist ?',
onConfirm: async () => {
try {
if (playlistId) {
await playlistsRef.doc(playlistId).delete();
setTooltip({ type: "success", text: "Playlist supprimée" });
await playlistsRef.doc(playlistId).delete()
setTooltip({ type: 'success', text: 'Playlist supprimée' })
}
} catch (e) {
console.log("Delete playlist error", e?.message);
console.log('Delete playlist error', e?.message)
setTooltip({
type: "error",
text: e?.message || "Suppression impossible",
});
type: 'error',
text: e?.message || 'Suppression impossible',
})
} finally {
goBack();
goBack()
}
},
},
});
};
})
}
return (
<Page
headerType="NAVIGATION"
backgroundImg={background.libraryBG}
title={playlist?.name || "Playlist"}
title={playlist?.name || 'Playlist'}
rightComponent={() => (
<Pressable onPress={confirmDelete}>
<Image source={icons.trash} style={{ width: 24, height: 24 }} />
@@ -161,11 +155,9 @@ const PlaylistDetails = () => {
likeTarget={LIKE_TARGET.SONG}
onPress={() => handlePlayFromPlaylist(p)}
onPressMore={(posTop) => {
setSelectedProjectId(p.id);
setMenuPosition(posTop);
setShowMenu(
(prev) => !prev || posTop?.top !== (menuPosition?.top ?? null)
);
setSelectedProjectId(p.id)
setMenuPosition(posTop)
setShowMenu((prev) => !prev || posTop?.top !== (menuPosition?.top ?? null))
}}
/>
))}
@@ -181,7 +173,7 @@ const PlaylistDetails = () => {
/>
</View>
</Page>
);
};
)
}
export default PlaylistDetails;
export default PlaylistDetails
+10 -10
View File
@@ -1,10 +1,10 @@
import { playlistsRef } from "../../../config/firebase";
import { playlistsRef } from '../../../config/firebase'
export const createPlaylist = async (payload = {}) => {
try {
const createdBy = payload?.createdBy;
const name = payload?.name?.trim?.();
const musics = Array.isArray(payload?.musics) ? payload.musics : [];
const createdBy = payload?.createdBy
const name = payload?.name?.trim?.()
const musics = Array.isArray(payload?.musics) ? payload.musics : []
const doc = {
createdBy,
@@ -12,12 +12,12 @@ export const createPlaylist = async (payload = {}) => {
musics,
createdAt: new Date(),
updatedAt: new Date(),
};
}
const docRef = await playlistsRef.add(doc);
return docRef.id;
const docRef = await playlistsRef.add(doc)
return docRef.id
} catch (error) {
console.error("Error creating playlist:", error);
throw error;
console.error('Error creating playlist:', error)
throw error
}
};
}
+21 -30
View File
@@ -1,11 +1,11 @@
import React, { useCallback, useMemo } from "react";
import { View } from "react-native";
import { background } from "../../assets";
import useSearch from "../../hooks/useSearch";
import Page from "../../layouts/Page";
import { gutters } from "../../styles";
import ResearchHeader from "./components/ResearchHeader";
import SearchResultsList from "./components/SearchResultsList";
import React, { useCallback, useMemo } from 'react'
import { View } from 'react-native'
import { background } from '../../assets'
import useSearch from '../../hooks/useSearch'
import Page from '../../layouts/Page'
import { gutters } from '../../styles'
import ResearchHeader from './components/ResearchHeader'
import SearchResultsList from './components/SearchResultsList'
const Research = () => {
const {
@@ -17,33 +17,24 @@ const Research = () => {
musics = [],
playbacks = [],
loading,
} = useSearch();
} = useSearch()
const toggleSelected = useCallback(
(value) => {
setSelected((previous) => (previous === value ? null : value));
setSelected((previous) => (previous === value ? null : value))
},
[setSelected],
);
[setSelected]
)
const musicResults = useMemo(
() => (Array.isArray(musics) ? musics : []),
[musics],
);
const musicResults = useMemo(() => (Array.isArray(musics) ? musics : []), [musics])
const playbackResults = useMemo(
() => (Array.isArray(playbacks) ? playbacks : []),
[playbacks],
);
const playbackResults = useMemo(() => (Array.isArray(playbacks) ? playbacks : []), [playbacks])
const userResults = useMemo(
() => (Array.isArray(users) ? users : []),
[users],
);
const userResults = useMemo(() => (Array.isArray(users) ? users : []), [users])
const musicsLoading = loading;
const playbacksLoading = loading;
const usersLoading = loading;
const musicsLoading = loading
const playbacksLoading = loading
const usersLoading = loading
return (
<Page
@@ -72,7 +63,7 @@ const Research = () => {
/>
</View>
</Page>
);
};
)
}
export default Research;
export default Research
+31 -47
View File
@@ -1,47 +1,35 @@
import React, { useCallback, useState } from "react";
import {
FlatList,
Image,
Pressable,
Text,
View,
useWindowDimensions,
} from "react-native";
import { SheetManager } from "react-native-actions-sheet";
import GradientButton from "../../../components/GradientButton";
import useLayoutType from "../../../hooks/useLayoutType";
import { Routes } from "../../../navigation";
import { navigate } from "../../../navigation/NavigationService";
import { useUserData } from "../../../providers/UserDataProvider";
import { Palette } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
import CardContainer from "./CardContainer";
import React, { useCallback, useState } from 'react'
import { FlatList, Image, Pressable, Text, View, useWindowDimensions } from 'react-native'
import { SheetManager } from 'react-native-actions-sheet'
import GradientButton from '../../../components/GradientButton'
import useLayoutType from '../../../hooks/useLayoutType'
import { Routes } from '../../../navigation'
import { navigate } from '../../../navigation/NavigationService'
import { useUserData } from '../../../providers/UserDataProvider'
import { Palette } from '../../../styles'
import { FONT_FAMILY } from '../../../styles/Fonts'
import CardContainer from './CardContainer'
const BackTracks = () => {
const { userPlaybacks } = useUserData();
const { isWeb } = useLayoutType();
const items = Array.isArray(userPlaybacks)
? userPlaybacks.slice(0, isWeb ? 2 : 3)
: [];
const { width: screenWidth } = useWindowDimensions();
const [containerWidth, setContainerWidth] = useState(screenWidth);
const numColumns = isWeb ? 2 : 3;
const gap = 8;
const itemWidth = Math.max(
0,
Math.floor((containerWidth - gap * (numColumns - 1)) / numColumns)
);
const { userPlaybacks } = useUserData()
const { isWeb } = useLayoutType()
const items = Array.isArray(userPlaybacks) ? userPlaybacks.slice(0, isWeb ? 2 : 3) : []
const { width: screenWidth } = useWindowDimensions()
const [containerWidth, setContainerWidth] = useState(screenWidth)
const numColumns = isWeb ? 2 : 3
const gap = 8
const itemWidth = Math.max(0, Math.floor((containerWidth - gap * (numColumns - 1)) / numColumns))
const openPlaybackPicker = useCallback(() => {
SheetManager.show("PlaybackPicker");
}, []);
SheetManager.show('PlaybackPicker')
}, [])
return (
<CardContainer
label="Mes Playback"
onPress={() =>
navigate(Routes.AllMyList, {
title: "Mes playback",
scope: "playback",
title: 'Mes playback',
scope: 'playback',
liked: false,
})
}
@@ -58,15 +46,11 @@ const BackTracks = () => {
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ width: itemWidth }} key={item.id}>
<Pressable
onPress={() =>
navigate(Routes.Playbacks, { projectId: item.id })
}
>
<Pressable onPress={() => navigate(Routes.Playbacks, { projectId: item.id })}>
<Image
source={{ uri: item?.thumbnailUrl || item?.coverUrl || "" }}
source={{ uri: item?.thumbnailUrl || item?.coverUrl || '' }}
style={{
width: "100%",
width: '100%',
height: Math.round((itemWidth * 16) / 9),
borderRadius: 10,
}}
@@ -76,10 +60,10 @@ const BackTracks = () => {
)}
/>
) : (
<View style={{ flexDirection: "column", gap: 10 }}>
<View style={{ flexDirection: 'column', gap: 10 }}>
<Text
style={{
textAlign: "center",
textAlign: 'center',
fontFamily: FONT_FAMILY.InterRegular,
color: Palette.white,
fontSize: 16,
@@ -96,7 +80,7 @@ const BackTracks = () => {
)}
</View>
</CardContainer>
);
};
)
}
export default BackTracks;
export default BackTracks
+11 -11
View File
@@ -1,15 +1,15 @@
import React from "react";
import { Image, Pressable, StyleSheet, Text, View } from "react-native";
import { icons } from "../../../assets";
import { Palette, Style } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
const CardContainer = ({ children, label = "", onPress, onPressPlus }) => {
import React from 'react'
import { Image, Pressable, StyleSheet, Text, View } from 'react-native'
import { icons } from '../../../assets'
import { Palette, Style } from '../../../styles'
import { FONT_FAMILY } from '../../../styles/Fonts'
const CardContainer = ({ children, label = '', onPress, onPressPlus }) => {
return (
<View style={{ gap: 12 }}>
<View style={{ ...Style.containerSpaceBetween }}>
<Text style={styles.label}>{label}</Text>
<View style={{ flexDirection: "row", alignItems: "center", gap: 20 }}>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 20 }}>
<Pressable onPress={onPressPlus}>
<Image source={icons.add} style={{ width: 15, height: 15 }} />
</Pressable>
@@ -20,10 +20,10 @@ const CardContainer = ({ children, label = "", onPress, onPressPlus }) => {
</View>
{children}
</View>
);
};
)
}
export default CardContainer;
export default CardContainer
const styles = StyleSheet.create({
label: {
@@ -36,4 +36,4 @@ const styles = StyleSheet.create({
color: Palette.gray,
fontFamily: FONT_FAMILY.InterRegular,
},
});
})
+9 -9
View File
@@ -1,8 +1,8 @@
import { View, Text, Image } from "react-native";
import React from "react";
import CardContainer from "./CardContainer";
import { Style } from "../../../styles";
import { img } from "../../../assets";
import { View, Text, Image } from 'react-native'
import React from 'react'
import CardContainer from './CardContainer'
import { Style } from '../../../styles'
import { img } from '../../../assets'
const LikedClips = () => {
return (
@@ -12,13 +12,13 @@ const LikedClips = () => {
<View style={{ flex: 1 }} key={index}>
<Image
source={img.placeholder3}
style={{ width: "100%", height: 172, borderRadius: 10 }}
style={{ width: '100%', height: 172, borderRadius: 10 }}
/>
</View>
))}
</View>
</CardContainer>
);
};
)
}
export default LikedClips;
export default LikedClips
+40 -53
View File
@@ -1,5 +1,5 @@
import { Image as ExpoImage } from "expo-image";
import React, { useCallback, useState } from "react";
import { Image as ExpoImage } from 'expo-image'
import React, { useCallback, useState } from 'react'
import {
FlatList,
Pressable,
@@ -7,62 +7,49 @@ import {
Text,
useWindowDimensions,
View,
} from "react-native";
import { img } from "../../../assets";
import GradientButton from "../../../components/GradientButton";
import useLayoutType from "../../../hooks/useLayoutType";
import useNavigateToMusicDetails from "../../../hooks/useNavigateToMusicDetails";
import { Routes } from "../../../navigation";
import { navigate } from "../../../navigation/NavigationService";
import { useUser } from "../../../providers/UserDataProvider";
import { Palette } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
import CardContainer from "./CardContainer";
} from 'react-native'
import { img } from '../../../assets'
import GradientButton from '../../../components/GradientButton'
import useLayoutType from '../../../hooks/useLayoutType'
import useNavigateToMusicDetails from '../../../hooks/useNavigateToMusicDetails'
import { Routes } from '../../../navigation'
import { navigate } from '../../../navigation/NavigationService'
import { useUser } from '../../../providers/UserDataProvider'
import { Palette } from '../../../styles'
import { FONT_FAMILY } from '../../../styles/Fonts'
import CardContainer from './CardContainer'
const LikedMusic = () => {
const { userLikedProjects = [] } = useUser();
const items = Array.isArray(userLikedProjects)
? userLikedProjects.slice(0, 3)
: [];
const { width: screenWidth } = useWindowDimensions();
const { isWeb } = useLayoutType();
const [containerWidth, setContainerWidth] = useState(screenWidth);
const numColumns = isWeb ? 2 : 3;
const gap = 8;
const itemWidth = Math.max(
0,
Math.floor((containerWidth - gap * (numColumns - 1)) / numColumns)
);
const navigateToMusicDetails = useNavigateToMusicDetails();
const { userLikedProjects = [] } = useUser()
const items = Array.isArray(userLikedProjects) ? userLikedProjects.slice(0, 3) : []
const { width: screenWidth } = useWindowDimensions()
const { isWeb } = useLayoutType()
const [containerWidth, setContainerWidth] = useState(screenWidth)
const numColumns = isWeb ? 2 : 3
const gap = 8
const itemWidth = Math.max(0, Math.floor((containerWidth - gap * (numColumns - 1)) / numColumns))
const navigateToMusicDetails = useNavigateToMusicDetails()
const resolveCoverUri = useCallback((project) => {
if (!project) return null;
const isValid = (value) =>
typeof value === "string" && value.trim().length > 0;
if (!project) return null
const isValid = (value) => typeof value === 'string' && value.trim().length > 0
if (isValid(project?.coverUrl)) {
return project.coverUrl;
return project.coverUrl
}
const cover = project?.cover || {};
const coverCandidates = [
cover?.result,
cover?.finalUrl,
cover?.generatedBackground,
];
const cover = project?.cover || {}
const coverCandidates = [cover?.result, cover?.finalUrl, cover?.generatedBackground]
if (Array.isArray(cover?.options)) {
coverCandidates.push(
...cover.options.flatMap((option) => [
option?.finalUrl,
option?.generatedUrl,
])
);
...cover.options.flatMap((option) => [option?.finalUrl, option?.generatedUrl])
)
}
return coverCandidates.find(isValid) || null;
}, []);
return coverCandidates.find(isValid) || null
}, [])
return (
<CardContainer
label="Musiques likées"
onPress={() =>
navigate(Routes.AllMyList, {
title: "Musiques likées",
scope: "music",
title: 'Musiques likées',
scope: 'music',
liked: true,
})
}
@@ -97,7 +84,7 @@ const LikedMusic = () => {
contentFit="cover"
transition={100}
style={{
width: "100%",
width: '100%',
height: itemWidth,
borderRadius: 10,
}}
@@ -106,7 +93,7 @@ const LikedMusic = () => {
<RNImage
source={img.placeholder3}
style={{
width: "100%",
width: '100%',
height: itemWidth,
borderRadius: 10,
}}
@@ -116,10 +103,10 @@ const LikedMusic = () => {
)}
/>
) : (
<View style={{ flexDirection: "column", gap: 10 }}>
<View style={{ flexDirection: 'column', gap: 10 }}>
<Text
style={{
textAlign: "center",
textAlign: 'center',
fontFamily: FONT_FAMILY.InterRegular,
color: Palette.white,
fontSize: 16,
@@ -135,7 +122,7 @@ const LikedMusic = () => {
</View>
)}
</CardContainer>
);
};
)
}
export default LikedMusic;
export default LikedMusic
+28 -44
View File
@@ -1,41 +1,29 @@
import React, { useState } from "react";
import {
FlatList,
Image,
Pressable,
Text,
View,
useWindowDimensions,
} from "react-native";
import GradientButton from "../../../components/GradientButton";
import useLayoutType from "../../../hooks/useLayoutType";
import { Routes } from "../../../navigation";
import { navigate } from "../../../navigation/NavigationService";
import { useUserData } from "../../../providers/UserDataProvider";
import { Palette } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
import CardContainer from "./CardContainer";
import React, { useState } from 'react'
import { FlatList, Image, Pressable, Text, View, useWindowDimensions } from 'react-native'
import GradientButton from '../../../components/GradientButton'
import useLayoutType from '../../../hooks/useLayoutType'
import { Routes } from '../../../navigation'
import { navigate } from '../../../navigation/NavigationService'
import { useUserData } from '../../../providers/UserDataProvider'
import { Palette } from '../../../styles'
import { FONT_FAMILY } from '../../../styles/Fonts'
import CardContainer from './CardContainer'
const LikedPlayback = () => {
const { isWeb } = useLayoutType();
const { userLikedPlaybacks } = useUserData();
const items = Array.isArray(userLikedPlaybacks)
? userLikedPlaybacks.slice(0, isWeb ? 2 : 3)
: [];
const { width: screenWidth } = useWindowDimensions();
const [containerWidth, setContainerWidth] = useState(screenWidth);
const numColumns = isWeb ? 2 : 3;
const gap = 8;
const itemWidth = Math.max(
0,
Math.floor((containerWidth - gap * (numColumns - 1)) / numColumns)
);
const { isWeb } = useLayoutType()
const { userLikedPlaybacks } = useUserData()
const items = Array.isArray(userLikedPlaybacks) ? userLikedPlaybacks.slice(0, isWeb ? 2 : 3) : []
const { width: screenWidth } = useWindowDimensions()
const [containerWidth, setContainerWidth] = useState(screenWidth)
const numColumns = isWeb ? 2 : 3
const gap = 8
const itemWidth = Math.max(0, Math.floor((containerWidth - gap * (numColumns - 1)) / numColumns))
return (
<CardContainer
label="Playback likés"
onPress={() =>
navigate(Routes.AllMyList, {
title: "Playback likés",
scope: "playback",
title: 'Playback likés',
scope: 'playback',
liked: true,
})
}
@@ -52,15 +40,11 @@ const LikedPlayback = () => {
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ width: itemWidth }} key={item.id}>
<Pressable
onPress={() =>
navigate(Routes.Playbacks, { projectId: item.id })
}
>
<Pressable onPress={() => navigate(Routes.Playbacks, { projectId: item.id })}>
<Image
source={{ uri: item?.thumbnailUrl || item?.coverUrl || "" }}
source={{ uri: item?.thumbnailUrl || item?.coverUrl || '' }}
style={{
width: "100%",
width: '100%',
height: Math.round((itemWidth * 16) / 9),
borderRadius: 10,
}}
@@ -70,10 +54,10 @@ const LikedPlayback = () => {
)}
/>
) : (
<View style={{ flexDirection: "column", gap: 10 }}>
<View style={{ flexDirection: 'column', gap: 10 }}>
<Text
style={{
textAlign: "center",
textAlign: 'center',
fontFamily: FONT_FAMILY.InterRegular,
color: Palette.white,
fontSize: 16,
@@ -90,7 +74,7 @@ const LikedPlayback = () => {
)}
</View>
</CardContainer>
);
};
)
}
export default LikedPlayback;
export default LikedPlayback
+58 -68
View File
@@ -1,95 +1,88 @@
import { BlurView } from "expo-blur";
import { Image as ExpoImage } from "expo-image";
import React, { useEffect, useRef, useState } from "react";
import {
Dimensions,
Pressable,
Image as RNImage,
StyleSheet,
Text,
View,
} from "react-native";
import { responsiveWidth } from "react-native-responsive-dimensions";
import { useGlobal } from "reactn";
import { icons, img } from "../../../assets";
import PressableScale from "../../../components/PressableScale";
import { LIKE_TARGET, toggleProjectLike } from "../../../utils/likes";
import { ensureAuthenticated } from "../../../utils/authRedirect";
import { Palette, Style } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
import { size } from "../../../styles/Style";
import { BlurView } from 'expo-blur'
import { Image as ExpoImage } from 'expo-image'
import React, { useEffect, useRef, useState } from 'react'
import { Dimensions, Pressable, Image as RNImage, StyleSheet, Text, View } from 'react-native'
import { responsiveWidth } from 'react-native-responsive-dimensions'
import { useGlobal } from 'reactn'
import { icons, img } from '../../../assets'
import PressableScale from '../../../components/PressableScale'
import { LIKE_TARGET, toggleProjectLike } from '../../../utils/likes'
import { ensureAuthenticated } from '../../../utils/authRedirect'
import { Palette, Style } from '../../../styles'
import { FONT_FAMILY } from '../../../styles/Fonts'
import { size } from '../../../styles/Style'
const MusicCard = ({
onPress,
onPressMore,
title = "Sans titre",
subtitle = "MusicLand",
title = 'Sans titre',
subtitle = 'MusicLand',
imageUri = null,
projectId = null,
likedBy = [],
likeTarget = LIKE_TARGET.SONG,
}) => {
const [currentUID] = useGlobal("currentUID");
const [selected, setSelected] = useState(false);
const [layout, setLayout] = useState(null);
const [currentUID] = useGlobal('currentUID')
const [selected, setSelected] = useState(false)
const [layout, setLayout] = useState(null)
const [menuPos, setMenuPos] = useState({
top: 0,
right: 0,
left: 0,
width: 0,
height: 0,
});
const cardRef = useRef(null);
const moreButtonRef = useRef(null);
})
const cardRef = useRef(null)
const moreButtonRef = useRef(null)
useEffect(() => {
if (layout) {
const top = (layout?.y || 0) + 45;
setMenuPos((prev) => ({ ...prev, top }));
const top = (layout?.y || 0) + 45
setMenuPos((prev) => ({ ...prev, top }))
}
}, [layout]);
}, [layout])
useEffect(() => {
if (Array.isArray(likedBy) && currentUID) {
setSelected(likedBy.includes(currentUID));
setSelected(likedBy.includes(currentUID))
}
}, [JSON.stringify(likedBy), currentUID]);
}, [JSON.stringify(likedBy), currentUID])
const toggleLike = async () => {
if (!projectId) return;
if (!ensureAuthenticated(currentUID)) return;
const next = !selected;
setSelected(next);
if (!projectId) return
if (!ensureAuthenticated(currentUID)) return
const next = !selected
setSelected(next)
try {
await toggleProjectLike({
projectId,
target: likeTarget,
currentUID,
next,
});
})
} catch (e) {
// rollback on failure
setSelected(!next);
setSelected(!next)
}
};
}
const onPressMenu = () => {
const computeAnchor = (x = 0, y = 0, width = 0, height = 0) => {
const windowWidth = Dimensions.get("window").width || 0;
const top = (y || 0) + (height || 0) + 8;
const right = Math.max(0, windowWidth - ((x || 0) + (width || 0)));
const left = Math.max(0, x || 0);
const anchor = { top, right, left, width, height };
setMenuPos(anchor);
onPressMore?.(anchor);
};
const windowWidth = Dimensions.get('window').width || 0
const top = (y || 0) + (height || 0) + 8
const right = Math.max(0, windowWidth - ((x || 0) + (width || 0)))
const left = Math.max(0, x || 0)
const anchor = { top, right, left, width, height }
setMenuPos(anchor)
onPressMore?.(anchor)
}
if (moreButtonRef?.current?.measureInWindow) {
try {
moreButtonRef.current.measureInWindow((x, y, width, height) => {
computeAnchor(x, y, width, height);
});
return;
computeAnchor(x, y, width, height)
})
return
} catch (e) {
// fallback to card measurement below
}
@@ -99,15 +92,15 @@ const MusicCard = ({
if (cardRef?.current?.measureInWindow) {
try {
cardRef.current.measureInWindow((x, y, width, height) => {
computeAnchor(x, y, width, height);
});
return;
computeAnchor(x, y, width, height)
})
return
} catch (e) {
// fallback to relative layout position
}
}
onPressMore?.(menuPos);
};
onPressMore?.(menuPos)
}
return (
<>
@@ -130,10 +123,7 @@ const MusicCard = ({
style={{ ...size({ size: 60 }), borderRadius: 12 }}
/>
) : (
<RNImage
source={img.placeholder}
style={{ ...size({ size: 60 }), borderRadius: 12 }}
/>
<RNImage source={img.placeholder} style={{ ...size({ size: 60 }), borderRadius: 12 }} />
)}
<View style={styles.blurContainer}>
<BlurView
@@ -149,9 +139,9 @@ const MusicCard = ({
>
<View style={{ gap: 3, flexShrink: 1, paddingVertical: 11 }}>
<Text numberOfLines={2} style={styles.title}>
{title || "Sans titre"}
{title || 'Sans titre'}
</Text>
<Text style={styles.subTitle}>{subtitle || "MusicLand"}</Text>
<Text style={styles.subTitle}>{subtitle || 'MusicLand'}</Text>
</View>
<View
style={{
@@ -187,17 +177,17 @@ const MusicCard = ({
/>
)} */}
</>
);
};
)
}
export default MusicCard;
export default MusicCard
const styles = StyleSheet.create({
blurContainer: {
borderRadius: 12,
overflow: "hidden",
overflow: 'hidden',
flex: 1,
height: "100%",
height: '100%',
},
title: {
fontSize: 16,
@@ -214,4 +204,4 @@ const styles = StyleSheet.create({
filterContainer: {
backgroundColor: Palette.tran,
},
});
})
+9 -9
View File
@@ -1,8 +1,8 @@
import { View, Text, Image } from "react-native";
import React from "react";
import CardContainer from "./CardContainer";
import { Style } from "../../../styles";
import { img } from "../../../assets";
import { View, Text, Image } from 'react-native'
import React from 'react'
import CardContainer from './CardContainer'
import { Style } from '../../../styles'
import { img } from '../../../assets'
const MyClips = () => {
return (
@@ -12,13 +12,13 @@ const MyClips = () => {
<View style={{ flex: 1 }} key={index}>
<Image
source={img.placeholder3}
style={{ width: "100%", height: 172, borderRadius: 10 }}
style={{ width: '100%', height: 172, borderRadius: 10 }}
/>
</View>
))}
</View>
</CardContainer>
);
};
)
}
export default MyClips;
export default MyClips
+46 -50
View File
@@ -1,37 +1,37 @@
import React, { useState } from "react";
import { Text, View } from "react-native";
import { SheetManager } from "react-native-actions-sheet";
import { useGlobal } from "reactn";
import GradientButton from "../../../components/GradientButton";
import MoreMenu from "../../../components/MoreMenu";
import { projectsRef } from "../../../config/firebase";
import useNavigateToMusicDetails from "../../../hooks/useNavigateToMusicDetails";
import { Routes } from "../../../navigation";
import { navigate } from "../../../navigation/NavigationService";
import { useUser } from "../../../providers/UserDataProvider";
import { Palette } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
import { getProjectLikes, LIKE_TARGET } from "../../../utils/likes";
import CardContainer from "./CardContainer";
import MusicCard from "./MusicCard";
import React, { useState } from 'react'
import { Text, View } from 'react-native'
import { SheetManager } from 'react-native-actions-sheet'
import { useGlobal } from 'reactn'
import GradientButton from '../../../components/GradientButton'
import MoreMenu from '../../../components/MoreMenu'
import { projectsRef } from '../../../config/firebase'
import useNavigateToMusicDetails from '../../../hooks/useNavigateToMusicDetails'
import { Routes } from '../../../navigation'
import { navigate } from '../../../navigation/NavigationService'
import { useUser } from '../../../providers/UserDataProvider'
import { Palette } from '../../../styles'
import { FONT_FAMILY } from '../../../styles/Fonts'
import { getProjectLikes, LIKE_TARGET } from '../../../utils/likes'
import CardContainer from './CardContainer'
import MusicCard from './MusicCard'
const MyMusic = () => {
const { userProjects = [] } = useUser();
const [menuPosition, setMenuPosition] = useState(null);
const [showMenu, setShowMenu] = useState(false);
const [selectedProjectId, setSelectedProjectId] = useState(null);
const projects = Array.isArray(userProjects) ? userProjects.slice(0, 3) : [];
const hasProjects = projects.length > 0;
const { userProjects = [] } = useUser()
const [menuPosition, setMenuPosition] = useState(null)
const [showMenu, setShowMenu] = useState(false)
const [selectedProjectId, setSelectedProjectId] = useState(null)
const projects = Array.isArray(userProjects) ? userProjects.slice(0, 3) : []
const hasProjects = projects.length > 0
const [, setTooltip] = useGlobal("_tooltip");
const navigateToMusicDetails = useNavigateToMusicDetails();
const [, setTooltip] = useGlobal('_tooltip')
const navigateToMusicDetails = useNavigateToMusicDetails()
return (
<CardContainer
label="Mes musiques"
onPress={() =>
navigate(Routes.AllMyList, {
title: "Mes musiques",
scope: "music",
title: 'Mes musiques',
scope: 'music',
liked: false,
})
}
@@ -57,12 +57,9 @@ const MyMusic = () => {
})
}
onPressMore={(posTop) => {
setSelectedProjectId(p.id);
setMenuPosition(posTop);
setShowMenu(
(prev) =>
!prev || posTop?.top !== (menuPosition?.top ?? null)
);
setSelectedProjectId(p.id)
setMenuPosition(posTop)
setShowMenu((prev) => !prev || posTop?.top !== (menuPosition?.top ?? null))
}}
/>
))}
@@ -75,26 +72,25 @@ const MyMusic = () => {
projectId={selectedProjectId}
extraItems={[
{
label: "Supprimer",
label: 'Supprimer',
onPress: () =>
SheetManager.show("Delete", {
SheetManager.show('Delete', {
payload: {
title: "Supprimer le projet",
message:
"Cette action supprimera définitivement ce projet.",
title: 'Supprimer le projet',
message: 'Cette action supprimera définitivement ce projet.',
onConfirm: async () => {
try {
if (!selectedProjectId) return;
await projectsRef.doc(selectedProjectId).delete();
if (!selectedProjectId) return
await projectsRef.doc(selectedProjectId).delete()
setTooltip({
type: "success",
text: "Projet supprimé",
});
type: 'success',
text: 'Projet supprimé',
})
} catch (e) {
setTooltip({
type: "error",
text: e?.message || "Suppression impossible",
});
type: 'error',
text: e?.message || 'Suppression impossible',
})
}
},
},
@@ -104,10 +100,10 @@ const MyMusic = () => {
/>
</View>
) : (
<View style={{ flexDirection: "column", gap: 10 }}>
<View style={{ flexDirection: 'column', gap: 10 }}>
<Text
style={{
textAlign: "center",
textAlign: 'center',
fontFamily: FONT_FAMILY.InterRegular,
color: Palette.white,
fontSize: 16,
@@ -123,7 +119,7 @@ const MyMusic = () => {
</View>
)}
</CardContainer>
);
};
)
}
export default MyMusic;
export default MyMusic
+32 -34
View File
@@ -1,30 +1,28 @@
import { BlurView } from "expo-blur";
import React from "react";
import { Image, Platform, Pressable, Text, View } from "react-native";
import { SheetManager } from "react-native-actions-sheet";
import { icons } from "../../../assets";
import GradientButton from "../../../components/GradientButton";
import useLayoutType from "../../../hooks/useLayoutType";
import { Routes } from "../../../navigation";
import { navigate } from "../../../navigation/NavigationService";
import { useUser } from "../../../providers/UserDataProvider";
import { Palette } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
import Style, { size } from "../../../styles/Style";
import CardContainer from "./CardContainer";
import { BlurView } from 'expo-blur'
import React from 'react'
import { Image, Platform, Pressable, Text, View } from 'react-native'
import { SheetManager } from 'react-native-actions-sheet'
import { icons } from '../../../assets'
import GradientButton from '../../../components/GradientButton'
import useLayoutType from '../../../hooks/useLayoutType'
import { Routes } from '../../../navigation'
import { navigate } from '../../../navigation/NavigationService'
import { useUser } from '../../../providers/UserDataProvider'
import { Palette } from '../../../styles'
import { FONT_FAMILY } from '../../../styles/Fonts'
import Style, { size } from '../../../styles/Style'
import CardContainer from './CardContainer'
const MyPlaylist = () => {
const { userPlaylists = [] } = useUser();
const playlists = Array.isArray(userPlaylists)
? userPlaylists.slice(0, 2)
: [];
const { isWeb } = useLayoutType();
const hasPlaylists = playlists.length > 0;
const { userPlaylists = [] } = useUser()
const playlists = Array.isArray(userPlaylists) ? userPlaylists.slice(0, 2) : []
const { isWeb } = useLayoutType()
const hasPlaylists = playlists.length > 0
return (
<CardContainer
label="Mes Playlists"
onPress={() => navigate(Routes.AllMyPlaylist)}
onPressPlus={() =>
SheetManager.show("Playlist", {
SheetManager.show('Playlist', {
payload: { startAtCreate: true },
})
}
@@ -34,18 +32,18 @@ const MyPlaylist = () => {
playlists.map((item) => (
<Pressable
key={item.id}
style={{ borderRadius: 12, overflow: "hidden" }}
style={{ borderRadius: 12, overflow: 'hidden' }}
onPress={() => {
console.log("isWeb", isWeb);
console.log('isWeb', isWeb)
if (isWeb) {
navigate(Routes.AllMyPlaylist, { playListId: item.id });
navigate(Routes.AllMyPlaylist, { playListId: item.id })
} else {
navigate(Routes.PlaylistDetails, { playlistId: item.id });
navigate(Routes.PlaylistDetails, { playlistId: item.id })
}
}}
>
<BlurView
intensity={Platform.OS !== "ios" ? 10 : 20}
intensity={Platform.OS !== 'ios' ? 10 : 20}
style={{
...Style.containerSpaceBetween,
minHeight: 59,
@@ -66,13 +64,13 @@ const MyPlaylist = () => {
flexShrink: 1,
}}
>
{item?.name || "Sans nom"}
{item?.name || 'Sans nom'}
</Text>
<Image
source={icons.chevronDown}
style={{
...size({ size: 15 }),
transform: [{ rotate: "-90deg" }],
transform: [{ rotate: '-90deg' }],
}}
resizeMode="contain"
/>
@@ -80,10 +78,10 @@ const MyPlaylist = () => {
</Pressable>
))
) : (
<View style={{ flexDirection: "column", gap: 10 }}>
<View style={{ flexDirection: 'column', gap: 10 }}>
<Text
style={{
textAlign: "center",
textAlign: 'center',
fontFamily: FONT_FAMILY.InterRegular,
color: Palette.white,
fontSize: 16,
@@ -94,7 +92,7 @@ const MyPlaylist = () => {
<GradientButton
containerStyle={{ padding: 2 }}
onPress={() =>
SheetManager.show("Playlist", {
SheetManager.show('Playlist', {
payload: { startAtCreate: true },
})
}
@@ -104,7 +102,7 @@ const MyPlaylist = () => {
)}
</View>
</CardContainer>
);
};
)
}
export default MyPlaylist;
export default MyPlaylist
@@ -1,15 +1,15 @@
import { View, Text, Pressable, StyleSheet, Platform } from "react-native";
import React from "react";
import SearchBar from "../../../components/SearchBar";
import { Palette, Style } from "../../../styles";
import BorderGradient from "../../../components/BorderGradient/BorderGradient";
import { BlurView } from "expo-blur";
import { FONT_FAMILY } from "../../../styles/Fonts";
import { View, Text, Pressable, StyleSheet, Platform } from 'react-native'
import React from 'react'
import SearchBar from '../../../components/SearchBar'
import { Palette, Style } from '../../../styles'
import BorderGradient from '../../../components/BorderGradient/BorderGradient'
import { BlurView } from 'expo-blur'
import { FONT_FAMILY } from '../../../styles/Fonts'
const ResearchHeader = ({
onPress,
selected,
searchValue = "",
searchValue = '',
onChangeSearch = () => {},
showSearchBar = true,
autoFocusSearch = true,
@@ -31,14 +31,11 @@ const ResearchHeader = ({
gap: 6,
}}
>
{["Musiques", "Playback", "Profils"].map((item, index) => (
{['Musiques', 'Playback', 'Profils'].map((item, index) => (
<Pressable key={index} onPress={() => onPress(item)}>
<BorderGradient
gradientProps={{
colors:
selected === item
? ["#F94697", "#7023F7"]
: [Palette.tran, Palette.tran],
colors: selected === item ? ['#F94697', '#7023F7'] : [Palette.tran, Palette.tran],
locations: [0, 1],
start: { x: 0, y: 0 },
end: { x: 1, y: 0 },
@@ -46,10 +43,7 @@ const ResearchHeader = ({
style={styles.borderGradient}
/>
<View style={styles.blurContainer}>
<BlurView
intensity={Platform.OS !== "ios" ? 10 : 20}
style={styles.blurView}
>
<BlurView intensity={Platform.OS !== 'ios' ? 10 : 20} style={styles.blurView}>
<Text style={styles.menu}>{item}</Text>
</BlurView>
</View>
@@ -57,10 +51,10 @@ const ResearchHeader = ({
))}
</View>
</View>
);
};
)
}
export default ResearchHeader;
export default ResearchHeader
const styles = StyleSheet.create({
menu: {
@@ -72,21 +66,21 @@ const styles = StyleSheet.create({
borderWidth: 1,
borderRadius: 100,
height: 30,
position: "absolute",
position: 'absolute',
zIndex: 1,
width: "100%",
width: '100%',
},
blurContainer: {
height: 30,
zIndex: -1,
borderRadius: 100,
overflow: "hidden",
overflow: 'hidden',
backgroundColor: Palette.glass,
},
blurView: {
width: "100%",
height: "100%",
justifyContent: "center",
width: '100%',
height: '100%',
justifyContent: 'center',
paddingHorizontal: 10,
},
});
})
@@ -1,20 +1,20 @@
import React, { useCallback, useMemo, useState } from "react";
import { Pressable, ScrollView, Text, View } from "react-native";
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 { getArtistDisplayName } from "../../../utils/artistName";
import { getProjectLikes, LIKE_TARGET } from "../../../utils/likes";
import CreateLyricsHeader from "../../Writing/components/CreateLyricsHeader";
import MusicCard from "./MusicCard";
import { defaultAvatar } from "../../../data/data";
import React, { useCallback, useMemo, useState } from 'react'
import { Pressable, ScrollView, Text, View } from 'react-native'
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 { getArtistDisplayName } from '../../../utils/artistName'
import { getProjectLikes, LIKE_TARGET } from '../../../utils/likes'
import CreateLyricsHeader from '../../Writing/components/CreateLyricsHeader'
import MusicCard from './MusicCard'
import { defaultAvatar } from '../../../data/data'
const EmptyText = ({ text }) => (
<View style={{ flex: 1, alignItems: "center", paddingVertical: 16 }}>
<View style={{ flex: 1, alignItems: 'center', paddingVertical: 16 }}>
<Text
style={{
fontSize: 14,
@@ -26,7 +26,7 @@ const EmptyText = ({ text }) => (
{text}
</Text>
</View>
);
)
const SearchResultsList = ({
selected,
@@ -40,105 +40,89 @@ const SearchResultsList = ({
contentContainerStyle,
style,
}) => {
const [menuPosition, setMenuPosition] = useState(null);
const [showMenu, setShowMenu] = useState(false);
const [selectedProjectId, setSelectedProjectId] = useState(null);
const [menuPosition, setMenuPosition] = useState(null)
const [showMenu, setShowMenu] = useState(false)
const [selectedProjectId, setSelectedProjectId] = useState(null)
const navigateToMusicDetails = useNavigateToMusicDetails();
const navigateToMusicDetails = useNavigateToMusicDetails()
const resolveCoverUri = useCallback(
(project, { preferThumbnail = false } = {}) => {
const isValid = (value) =>
typeof value === "string" && value.trim().length > 0;
if (!project) {
return null;
const resolveCoverUri = useCallback((project, { preferThumbnail = false } = {}) => {
const isValid = (value) => typeof value === 'string' && value.trim().length > 0
if (!project) {
return null
}
const cover = project?.cover || {}
const thumbnailCandidates = [project?.thumbnailUrl, project?.songThumbnailUrl].filter(isValid)
if (preferThumbnail) {
const preferredThumbnail = thumbnailCandidates.find(isValid)
if (preferredThumbnail) {
return preferredThumbnail
}
}
const cover = project?.cover || {};
const thumbnailCandidates = [
project?.thumbnailUrl,
project?.songThumbnailUrl,
].filter(isValid);
if (isValid(project?.coverUrl)) {
return project.coverUrl
}
if (preferThumbnail) {
const preferredThumbnail = thumbnailCandidates.find(isValid);
if (preferredThumbnail) {
return preferredThumbnail;
}
}
const coverCandidates = [cover?.result, cover?.finalUrl, cover?.generatedBackground]
if (isValid(project?.coverUrl)) {
return project.coverUrl;
}
if (Array.isArray(cover?.options)) {
coverCandidates.push(
...cover.options.flatMap((option) => [option?.finalUrl, option?.generatedUrl])
)
}
const coverCandidates = [
cover?.result,
cover?.finalUrl,
cover?.generatedBackground,
];
if (Array.isArray(cover?.options)) {
coverCandidates.push(
...cover.options.flatMap((option) => [
option?.finalUrl,
option?.generatedUrl,
]),
);
}
return coverCandidates.find(isValid) || null;
},
[],
);
return coverCandidates.find(isValid) || null
}, [])
const handleMusicPress = (project) => {
const didNavigate = navigateToMusicDetails({
projectId: project?.id,
songUrl: project?.songUrl,
project,
});
})
if (didNavigate) {
onResultSelected?.();
onResultSelected?.()
}
};
}
const handlePlaybackPress = (project) => {
if (!project?.id) {
return;
return
}
navigate(Routes.Playbacks, { projectId: project.id });
onResultSelected?.();
};
navigate(Routes.Playbacks, { projectId: project.id })
onResultSelected?.()
}
const handleProfilePress = (userId) => {
navigate(Routes.SingerProfile, { userId });
onResultSelected?.();
};
navigate(Routes.SingerProfile, { userId })
onResultSelected?.()
}
const musicItems = useMemo(() => {
if (!Array.isArray(musics)) {
return [];
return []
}
if (selected === "Musiques") {
return musics;
if (selected === 'Musiques') {
return musics
}
return musics.filter((project) => project?.hasPlayback !== true);
}, [musics, selected]);
return musics.filter((project) => project?.hasPlayback !== true)
}, [musics, selected])
const shouldShowMusics =
(!selected && (musicItems.length > 0 || musicsLoading)) ||
selected === "Musiques";
(!selected && (musicItems.length > 0 || musicsLoading)) || selected === 'Musiques'
const shouldShowPlaybacks =
(!selected && (playbacks.length > 0 || playbacksLoading)) ||
selected === "Playback";
(!selected && (playbacks.length > 0 || playbacksLoading)) || selected === 'Playback'
const shouldShowUsers =
(!selected && (users.length > 0 || usersLoading)) || selected === "Profils";
(!selected && (users.length > 0 || usersLoading)) || selected === 'Profils'
return (
<View style={[{ flex: 1 }, style]}>
@@ -154,7 +138,7 @@ const SearchResultsList = ({
musicItems.slice(0, 6).map((project) => (
<MusicCard
key={project.id}
title={project?.title || "Sans titre"}
title={project?.title || 'Sans titre'}
subtitle={project?.userName}
imageUri={resolveCoverUri(project)}
projectId={project?.id}
@@ -162,13 +146,12 @@ const SearchResultsList = ({
likeTarget={LIKE_TARGET.SONG}
onPress={() => handleMusicPress(project)}
onPressMore={(positionTop) => {
setSelectedProjectId(project.id);
setMenuPosition(positionTop);
setSelectedProjectId(project.id)
setMenuPosition(positionTop)
setShowMenu(
(previous) =>
!previous ||
positionTop?.top !== (menuPosition?.top ?? null),
);
!previous || positionTop?.top !== (menuPosition?.top ?? null)
)
}}
/>
))}
@@ -178,16 +161,16 @@ const SearchResultsList = ({
fontSize: 12,
color: Palette.white,
fontFamily: FONT_FAMILY.InterRegular,
alignSelf: "center",
alignSelf: 'center',
}}
>
{"Chargement…"}
{'Chargement…'}
</Text>
)}
</>
)}
{musicItems.length === 0 && !musicsLoading && (
<EmptyText text={"Aucune musique trouvée"} />
<EmptyText text={'Aucune musique trouvée'} />
)}
</View>
)}
@@ -199,7 +182,7 @@ const SearchResultsList = ({
playbacks.slice(0, 6).map((project) => (
<MusicCard
key={project.id}
title={project?.title || "Sans titre"}
title={project?.title || 'Sans titre'}
subtitle={project?.userName}
imageUri={resolveCoverUri(project, {
preferThumbnail: true,
@@ -209,13 +192,12 @@ const SearchResultsList = ({
likeTarget={LIKE_TARGET.PLAYBACK}
onPress={() => handlePlaybackPress(project)}
onPressMore={(positionTop) => {
setSelectedProjectId(project.id);
setMenuPosition(positionTop);
setSelectedProjectId(project.id)
setMenuPosition(positionTop)
setShowMenu(
(previous) =>
!previous ||
positionTop?.top !== (menuPosition?.top ?? null),
);
!previous || positionTop?.top !== (menuPosition?.top ?? null)
)
}}
/>
))}
@@ -225,24 +207,22 @@ const SearchResultsList = ({
fontSize: 12,
color: Palette.white,
fontFamily: FONT_FAMILY.InterRegular,
alignSelf: "center",
alignSelf: 'center',
}}
>
{"Chargement…"}
{'Chargement…'}
</Text>
)}
</>
)}
{playbacks.length === 0 && !playbacksLoading && (
<EmptyText text={"Aucun playback trouvé"} />
<EmptyText text={'Aucun playback trouvé'} />
)}
</View>
)}
{shouldShowUsers && (
<View style={{ paddingHorizontal: 2, paddingTop: 10 }}>
<CreateLyricsHeader
gradientProps={{ start: { x: 0, y: 0 }, end: { x: 0, y: 1 } }}
>
<CreateLyricsHeader gradientProps={{ start: { x: 0, y: 0 }, end: { x: 0, y: 1 } }}>
{users.length > 0 && (
<View style={{ gap: 8 }}>
{Array.isArray(users) &&
@@ -255,7 +235,7 @@ const SearchResultsList = ({
<ProfilePicture
uri={user?.profilePictureURL || defaultAvatar}
size={60}
imageProps={{ priority: "high" }}
imageProps={{ priority: 'high' }}
/>
<Text
style={{
@@ -264,7 +244,7 @@ const SearchResultsList = ({
fontFamily: FONT_FAMILY.InterRegular,
}}
>
{getArtistDisplayName(user, "Utilisateur")}
{getArtistDisplayName(user, 'Utilisateur')}
</Text>
</Pressable>
))}
@@ -274,26 +254,22 @@ const SearchResultsList = ({
fontSize: 12,
color: Palette.white,
fontFamily: FONT_FAMILY.InterRegular,
alignSelf: "center",
alignSelf: 'center',
}}
>
{"Chargement…"}
{'Chargement…'}
</Text>
)}
</View>
)}
{users.length === 0 && !usersLoading && (
<EmptyText text={"Aucun profil trouvé"} />
)}
{users.length === 0 && !usersLoading && <EmptyText text={'Aucun profil trouvé'} />}
</CreateLyricsHeader>
</View>
)}
{selected === "Clips" && (
{selected === 'Clips' && (
<View style={{ paddingHorizontal: 2, paddingTop: 10 }}>
<CreateLyricsHeader
gradientProps={{ start: { x: 0, y: 0 }, end: { x: 0, y: 1 } }}
>
<EmptyText text={"Aucun clip trouvé"} />
<CreateLyricsHeader gradientProps={{ start: { x: 0, y: 0 }, end: { x: 0, y: 1 } }}>
<EmptyText text={'Aucun clip trouvé'} />
</CreateLyricsHeader>
</View>
)}
@@ -307,7 +283,7 @@ const SearchResultsList = ({
projectId={selectedProjectId}
/>
</View>
);
};
)
}
export default SearchResultsList;
export default SearchResultsList