clear more ticket, sub and design
This commit is contained in:
@@ -21,7 +21,7 @@ import { background, icons } from "../../assets";
|
||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||
import SearchBar from "../../components/SearchBar";
|
||||
import ShareBtn from "../../components/ShareBtn/ShareBtn";
|
||||
import { projectsRef } from "../../config/firebase";
|
||||
import firebase, { projectsRef, usersRef } from "../../config/firebase";
|
||||
import useDataFromRef from "../../hooks/useDataFromRef";
|
||||
import useNavigateToMusicDetails from "../../hooks/useNavigateToMusicDetails";
|
||||
import useSearch from "../../hooks/useSearch";
|
||||
@@ -37,6 +37,25 @@ import SearchResultsList from "../Library/components/SearchResultsList";
|
||||
import PlaybacksCard from "./components/PlaybacksCard";
|
||||
import SongCard from "./components/SongCard";
|
||||
import BorderGradient from "../../components/BorderGradient/BorderGradient.web";
|
||||
|
||||
const allowedPremiumLevels = new Set(["starter", "pro", "premium"]);
|
||||
|
||||
const normalizePremiumLevel = (value) => {
|
||||
if (typeof value !== "string") {
|
||||
return null;
|
||||
}
|
||||
const normalized = value.trim().toLowerCase();
|
||||
return allowedPremiumLevels.has(normalized) ? normalized : null;
|
||||
};
|
||||
|
||||
const chunkArray = (items = [], size = 10) => {
|
||||
const chunks = [];
|
||||
for (let i = 0; i < items.length; i += size) {
|
||||
chunks.push(items.slice(i, i + size));
|
||||
}
|
||||
return chunks;
|
||||
};
|
||||
|
||||
const HitParade = () => {
|
||||
const [selectedCategory, setSelectedCategory] = useState("Chansons");
|
||||
const navigateToMusicDetails = useNavigateToMusicDetails();
|
||||
@@ -92,6 +111,7 @@ const HitParade = () => {
|
||||
title={item?.title || "Sans titre"}
|
||||
artist={item?.userName}
|
||||
coverUrl={item?.coverUrl || null}
|
||||
subscriptionLevel={getCreatorLevel(item)}
|
||||
onPress={() =>
|
||||
navigateToMusicDetails({
|
||||
projectId: item.id,
|
||||
@@ -118,6 +138,7 @@ const HitParade = () => {
|
||||
title={item?.title || "Sans titre"}
|
||||
artist={item?.userName}
|
||||
thumbnailUrl={resolvePlaybackThumbnail(item)}
|
||||
subscriptionLevel={getCreatorLevel(item)}
|
||||
onPress={() => navigate(Routes.Playbacks, { projectId: item.id })}
|
||||
/>
|
||||
)}
|
||||
@@ -133,6 +154,7 @@ const HitParade = () => {
|
||||
title={item?.title || "Sans titre"}
|
||||
artist={item?.userName}
|
||||
coverUrl={item?.coverUrl || null}
|
||||
subscriptionLevel={getCreatorLevel(item)}
|
||||
onPress={() =>
|
||||
navigateToMusicDetails({
|
||||
projectId: item.id,
|
||||
@@ -154,6 +176,7 @@ const HitParade = () => {
|
||||
title={item?.title || "Sans titre"}
|
||||
artist={item?.userName}
|
||||
thumbnailUrl={resolvePlaybackThumbnail(item)}
|
||||
subscriptionLevel={getCreatorLevel(item)}
|
||||
onPress={() => navigate(Routes.Playbacks, { projectId: item.id })}
|
||||
/>
|
||||
))}
|
||||
@@ -188,6 +211,99 @@ const HitParade = () => {
|
||||
[musics]
|
||||
);
|
||||
|
||||
const [creatorsById, setCreatorsById] = useState({});
|
||||
|
||||
const requiredUserIds = useMemo(() => {
|
||||
const collected = new Set();
|
||||
const collectUserId = (project) => {
|
||||
const uid =
|
||||
project && typeof project.userId === "string"
|
||||
? project.userId.trim()
|
||||
: null;
|
||||
if (uid) {
|
||||
collected.add(uid);
|
||||
}
|
||||
};
|
||||
songsList.forEach(collectUserId);
|
||||
playbackList.forEach(collectUserId);
|
||||
return Array.from(collected);
|
||||
}, [playbackList, songsList]);
|
||||
|
||||
const fetchCreatorsByIds = useCallback(
|
||||
async (userIds) => {
|
||||
if (!Array.isArray(userIds) || userIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const normalizedIds = userIds
|
||||
.map((id) => (typeof id === "string" ? id.trim() : null))
|
||||
.filter(Boolean);
|
||||
|
||||
if (normalizedIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pendingIds = new Set(normalizedIds);
|
||||
const nextCreators = {};
|
||||
|
||||
const idChunks = chunkArray(normalizedIds, 10);
|
||||
|
||||
await Promise.all(
|
||||
idChunks.map(async (chunk) => {
|
||||
try {
|
||||
const snapshot = await usersRef
|
||||
.where(firebase.firestore.FieldPath.documentId(), "in", chunk)
|
||||
.get();
|
||||
|
||||
snapshot.docs.forEach((doc) => {
|
||||
const data = doc.data() || {};
|
||||
nextCreators[doc.id] = {
|
||||
premiumLevel: normalizePremiumLevel(data.premiumLevel),
|
||||
};
|
||||
pendingIds.delete(doc.id);
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(
|
||||
"HitParade: unable to fetch creators",
|
||||
error?.message || error
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
pendingIds.forEach((userId) => {
|
||||
nextCreators[userId] = { premiumLevel: null };
|
||||
});
|
||||
|
||||
if (Object.keys(nextCreators).length > 0) {
|
||||
setCreatorsById((previous) => ({ ...previous, ...nextCreators }));
|
||||
}
|
||||
},
|
||||
[setCreatorsById]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const missingIds = requiredUserIds.filter((id) => !creatorsById[id]);
|
||||
if (missingIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
fetchCreatorsByIds(missingIds);
|
||||
}, [creatorsById, fetchCreatorsByIds, requiredUserIds]);
|
||||
|
||||
const getCreatorLevel = useCallback(
|
||||
(project) => {
|
||||
const uid =
|
||||
project && typeof project.userId === "string"
|
||||
? project.userId.trim()
|
||||
: null;
|
||||
if (!uid) {
|
||||
return null;
|
||||
}
|
||||
return creatorsById?.[uid]?.premiumLevel || null;
|
||||
},
|
||||
[creatorsById]
|
||||
);
|
||||
|
||||
const playbackResults = useMemo(
|
||||
() => (Array.isArray(playbacks) ? playbacks : []),
|
||||
[playbacks]
|
||||
@@ -246,7 +362,7 @@ const HitParade = () => {
|
||||
}, [closeDropdown, dropdownVisible, isWeb]);
|
||||
|
||||
const shouldShowResults = isWeb && dropdownVisible;
|
||||
const shouldBlurContent = shouldShowResults && hasSearchQuery;
|
||||
const shouldBlurContent = shouldShowResults;
|
||||
|
||||
const handlePlayRandomSong = useCallback(() => {
|
||||
const arr = Array.isArray(topSongs) ? topSongs : [];
|
||||
@@ -520,7 +636,9 @@ const HitParade = () => {
|
||||
StyleSheet.absoluteFillObject,
|
||||
{
|
||||
zIndex: 10,
|
||||
borderRadius: 18,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.25)",
|
||||
overflow: "hidden",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { BlurView } from "expo-blur";
|
||||
import { img } from "../../../assets";
|
||||
import { Palette, Style } from "../../../styles";
|
||||
import { FONT_FAMILY } from "../../../styles/Fonts";
|
||||
import SubscriptionBadge from "../../../components/SubscriptionBadge";
|
||||
|
||||
const PlaybacksCard = ({
|
||||
rank = 1,
|
||||
@@ -12,6 +13,7 @@ const PlaybacksCard = ({
|
||||
thumbnailUrl = null,
|
||||
coverUrl = null,
|
||||
onPress = () => null,
|
||||
subscriptionLevel = null,
|
||||
}) => {
|
||||
const resolveUri = (value) =>
|
||||
typeof value === "string" && value.trim().length > 0 ? value : null;
|
||||
@@ -38,43 +40,67 @@ const PlaybacksCard = ({
|
||||
style={{
|
||||
...Style.containerRow,
|
||||
gap: 15,
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
<View
|
||||
style={{
|
||||
fontSize: 22,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
...Style.containerRow,
|
||||
gap: 15,
|
||||
alignItems: "center",
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
{rank}
|
||||
</Text>
|
||||
<Image
|
||||
source={imageUri ? { uri: imageUri } : img.placeholder3}
|
||||
style={{ width: 67, height: 108, borderRadius: 16 }}
|
||||
/>
|
||||
<View>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 16,
|
||||
fontSize: 22,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{artist}
|
||||
{rank}
|
||||
</Text>
|
||||
<Image
|
||||
source={imageUri ? { uri: imageUri } : img.placeholder3}
|
||||
style={{ width: 67, height: 108, borderRadius: 16 }}
|
||||
/>
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
<View
|
||||
style={{
|
||||
...Style.containerRow,
|
||||
gap: 6,
|
||||
flexWrap: "wrap",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
flexShrink: 1,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{artist}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<SubscriptionBadge level={subscriptionLevel} size={36} />
|
||||
</View>
|
||||
</BlurView>
|
||||
</Pressable>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { BlurView } from "expo-blur";
|
||||
import Style, { size } from "../../../styles/Style";
|
||||
import { Palette } from "../../../styles";
|
||||
import { FONT_FAMILY } from "../../../styles/Fonts";
|
||||
import SubscriptionBadge from "../../../components/SubscriptionBadge";
|
||||
|
||||
const SongCard = ({
|
||||
rank = 1,
|
||||
@@ -19,6 +20,7 @@ const SongCard = ({
|
||||
artist = "MusicLand",
|
||||
coverUrl = null,
|
||||
onPress = null,
|
||||
subscriptionLevel = null,
|
||||
}) => {
|
||||
return (
|
||||
<Pressable
|
||||
@@ -69,7 +71,7 @@ const SongCard = ({
|
||||
>
|
||||
{rank}
|
||||
</Text>
|
||||
<View>
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 16,
|
||||
@@ -79,16 +81,32 @@ const SongCard = ({
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
<Text
|
||||
<View
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
...Style.containerRow,
|
||||
gap: 6,
|
||||
flexWrap: "wrap",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
{artist}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
flexShrink: 1,
|
||||
}}
|
||||
>
|
||||
{artist}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<SubscriptionBadge
|
||||
level={subscriptionLevel}
|
||||
size={36}
|
||||
style={{ marginLeft: "auto" }}
|
||||
/>
|
||||
</View>
|
||||
</BlurView>
|
||||
</Pressable>
|
||||
|
||||
Reference in New Issue
Block a user