search on web
This commit is contained in:
@@ -1,18 +1,10 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Image,
|
||||
Platform,
|
||||
Pressable,
|
||||
ScrollView,
|
||||
Text,
|
||||
View,
|
||||
} from "react-native";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Image, Platform, ScrollView, 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 { Routes } from "../../navigation";
|
||||
import { navigate } from "../../navigation/NavigationService";
|
||||
import { useUser } from "../../providers/UserDataProvider";
|
||||
import { Palette, Style } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
@@ -21,6 +13,8 @@ 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 = [],
|
||||
@@ -51,18 +45,126 @@ const Library = () => {
|
||||
hasMyClips ||
|
||||
hasLikedClips;
|
||||
|
||||
const {
|
||||
search,
|
||||
setSearch,
|
||||
selected,
|
||||
setSelected,
|
||||
toggleSelected,
|
||||
filteredProjects,
|
||||
projectsLoading,
|
||||
loadMoreProjects,
|
||||
filteredUsers,
|
||||
usersLoading,
|
||||
loadMoreUsers,
|
||||
} = useSearch();
|
||||
|
||||
const [dropdownVisible, setDropdownVisible] = useState(false);
|
||||
const searchWrapperRef = useRef(null);
|
||||
|
||||
const closeDropdown = useCallback(() => {
|
||||
setDropdownVisible(false);
|
||||
setSelected(null);
|
||||
setSearch("");
|
||||
}, [setSelected, setSearch]);
|
||||
|
||||
const handleFocus = () => {
|
||||
setDropdownVisible(true);
|
||||
};
|
||||
|
||||
const shouldShowResults = dropdownVisible;
|
||||
|
||||
const handleChangeText = (value) => {
|
||||
setSearch(value);
|
||||
setDropdownVisible(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!dropdownVisible) return undefined;
|
||||
|
||||
const handleClickOutside = (event) => {
|
||||
if (searchWrapperRef.current?.contains(event.target)) return;
|
||||
closeDropdown();
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
document.addEventListener("touchstart", handleClickOutside);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
document.removeEventListener("touchstart", handleClickOutside);
|
||||
};
|
||||
}, [dropdownVisible, closeDropdown]);
|
||||
|
||||
return (
|
||||
<Page backgroundImg={background.libraryBG} headerType="NONE" width={"80%"}>
|
||||
<View style={{ gap: 17, paddingBottom: 20 }}>
|
||||
<View
|
||||
style={{ gap: 17, paddingBottom: 20, zIndex: dropdownVisible ? 40 : 1 }}
|
||||
>
|
||||
<Image source={icons.musicLandLogo} style={{ alignSelf: "center" }} />
|
||||
<Pressable onPress={() => navigate(Routes.Research)}>
|
||||
<View
|
||||
ref={searchWrapperRef}
|
||||
style={{
|
||||
position: "relative",
|
||||
alignSelf: "center",
|
||||
width: "65%",
|
||||
maxWidth: 520,
|
||||
minWidth: 360,
|
||||
zIndex: 40,
|
||||
overflow: "visible",
|
||||
}}
|
||||
>
|
||||
<SearchBar
|
||||
textInputProps={{
|
||||
editable: false,
|
||||
onPress: () => navigate(Routes.Research),
|
||||
value: search,
|
||||
onChangeText: handleChangeText,
|
||||
autoFocus: false,
|
||||
onFocus: handleFocus,
|
||||
}}
|
||||
/>
|
||||
</Pressable>
|
||||
{shouldShowResults && (
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 60,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 50,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
gap: 14,
|
||||
borderRadius: 18,
|
||||
padding: 16,
|
||||
backgroundColor: Palette.ultraLightWhite,
|
||||
...Style.defaultBorder,
|
||||
width: "100%",
|
||||
zIndex: 50,
|
||||
elevation: 12,
|
||||
}}
|
||||
>
|
||||
<ResearchHeader
|
||||
onPress={toggleSelected}
|
||||
selected={selected}
|
||||
showSearchBar={false}
|
||||
/>
|
||||
<SearchResultsList
|
||||
selected={selected}
|
||||
filteredProjects={filteredProjects}
|
||||
projectsLoading={projectsLoading}
|
||||
loadMoreProjects={loadMoreProjects}
|
||||
filteredUsers={filteredUsers}
|
||||
usersLoading={usersLoading}
|
||||
loadMoreUsers={loadMoreUsers}
|
||||
onResultSelected={closeDropdown}
|
||||
style={{ flex: 0, maxHeight: 420 }}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
<View style={{ flex: 1 }}>
|
||||
<ScrollView
|
||||
|
||||
+27
-382
@@ -1,225 +1,25 @@
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { Image, Pressable, ScrollView, Text, View } from "react-native";
|
||||
import { Image as ExpoImage } from "expo-image";
|
||||
import { background, img } from "../../assets";
|
||||
import MoreMenu from "../../components/MoreMenu";
|
||||
import { usersRef, projectsRef } from "../../config/firebase";
|
||||
import useDataFromRef from "../../hooks/useDataFromRef";
|
||||
import { useGlobal } from "reactn";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { background } from "../../assets";
|
||||
import useSearch from "../../hooks/useSearch";
|
||||
import Page from "../../layouts/Page";
|
||||
import { Routes } from "../../navigation";
|
||||
import { navigate } from "../../navigation/NavigationService";
|
||||
import { gutters, Palette, Style } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { size } from "../../styles/Style";
|
||||
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
|
||||
import MusicCard from "./components/MusicCard";
|
||||
import { gutters } from "../../styles";
|
||||
import ResearchHeader from "./components/ResearchHeader";
|
||||
import SearchResultsList from "./components/SearchResultsList";
|
||||
|
||||
const Research = () => {
|
||||
const [selected, setSelected] = useState(null);
|
||||
const [search, setSearch] = useState("");
|
||||
const queryText = useMemo(() => search.trim(), [search]);
|
||||
const queryLower = useMemo(
|
||||
() => (search || "").toLowerCase().trim(),
|
||||
[search],
|
||||
);
|
||||
const [currentUID] = useGlobal("currentUID");
|
||||
const [autoLoads, setAutoLoads] = useState(0);
|
||||
|
||||
const usersQueryRef = useMemo(() => {
|
||||
try {
|
||||
const lower = queryLower;
|
||||
if (lower) {
|
||||
return usersRef
|
||||
.orderBy("userNameLower")
|
||||
.startAt(lower)
|
||||
.endAt(lower + "\uf8ff");
|
||||
}
|
||||
return usersRef.orderBy("userNameLower");
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}, [queryLower]);
|
||||
|
||||
const projectsQueryRefLower = useMemo(() => {
|
||||
try {
|
||||
const lower = queryText ? queryText.toLowerCase() : "";
|
||||
if (lower) {
|
||||
return projectsRef
|
||||
.orderBy("titleLower")
|
||||
.startAt(lower)
|
||||
.endAt(lower + "\uf8ff");
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}, [queryText]);
|
||||
|
||||
const projectsQueryRefCase = useMemo(() => {
|
||||
try {
|
||||
const raw = queryText || "";
|
||||
if (raw) {
|
||||
return projectsRef
|
||||
.orderBy("title")
|
||||
.startAt(raw)
|
||||
.endAt(raw + "\uf8ff");
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}, [queryText]);
|
||||
|
||||
const {
|
||||
data: userResults = [],
|
||||
loading: usersLoading,
|
||||
loadMore: loadMoreUsers,
|
||||
} = useDataFromRef({
|
||||
ref: usersQueryRef,
|
||||
simpleRef: false,
|
||||
listener: false,
|
||||
condition: !!usersQueryRef,
|
||||
refreshArray: [queryLower],
|
||||
usePagination: true,
|
||||
batchSize: 5,
|
||||
});
|
||||
const filteredUsers = useMemo(() => {
|
||||
let arr = Array.isArray(userResults) ? userResults : [];
|
||||
// Client-side safety filter to guarantee match on pseudo
|
||||
if (queryLower) {
|
||||
arr = arr.filter((u) => {
|
||||
const nameLower = (u?.userNameLower || u?.userName || "").toLowerCase();
|
||||
return nameLower.includes(queryLower);
|
||||
});
|
||||
}
|
||||
const seen = new Set();
|
||||
const unique = [];
|
||||
for (const u of arr) {
|
||||
const id = u?.id;
|
||||
if (!id || seen.has(id)) continue;
|
||||
if (id === currentUID || u?.userID === currentUID) continue;
|
||||
seen.add(id);
|
||||
unique.push(u);
|
||||
}
|
||||
return unique;
|
||||
}, [JSON.stringify(userResults), queryLower, currentUID]);
|
||||
|
||||
const {
|
||||
data: projectResultsLower = [],
|
||||
loading: projectsLoadingLower,
|
||||
loadMore: loadMoreProjectsLower,
|
||||
} = useDataFromRef({
|
||||
ref: projectsQueryRefLower,
|
||||
simpleRef: false,
|
||||
listener: false,
|
||||
condition: !!projectsQueryRefLower,
|
||||
refreshArray: [queryText],
|
||||
usePagination: true,
|
||||
batchSize: 6,
|
||||
});
|
||||
const {
|
||||
data: projectResultsCase = [],
|
||||
loading: projectsLoadingCase,
|
||||
loadMore: loadMoreProjectsCase,
|
||||
} = useDataFromRef({
|
||||
ref: projectsQueryRefCase,
|
||||
simpleRef: false,
|
||||
listener: false,
|
||||
condition: !!projectsQueryRefCase,
|
||||
refreshArray: [queryText],
|
||||
usePagination: true,
|
||||
batchSize: 6,
|
||||
});
|
||||
const {
|
||||
data: projectResultsDefault = [],
|
||||
loading: projectsLoadingDefault,
|
||||
loadMore: loadMoreProjectsDefault,
|
||||
} = useDataFromRef({
|
||||
ref: !queryText ? projectsRef.orderBy("updatedAt", "desc") : null,
|
||||
simpleRef: false,
|
||||
listener: false,
|
||||
condition: !queryText,
|
||||
refreshArray: [queryText],
|
||||
usePagination: true,
|
||||
batchSize: 6,
|
||||
});
|
||||
|
||||
const projectsLoading = queryText
|
||||
? projectsLoadingLower || projectsLoadingCase
|
||||
: projectsLoadingDefault;
|
||||
const loadMoreProjects = () => {
|
||||
if (queryText) {
|
||||
loadMoreProjectsLower?.();
|
||||
loadMoreProjectsCase?.();
|
||||
} else {
|
||||
loadMoreProjectsDefault?.();
|
||||
}
|
||||
};
|
||||
const filteredProjects = useMemo(() => {
|
||||
const arr = queryText
|
||||
? [
|
||||
...(Array.isArray(projectResultsLower) ? projectResultsLower : []),
|
||||
...(Array.isArray(projectResultsCase) ? projectResultsCase : []),
|
||||
]
|
||||
: Array.isArray(projectResultsDefault)
|
||||
? projectResultsDefault
|
||||
: [];
|
||||
const seen = new Set();
|
||||
const unique = [];
|
||||
for (const p of arr) {
|
||||
const id = p?.id;
|
||||
if (!id || seen.has(id)) continue;
|
||||
seen.add(id);
|
||||
unique.push(p);
|
||||
}
|
||||
return unique;
|
||||
}, [
|
||||
queryText,
|
||||
JSON.stringify(projectResultsLower),
|
||||
JSON.stringify(projectResultsCase),
|
||||
JSON.stringify(projectResultsDefault),
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
setAutoLoads(0);
|
||||
}, [queryText]);
|
||||
|
||||
useEffect(() => {
|
||||
if (filteredUsers.length < 5 && usersQueryRef && !usersLoading) {
|
||||
if (autoLoads < 3) {
|
||||
setAutoLoads((n) => n + 1);
|
||||
loadMoreUsers();
|
||||
}
|
||||
}
|
||||
}, [filteredUsers.length, usersLoading, usersQueryRef, autoLoads]);
|
||||
const [menuPosition, setMenuPosition] = useState(null);
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
const [selectedProjectId, setSelectedProjectId] = useState(null);
|
||||
|
||||
const onPressMenu = (item) => {
|
||||
if (selected === item) {
|
||||
setSelected(null);
|
||||
} else {
|
||||
setSelected(item);
|
||||
}
|
||||
};
|
||||
|
||||
const EmptyText = ({ text }) => (
|
||||
<View style={{ flex: 1, alignItems: "center", paddingVertical: 16 }}>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 14,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
opacity: 0.8,
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
search,
|
||||
setSearch,
|
||||
selected,
|
||||
toggleSelected,
|
||||
filteredProjects,
|
||||
projectsLoading,
|
||||
loadMoreProjects,
|
||||
filteredUsers,
|
||||
usersLoading,
|
||||
loadMoreUsers,
|
||||
} = useSearch();
|
||||
|
||||
return (
|
||||
<Page
|
||||
@@ -232,175 +32,20 @@ const Research = () => {
|
||||
>
|
||||
<View style={{ flex: 1, gap: 10 }}>
|
||||
<ResearchHeader
|
||||
onPress={(item) => onPressMenu(item)}
|
||||
onPress={toggleSelected}
|
||||
selected={selected}
|
||||
searchValue={search}
|
||||
onChangeSearch={setSearch}
|
||||
/>
|
||||
<View style={{ flex: 1 }}>
|
||||
<ScrollView contentContainerStyle={{ paddingTop: 2 }}>
|
||||
{((!selected && (filteredProjects.length > 0 || projectsLoading)) ||
|
||||
selected === "Musiques") && (
|
||||
<View style={{ paddingHorizontal: 2 }}>
|
||||
<CreateLyricsHeader
|
||||
gradientProps={{
|
||||
start: { x: 0, y: 0 },
|
||||
end: { x: 0, y: 1 },
|
||||
}}
|
||||
>
|
||||
{filteredProjects.length > 0 ? (
|
||||
<View style={{ gap: 10 }}>
|
||||
{Array.isArray(filteredProjects) &&
|
||||
filteredProjects.slice(0, 6).map((p) => (
|
||||
<MusicCard
|
||||
key={p.id}
|
||||
title={p?.title || "Sans titre"}
|
||||
subtitle={"MusicLand"}
|
||||
imageUri={p?.coverUrl || null}
|
||||
projectId={p?.id}
|
||||
likedBy={p?.likedBy || []}
|
||||
onPress={() =>
|
||||
navigate(Routes.MusicDetails, { projectId: p.id })
|
||||
}
|
||||
onPressMore={(posTop) => {
|
||||
setSelectedProjectId(p.id);
|
||||
setMenuPosition(posTop);
|
||||
setShowMenu(
|
||||
(prev) =>
|
||||
!prev ||
|
||||
posTop?.top !== (menuPosition?.top ?? null),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{(filteredProjects.length >= 6 || projectsLoading) && (
|
||||
<Pressable
|
||||
onPress={loadMoreProjects}
|
||||
style={{ alignSelf: "center", marginTop: 6 }}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
opacity: projectsLoading ? 0.6 : 1,
|
||||
}}
|
||||
>
|
||||
{projectsLoading ? "Chargement…" : "Charger plus"}
|
||||
</Text>
|
||||
</Pressable>
|
||||
)}
|
||||
</View>
|
||||
) : (
|
||||
!projectsLoading && (
|
||||
<EmptyText text={"Aucune musique trouvée"} />
|
||||
)
|
||||
)}
|
||||
</CreateLyricsHeader>
|
||||
</View>
|
||||
)}
|
||||
{((!selected && (filteredUsers.length > 0 || usersLoading)) ||
|
||||
selected === "Profils") && (
|
||||
<View style={{ paddingHorizontal: 2, paddingTop: 10 }}>
|
||||
<CreateLyricsHeader
|
||||
gradientProps={{ start: { x: 0, y: 0 }, end: { x: 0, y: 1 } }}
|
||||
>
|
||||
{filteredUsers.length > 0 ? (
|
||||
<View style={{ gap: 8 }}>
|
||||
{Array.isArray(filteredUsers) &&
|
||||
filteredUsers.slice(0, 5).map((u) => (
|
||||
<Pressable
|
||||
style={{ gap: 14, ...Style.containerRow }}
|
||||
key={u.id}
|
||||
onPress={() =>
|
||||
navigate(Routes.SingerProfile, { userId: u.id })
|
||||
}
|
||||
>
|
||||
<ExpoImage
|
||||
source={
|
||||
u?.pictureUrl ||
|
||||
u?.profilePictureURL ||
|
||||
u?.photoURL
|
||||
? {
|
||||
uri:
|
||||
u?.pictureUrl ||
|
||||
u?.profilePictureURL ||
|
||||
u?.photoURL,
|
||||
}
|
||||
: img.profile
|
||||
}
|
||||
cachePolicy="memory-disk"
|
||||
priority="high"
|
||||
contentFit="cover"
|
||||
transition={100}
|
||||
style={{
|
||||
...size({ size: 60 }),
|
||||
borderRadius: 100,
|
||||
}}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
}}
|
||||
>
|
||||
{u?.userName || "Utilisateur"}
|
||||
</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
{(filteredUsers.length >= 5 || usersLoading) && (
|
||||
<Pressable
|
||||
onPress={loadMoreUsers}
|
||||
style={{ alignSelf: "center", marginTop: 6 }}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
opacity: usersLoading ? 0.6 : 1,
|
||||
}}
|
||||
>
|
||||
{usersLoading ? "Chargement…" : "Charger plus"}
|
||||
</Text>
|
||||
</Pressable>
|
||||
)}
|
||||
</View>
|
||||
) : (
|
||||
!usersLoading && <EmptyText text={"Aucun profil trouvé"} />
|
||||
)}
|
||||
</CreateLyricsHeader>
|
||||
</View>
|
||||
)}
|
||||
{selected === "Playbacks" && (
|
||||
<View style={{ paddingHorizontal: 2, paddingTop: 10 }}>
|
||||
<CreateLyricsHeader
|
||||
gradientProps={{ start: { x: 0, y: 0 }, end: { x: 0, y: 1 } }}
|
||||
>
|
||||
<EmptyText text={"Aucun playback trouvé"} />
|
||||
</CreateLyricsHeader>
|
||||
</View>
|
||||
)}
|
||||
{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>
|
||||
</View>
|
||||
)}
|
||||
<MoreMenu
|
||||
visible={showMenu}
|
||||
top={menuPosition?.top ?? 0}
|
||||
position={menuPosition}
|
||||
onClose={() => setShowMenu(false)}
|
||||
inPlaylist={false}
|
||||
projectId={selectedProjectId}
|
||||
/>
|
||||
</ScrollView>
|
||||
</View>
|
||||
<SearchResultsList
|
||||
selected={selected}
|
||||
filteredProjects={filteredProjects}
|
||||
projectsLoading={projectsLoading}
|
||||
loadMoreProjects={loadMoreProjects}
|
||||
filteredUsers={filteredUsers}
|
||||
usersLoading={usersLoading}
|
||||
loadMoreUsers={loadMoreUsers}
|
||||
/>
|
||||
</View>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@@ -11,23 +11,27 @@ const ResearchHeader = ({
|
||||
selected,
|
||||
searchValue = "",
|
||||
onChangeSearch = () => {},
|
||||
showSearchBar = true,
|
||||
autoFocusSearch = true,
|
||||
}) => {
|
||||
return (
|
||||
<View style={{ gap: 12 }}>
|
||||
<SearchBar
|
||||
textInputProps={{
|
||||
value: searchValue,
|
||||
onChangeText: onChangeSearch,
|
||||
autoFocus: true,
|
||||
}}
|
||||
/>
|
||||
{showSearchBar && (
|
||||
<SearchBar
|
||||
textInputProps={{
|
||||
value: searchValue,
|
||||
onChangeText: onChangeSearch,
|
||||
autoFocus: autoFocusSearch,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<View
|
||||
style={{
|
||||
...Style.containerRow,
|
||||
gap: 6,
|
||||
}}
|
||||
>
|
||||
{["Playbacks", "Clips", "Musiques", "Profils"].map((item, index) => (
|
||||
{["Musiques", "Playbacks", "Profils"].map((item, index) => (
|
||||
<Pressable key={index} onPress={() => onPress(item)}>
|
||||
<BorderGradient
|
||||
gradientProps={{
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
import React, { useState } from "react";
|
||||
import { Pressable, ScrollView, Text, View } from "react-native";
|
||||
import { Image as ExpoImage } from "expo-image";
|
||||
import { img } from "../../../assets";
|
||||
import MoreMenu from "../../../components/MoreMenu";
|
||||
import { Routes } from "../../../navigation";
|
||||
import { navigate } from "../../../navigation/NavigationService";
|
||||
import { Palette, Style } from "../../../styles";
|
||||
import { FONT_FAMILY } from "../../../styles/Fonts";
|
||||
import { size } from "../../../styles/Style";
|
||||
import CreateLyricsHeader from "../../Writing/components/CreateLyricsHeader";
|
||||
import MusicCard from "./MusicCard";
|
||||
|
||||
const EmptyText = ({ text }) => (
|
||||
<View style={{ flex: 1, alignItems: "center", paddingVertical: 16 }}>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 14,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
opacity: 0.8,
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
const SearchResultsList = ({
|
||||
selected,
|
||||
filteredProjects,
|
||||
projectsLoading,
|
||||
loadMoreProjects,
|
||||
filteredUsers,
|
||||
usersLoading,
|
||||
loadMoreUsers,
|
||||
onResultSelected,
|
||||
contentContainerStyle,
|
||||
style,
|
||||
}) => {
|
||||
const [menuPosition, setMenuPosition] = useState(null);
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
const [selectedProjectId, setSelectedProjectId] = useState(null);
|
||||
|
||||
const handleProjectPress = (projectId) => {
|
||||
navigate(Routes.MusicDetails, { projectId });
|
||||
onResultSelected?.();
|
||||
};
|
||||
|
||||
const handleProfilePress = (userId) => {
|
||||
navigate(Routes.SingerProfile, { userId });
|
||||
onResultSelected?.();
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={[{ flex: 1 }, style]}>
|
||||
<ScrollView
|
||||
contentContainerStyle={{ paddingTop: 2, ...contentContainerStyle }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
{((!selected && (filteredProjects.length > 0 || projectsLoading)) ||
|
||||
selected === "Musiques") && (
|
||||
<View style={{ paddingHorizontal: 2 }}>
|
||||
<CreateLyricsHeader
|
||||
gradientProps={{
|
||||
start: { x: 0, y: 0 },
|
||||
end: { x: 0, y: 1 },
|
||||
}}
|
||||
>
|
||||
{filteredProjects.length > 0 && (
|
||||
<View style={{ gap: 10 }}>
|
||||
{Array.isArray(filteredProjects) &&
|
||||
filteredProjects.slice(0, 6).map((project) => (
|
||||
<MusicCard
|
||||
key={project.id}
|
||||
title={project?.title || "Sans titre"}
|
||||
subtitle={"MusicLand"}
|
||||
imageUri={project?.coverUrl || null}
|
||||
projectId={project?.id}
|
||||
likedBy={project?.likedBy || []}
|
||||
onPress={() => handleProjectPress(project.id)}
|
||||
onPressMore={(positionTop) => {
|
||||
setSelectedProjectId(project.id);
|
||||
setMenuPosition(positionTop);
|
||||
setShowMenu(
|
||||
(previous) =>
|
||||
!previous ||
|
||||
positionTop?.top !== (menuPosition?.top ?? null),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{(filteredProjects.length >= 6 || projectsLoading) && (
|
||||
<Pressable
|
||||
onPress={loadMoreProjects}
|
||||
style={{ alignSelf: "center", marginTop: 6 }}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
opacity: projectsLoading ? 0.6 : 1,
|
||||
}}
|
||||
>
|
||||
{projectsLoading ? "Chargement…" : "Charger plus"}
|
||||
</Text>
|
||||
</Pressable>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
{filteredProjects.length === 0 && !projectsLoading && (
|
||||
<EmptyText text={"Aucune musique trouvée"} />
|
||||
)}
|
||||
</CreateLyricsHeader>
|
||||
</View>
|
||||
)}
|
||||
{((!selected && (filteredUsers.length > 0 || usersLoading)) ||
|
||||
selected === "Profils") && (
|
||||
<View style={{ paddingHorizontal: 2, paddingTop: 10 }}>
|
||||
<CreateLyricsHeader
|
||||
gradientProps={{ start: { x: 0, y: 0 }, end: { x: 0, y: 1 } }}
|
||||
>
|
||||
{filteredUsers.length > 0 && (
|
||||
<View style={{ gap: 8 }}>
|
||||
{Array.isArray(filteredUsers) &&
|
||||
filteredUsers.slice(0, 5).map((user) => (
|
||||
<Pressable
|
||||
style={{ gap: 14, ...Style.containerRow }}
|
||||
key={user.id}
|
||||
onPress={() => handleProfilePress(user.id)}
|
||||
>
|
||||
<ExpoImage
|
||||
source={
|
||||
user?.pictureUrl ||
|
||||
user?.profilePictureURL ||
|
||||
user?.photoURL
|
||||
? {
|
||||
uri:
|
||||
user?.pictureUrl ||
|
||||
user?.profilePictureURL ||
|
||||
user?.photoURL,
|
||||
}
|
||||
: img.profile
|
||||
}
|
||||
cachePolicy="memory-disk"
|
||||
priority="high"
|
||||
contentFit="cover"
|
||||
transition={100}
|
||||
style={{
|
||||
...size({ size: 60 }),
|
||||
borderRadius: 100,
|
||||
}}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
}}
|
||||
>
|
||||
{user?.userName || "Utilisateur"}
|
||||
</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
{(filteredUsers.length >= 5 || usersLoading) && (
|
||||
<Pressable
|
||||
onPress={loadMoreUsers}
|
||||
style={{ alignSelf: "center", marginTop: 6 }}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
opacity: usersLoading ? 0.6 : 1,
|
||||
}}
|
||||
>
|
||||
{usersLoading ? "Chargement…" : "Charger plus"}
|
||||
</Text>
|
||||
</Pressable>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
{filteredUsers.length === 0 && !usersLoading && (
|
||||
<EmptyText text={"Aucun profil trouvé"} />
|
||||
)}
|
||||
</CreateLyricsHeader>
|
||||
</View>
|
||||
)}
|
||||
{selected === "Playbacks" && (
|
||||
<View style={{ paddingHorizontal: 2, paddingTop: 10 }}>
|
||||
<CreateLyricsHeader
|
||||
gradientProps={{ start: { x: 0, y: 0 }, end: { x: 0, y: 1 } }}
|
||||
>
|
||||
<EmptyText text={"Aucun playback trouvé"} />
|
||||
</CreateLyricsHeader>
|
||||
</View>
|
||||
)}
|
||||
{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>
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
<MoreMenu
|
||||
visible={showMenu}
|
||||
top={menuPosition?.top ?? 0}
|
||||
position={menuPosition}
|
||||
onClose={() => setShowMenu(false)}
|
||||
inPlaylist={false}
|
||||
projectId={selectedProjectId}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchResultsList;
|
||||
Reference in New Issue
Block a user