Change main page and modify flows
This commit is contained in:
+259
-148
@@ -1,5 +1,6 @@
|
||||
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";
|
||||
@@ -19,32 +20,53 @@ 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 {
|
||||
if (queryText) {
|
||||
const lower = queryLower;
|
||||
if (lower) {
|
||||
return usersRef
|
||||
.orderBy("userName")
|
||||
.startAt(queryText)
|
||||
.endAt(queryText + "\uf8ff");
|
||||
.orderBy("userNameLower")
|
||||
.startAt(lower)
|
||||
.endAt(lower + "\uf8ff");
|
||||
}
|
||||
return usersRef.orderBy("userName");
|
||||
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 projectsQueryRef = useMemo(() => {
|
||||
const projectsQueryRefCase = useMemo(() => {
|
||||
try {
|
||||
if (queryText) {
|
||||
const raw = queryText || "";
|
||||
if (raw) {
|
||||
return projectsRef
|
||||
.orderBy("title")
|
||||
.startAt(queryText)
|
||||
.endAt(queryText + "\uf8ff");
|
||||
.startAt(raw)
|
||||
.endAt(raw + "\uf8ff");
|
||||
}
|
||||
return projectsRef.orderBy("updatedAt", "desc");
|
||||
return null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
@@ -59,12 +81,19 @@ const Research = () => {
|
||||
simpleRef: false,
|
||||
listener: false,
|
||||
condition: !!usersQueryRef,
|
||||
refreshArray: [queryText],
|
||||
refreshArray: [queryLower],
|
||||
usePagination: true,
|
||||
batchSize: 5,
|
||||
});
|
||||
const filteredUsers = useMemo(() => {
|
||||
const arr = Array.isArray(userResults) ? userResults : [];
|
||||
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) {
|
||||
@@ -75,24 +104,83 @@ const Research = () => {
|
||||
unique.push(u);
|
||||
}
|
||||
return unique;
|
||||
}, [JSON.stringify(userResults), currentUID]);
|
||||
}, [JSON.stringify(userResults), queryLower, currentUID]);
|
||||
|
||||
const {
|
||||
data: projectResults = [],
|
||||
loading: projectsLoading,
|
||||
loadMore: loadMoreProjects,
|
||||
data: projectResultsLower = [],
|
||||
loading: projectsLoadingLower,
|
||||
loadMore: loadMoreProjectsLower,
|
||||
} = useDataFromRef({
|
||||
ref: projectsQueryRef,
|
||||
ref: projectsQueryRefLower,
|
||||
simpleRef: false,
|
||||
listener: false,
|
||||
condition: !!projectsQueryRef,
|
||||
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(() => {
|
||||
return Array.isArray(projectResults) ? projectResults : [];
|
||||
}, [JSON.stringify(projectResults)]);
|
||||
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);
|
||||
@@ -105,13 +193,7 @@ const Research = () => {
|
||||
loadMoreUsers();
|
||||
}
|
||||
}
|
||||
}, [
|
||||
filteredUsers.length,
|
||||
usersLoading,
|
||||
usersQueryRef,
|
||||
autoLoads,
|
||||
loadMoreUsers,
|
||||
]);
|
||||
}, [filteredUsers.length, usersLoading, usersQueryRef, autoLoads]);
|
||||
const [menuTop, setMenuTop] = useState(0);
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
const [selectedProjectId, setSelectedProjectId] = useState(null);
|
||||
@@ -124,6 +206,21 @@ const Research = () => {
|
||||
}
|
||||
};
|
||||
|
||||
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>
|
||||
);
|
||||
|
||||
return (
|
||||
<Page
|
||||
headerType="NAVIGATION"
|
||||
@@ -142,7 +239,15 @@ const Research = () => {
|
||||
/>
|
||||
<View style={{ flex: 1 }}>
|
||||
<ScrollView contentContainerStyle={{ paddingTop: 2 }}>
|
||||
{(!selected || selected === "Musiques") && (
|
||||
{(!selected &&
|
||||
!projectsLoading &&
|
||||
!usersLoading &&
|
||||
filteredProjects.length === 0 &&
|
||||
filteredUsers.length === 0) && (
|
||||
<EmptyText text={"Aucun résultat"} />
|
||||
)}
|
||||
{((!selected && (filteredProjects.length > 0 || projectsLoading)) ||
|
||||
selected === "Musiques") && (
|
||||
<View style={{ paddingHorizontal: 2 }}>
|
||||
<CreateLyricsHeader
|
||||
gradientProps={{
|
||||
@@ -150,138 +255,144 @@ const Research = () => {
|
||||
end: { x: 0, y: 1 },
|
||||
}}
|
||||
>
|
||||
<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);
|
||||
setMenuTop(posTop);
|
||||
setShowMenu((prev) => !prev || posTop !== menuTop);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{(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>
|
||||
</CreateLyricsHeader>
|
||||
</View>
|
||||
)}
|
||||
{(!selected || selected === "Clips") && (
|
||||
<View style={{ paddingHorizontal: 2, paddingTop: 10 }}>
|
||||
<CreateLyricsHeader
|
||||
gradientProps={{
|
||||
start: { x: 0, y: 0 },
|
||||
end: { x: 0, y: 1 },
|
||||
}}
|
||||
>
|
||||
<View style={{ gap: 6, ...Style.containerRow }}>
|
||||
{Array.from({ length: 3 }).map((_, index) => (
|
||||
<View style={{ flex: 1, gap: 6 }} key={index}>
|
||||
<Image
|
||||
source={img.placeholder3}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: 147,
|
||||
borderRadius: 10,
|
||||
}}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
}}
|
||||
>
|
||||
Pour Lili
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</CreateLyricsHeader>
|
||||
</View>
|
||||
)}
|
||||
{(!selected || selected === "Profils") && (
|
||||
<View style={{ paddingHorizontal: 2, paddingTop: 10 }}>
|
||||
<CreateLyricsHeader
|
||||
gradientProps={{ start: { x: 0, y: 0 }, end: { x: 0, y: 1 } }}
|
||||
>
|
||||
<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 })
|
||||
}
|
||||
>
|
||||
<Image
|
||||
source={
|
||||
u?.pictureUrl || u?.profilePictureURL || u?.photoURL
|
||||
? {
|
||||
uri:
|
||||
u?.pictureUrl ||
|
||||
u?.profilePictureURL ||
|
||||
u?.photoURL,
|
||||
}
|
||||
: img.profile
|
||||
{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 })
|
||||
}
|
||||
style={{ ...size({ size: 60 }), borderRadius: 100 }}
|
||||
onPressMore={(posTop) => {
|
||||
setSelectedProjectId(p.id);
|
||||
setMenuTop(posTop);
|
||||
setShowMenu(
|
||||
(prev) => !prev || posTop !== menuTop,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{(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,
|
||||
}}
|
||||
>
|
||||
{u?.userName || "Utilisateur"}
|
||||
{projectsLoading ? "Chargement…" : "Charger plus"}
|
||||
</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,
|
||||
}}
|
||||
)}
|
||||
</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 }}
|
||||
>
|
||||
{usersLoading ? "Chargement…" : "Charger plus"}
|
||||
</Text>
|
||||
</Pressable>
|
||||
)}
|
||||
</View>
|
||||
<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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user