This commit is contained in:
2025-09-03 14:55:01 +02:00
parent 2b24900e0c
commit 64fd988d2f
9 changed files with 187 additions and 123 deletions
@@ -0,0 +1,299 @@
// CommentsBottomSheet.jsx
import {
BottomSheetBackdrop,
BottomSheetModal,
BottomSheetScrollView,
} from "@gorhom/bottom-sheet";
import { BlurView } from "expo-blur";
import { Image as ExpoImage } from "expo-image";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import {
Image,
Platform,
Pressable,
Text,
TextInput,
View,
} from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { icons } from "../../assets";
import { projectsRef, serverTimestamp } from "../../config/firebase";
import useDataFromRef from "../../hooks/useDataFromRef";
import { useUser } from "../../providers/UserDataProvider";
import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import { size } from "../../styles/Style";
let externalOpen;
export const openComments = (projectId) => {
if (typeof externalOpen === "function") externalOpen(projectId);
};
const formatRelativeTime = (date) => {
try {
const d = date instanceof Date ? date : date?.toDate?.() || null;
if (!d) return "";
const diff = Math.max(0, Date.now() - d.getTime());
const sec = Math.floor(diff / 1000);
if (sec < 60) return `${sec}s`;
const min = Math.floor(sec / 60);
if (min < 60) return `${min}min`;
const h = Math.floor(min / 60);
if (h < 24) return `${h}h`;
const dA = Math.floor(h / 24);
return `${dA}j`;
} catch (e) {
return "";
}
};
const CommentsBottomSheet = () => {
const modalRef = useRef(null);
const insets = useSafeAreaInsets();
const { currentUID, currentUserData } = useUser();
const [projectId, setProjectId] = useState(null);
const [text, setText] = useState("");
const [typing, setTyping] = useState(false);
useEffect(() => {
externalOpen = (pid) => {
setProjectId(pid || null);
requestAnimationFrame(() => modalRef.current?.present());
};
return () => {
externalOpen = undefined;
};
}, []);
const snapPoints = useMemo(() => ["50%", "90%"], []);
const commentsRef = useMemo(() => {
try {
return projectId
? projectsRef
.doc(projectId)
.collection("comments")
.orderBy("createdAt", "desc")
: null;
} catch {
return null;
}
}, [projectId]);
const { data: comments = [] } = useDataFromRef({
ref: commentsRef,
simpleRef: false,
listener: true,
condition: !!commentsRef,
refreshArray: [projectId],
});
const onSend = async () => {
const value = (text || "").trim();
if (!value || !projectId || !currentUID) return;
try {
setText("");
await projectsRef
.doc(projectId)
.collection("comments")
.add({
userId: currentUID,
userName: currentUserData?.userName || "",
profilePicture: currentUserData?.profilePictureURL || "",
text: value,
createdAt: serverTimestamp(),
});
} catch {
// ignore
}
};
const renderBackdrop = useCallback(
(props) => (
<BottomSheetBackdrop
{...props}
appearsOnIndex={0}
disappearsOnIndex={-1}
pressBehavior={typing ? "none" : "close"}
/>
),
[typing]
);
return (
<BottomSheetModal
ref={modalRef}
snapPoints={snapPoints}
keyboardBehavior="interactive" // linput est dans le contenu → mode interactif OK
keyboardBlurBehavior="restore"
enablePanDownToClose={!typing} // évite un dismiss pendant la frappe
enableContentPanningGesture={!typing}
stackBehavior="push"
topInset={insets.top}
bottomInset={insets.bottom}
backdropComponent={renderBackdrop}
handleIndicatorStyle={{ backgroundColor: Palette.white }}
backgroundStyle={{ backgroundColor: "transparent" }}
>
<BlurView
intensity={20}
style={{ flex: 1, backgroundColor: Palette.glass }}
experimentalBlurMethod={
Platform.OS !== "ios" ? "dimezisBlurView" : "none"
}
>
<View style={{ paddingTop: 10, paddingBottom: 8 }}>
<Text
style={{
fontSize: 22,
color: Palette.white,
fontFamily: FONT_FAMILY.InterSemiBold,
alignSelf: "center",
}}
>
Commentaires
</Text>
</View>
<BottomSheetScrollView
keyboardShouldPersistTaps="handled"
contentContainerStyle={{
paddingHorizontal: 14,
paddingBottom: (insets?.bottom || 0) + 12,
}}
showsVerticalScrollIndicator
>
{Array.isArray(comments) && comments.length > 0 ? (
comments.map((c) => (
<View
key={c.id}
style={{ flexDirection: "row", gap: 12, marginBottom: 14 }}
>
{c?.profilePicture ? (
<ExpoImage
source={{ uri: c.profilePicture }}
cachePolicy="memory-disk"
priority="high"
contentFit="cover"
transition={100}
style={{ ...size({ size: 42 }), borderRadius: 100 }}
/>
) : (
<View
style={{
...size({ size: 42 }),
borderRadius: 100,
backgroundColor: "#FFFFFF22",
}}
/>
)}
<View style={{ flex: 1 }}>
<View
style={{
flexDirection: "row",
alignItems: "center",
gap: 6,
}}
>
<Text
style={{
fontSize: 12,
color: Palette.white,
fontFamily: FONT_FAMILY.InterMedium,
}}
>
{c?.userId === currentUID ? "vous" : c?.userName || ""}
</Text>
<Text
style={{
fontSize: 12,
color: Palette.white,
opacity: 0.75,
fontFamily: FONT_FAMILY.InterRegular,
}}
>
{formatRelativeTime(c?.createdAt)}
</Text>
</View>
<Text
style={{
fontSize: 14,
color: Palette.white,
fontFamily: FONT_FAMILY.InterRegular,
}}
>
{c?.text}
</Text>
</View>
</View>
))
) : (
<Text
style={{
fontSize: 14,
color: Palette.white,
opacity: 0.8,
fontFamily: FONT_FAMILY.InterRegular,
textAlign: "center",
marginTop: 6,
}}
>
Aucun commentaire pour le moment
</Text>
)}
{/* INPUT en bas du contenu */}
<View style={{ height: 12 }} />
<BlurView
intensity={20}
style={{
flexDirection: "row",
alignItems: "center",
gap: 10,
paddingHorizontal: 16,
backgroundColor: Palette.glass,
overflow: "hidden",
height: 54,
borderRadius: 16,
}}
experimentalBlurMethod={
Platform.OS !== "ios" ? "dimezisBlurView" : "none"
}
>
<TextInput
placeholder="Ajouter un commentaire"
placeholderTextColor="#FFFFFFAA"
value={text}
onChangeText={setText}
onFocus={() => setTyping(true)}
onBlur={() => setTyping(false)}
style={{
flex: 1,
color: Palette.white,
fontSize: 14,
fontFamily: FONT_FAMILY.InterRegular,
}}
/>
<Pressable onPress={onSend}>
<Image
source={icons.send}
style={{ width: 24, height: 24, tintColor: Palette.white }}
resizeMode="contain"
/>
</Pressable>
</BlurView>
<View style={{ height: (insets?.bottom || 0) + 8 }} />
</BottomSheetScrollView>
</BlurView>
</BottomSheetModal>
);
};
export default CommentsBottomSheet;