Files
musicland/src/screens/Library/components/MusicCard.js
T
2025-09-02 11:45:27 +02:00

173 lines
4.5 KiB
JavaScript

import { BlurView } from "expo-blur";
import React, { useEffect, useRef, useState } from "react";
import { Platform, Pressable, StyleSheet, Text, View, Image as RNImage } from "react-native";
import { Image as ExpoImage } from "expo-image";
import { useGlobal } from "reactn";
import { icons, img } from "../../../assets";
import { arrayRemove, arrayUnion, projectsRef } from "../../../config/firebase";
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",
imageUri = null,
projectId = null,
likedBy = [],
}) => {
const [currentUID] = useGlobal("currentUID");
const [selected, setSelected] = useState(false);
const [layout, setLayout] = useState(null);
const [menuPos, setMenuPos] = useState(0);
const cardRef = useRef(null);
useEffect(() => {
if (layout) {
const top = layout?.y + 45;
setMenuPos(top);
}
}, [layout]);
useEffect(() => {
if (Array.isArray(likedBy) && currentUID) {
setSelected(likedBy.includes(currentUID));
}
}, [JSON.stringify(likedBy), currentUID]);
const toggleLike = async () => {
if (!projectId || !currentUID) return;
const next = !selected;
setSelected(next);
try {
await projectsRef.doc(projectId).update({
likedBy: next ? arrayUnion(currentUID) : arrayRemove(currentUID),
});
} catch (e) {
// rollback on failure
setSelected(!next);
}
};
const onPressMenu = () => {
// Prefer absolute screen position when available (Portal rendering)
if (cardRef?.current?.measureInWindow) {
try {
cardRef.current.measureInWindow((x, y, width, height) => {
const top = (y || 0) + 45;
setMenuPos(top);
onPressMore?.(top);
});
return;
} catch (e) {
// fallback to relative layout position
}
}
onPressMore?.(menuPos);
};
return (
<>
<Pressable
style={{
...Style.containerRow,
gap: 6,
}}
ref={cardRef}
onPress={onPress}
onLayout={(e) => setLayout(e.nativeEvent.layout)}
>
{imageUri ? (
<ExpoImage
source={{ uri: imageUri }}
cachePolicy="memory-disk"
priority="high"
contentFit="cover"
transition={100}
style={{ ...size({ size: 60 }), borderRadius: 12 }}
/>
) : (
<RNImage
source={img.placeholder2}
style={{ ...size({ size: 60 }), borderRadius: 12 }}
/>
)}
<View style={styles.blurContainer}>
<BlurView
intensity={20}
style={{
flex: 1,
...Style.containerSpaceBetween,
paddingHorizontal: 8,
}}
experimentalBlurMethod={
Platform.OS !== "ios" ? "dimezisBlurView" : "none"
}
>
<View>
<Text style={styles.title}>{title || "Sans titre"}</Text>
<Text style={styles.subTitle}>{subtitle || "MusicLand"}</Text>
</View>
<View
style={{
...Style.containerRow,
gap: 12,
}}
>
<Pressable onPress={toggleLike}>
<RNImage
source={selected ? icons.heart : icons.heartOutline}
style={size({ size: 24 })}
resizeMode="contain"
/>
</Pressable>
<Pressable onPress={onPressMenu}>
<RNImage source={icons.more} style={size({ size: 24 })} />
</Pressable>
</View>
</BlurView>
</View>
</Pressable>
{/* {open && (
<View
style={{
width: 100,
height: 50,
backgroundColor: "red",
position: "absolute",
right: 0,
top: menuPos,
zIndex: 2,
}}
/>
)} */}
</>
);
};
export default MusicCard;
const styles = StyleSheet.create({
blurContainer: {
borderRadius: 12,
overflow: "hidden",
flex: 1,
height: "100%",
},
title: {
fontSize: 16,
color: Palette.white,
fontFamily: FONT_FAMILY.OwnersRegular,
},
subTitle: {
fontSize: 12,
color: Palette.white,
fontFamily: FONT_FAMILY.InterRegular,
},
filterContainer: {
backgroundColor: Palette.tran,
},
});