import { GET_JOB_BY_PK } from "@/graphql/jobs.queries"; import { useQuery } from "@apollo/client"; import { AntDesign } from "@expo/vector-icons"; import { useGlobalSearchParams } from "expo-router"; import { DateTime } from "luxon"; import React, { memo, useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { FlatList, RefreshControl, View } from "react-native"; import { ActivityIndicator, Button, Card, Divider, Text, } from "react-native-paper"; import ErrorDisplay from "../error/error-display"; import NewNoteModal from "./new-note-modal"; export default function JobNotes() { const { jobId } = useGlobalSearchParams(); const [noteModalVisible, setNoteModalVisible] = useState(false); const showNoteModal = () => setNoteModalVisible(true); const hideNoteModal = () => setNoteModalVisible(false); const { loading, error, data, refetch } = useQuery(GET_JOB_BY_PK, { variables: { id: jobId, }, skip: !jobId, }); const handleNoteCreated = useCallback(() => { hideNoteModal(); refetch(); }, [refetch]); const { t } = useTranslation(); const onRefresh = async () => { return refetch(); }; const job = data?.jobs_by_pk; // Memoized list data (only when job & notes exist) const listData = useMemo(() => { if (!job?.notes) return []; const notes = job.notes; const pinnedNotes = []; const otherNotes = []; for (const n of notes) { (n.pinned ? pinnedNotes : otherNotes).push(n); } pinnedNotes.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); otherNotes.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); const DIVIDER_ID = "__divider__"; return pinnedNotes.length > 0 && otherNotes.length > 0 ? [...pinnedNotes, { id: DIVIDER_ID, type: "divider" }, ...otherNotes] : [...pinnedNotes, ...otherNotes]; }, [job?.notes]); const keyExtractor = useCallback((item) => item.id, []); const renderItem = useCallback( ({ item }) => item.type === "divider" ? : , [] ); if (loading) { return ; } if (error) { return ; } if (!job) { return ; } return ( {listData.length === 0 ? ( {t("jobdetail.labels.nojobnotes")} ) : ( } style={{ flex: 1 }} data={listData} keyExtractor={keyExtractor} renderItem={renderItem} removeClippedSubviews initialNumToRender={12} maxToRenderPerBatch={8} windowSize={7} /> )} ); } const NoteListItem = memo(function NoteListItem({ item }) { return ( {item.text} {item.pinned && ( )} {item.private && ( )} {item.critical && ( )} {item.created_by} {DateTime.fromISO(item.created_at).toLocaleString( DateTime.DATETIME_SHORT )} ); }); const DividerItem = memo(function DividerItem() { return ; });