import React, { useMemo, useRef, useState } from 'react'; import TipTap from './TipTap'; import { AuthenticatedContainerInnerTop, CustomButton, } from '../../styles/App-styles'; import { Box, CircularProgress, useTheme } from '@mui/material'; import { objectToBase64 } from '../../qdn/encryption/group-encryption'; import ShortUniqueId from 'short-unique-id'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { getBaseApi, getFee } from '../../background'; import { decryptPublishes, getTempPublish, handleUnencryptedPublishes, saveTempPublish, } from './GroupAnnouncements'; import { AnnouncementList } from './AnnouncementList'; import { Spacer } from '../../common/Spacer'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { getArbitraryEndpointReact, getBaseApiReact, pauseAllQueues, resumeAllQueues, } from '../../App'; const tempKey = 'accouncement-comment'; const uid = new ShortUniqueId({ length: 8 }); export const AnnouncementDiscussion = ({ getSecretKey, encryptChatMessage, selectedAnnouncement, secretKey, setSelectedAnnouncement, show, myName, isPrivate, }) => { const theme = useTheme(); const [isSending, setIsSending] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isFocusedParent, setIsFocusedParent] = useState(false); const [comments, setComments] = useState([]); const [tempPublishedList, setTempPublishedList] = useState([]); const firstMountRef = useRef(false); const [data, setData] = useState({}); const editorRef = useRef(null); const setEditorRef = (editorInstance) => { editorRef.current = editorInstance; }; const clearEditorContent = () => { if (editorRef.current) { editorRef.current.chain().focus().clearContent().run(); } }; const getData = async ({ identifier, name }, isPrivate) => { try { const res = await fetch( `${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64` ); if (!res?.ok) return; const data = await res.text(); const response = isPrivate === false ? handleUnencryptedPublishes([data]) : await decryptPublishes([{ data }], secretKey); const messageData = response[0]; setData((prev) => { return { ...prev, [`${identifier}-${name}`]: messageData, }; }); } catch (error) { console.log(error); } }; const publishAnc = async ({ encryptedData, identifier }: any) => { try { if (!selectedAnnouncement) return; return new Promise((res, rej) => { window .sendMessage('publishGroupEncryptedResource', { encryptedData, identifier, }) .then((response) => { if (!response?.error) { res(response); return; } rej(response.error); }) .catch((error) => { rej(error.message || 'An error occurred'); }); }); } catch (error) { console.log(error); } }; const setTempData = async () => { try { const getTempAnnouncements = await getTempPublish(); if (getTempAnnouncements[tempKey]) { let tempData = []; Object.keys(getTempAnnouncements[tempKey] || {}).map((key) => { const value = getTempAnnouncements[tempKey][key]; if (value.data?.announcementId === selectedAnnouncement.identifier) { tempData.push(value.data); } }); setTempPublishedList(tempData); } } catch (error) { console.log(error); } }; const publishComment = async () => { try { pauseAllQueues(); const fee = await getFee('ARBITRARY'); await show({ message: 'Would you like to perform a ARBITRARY transaction?', publishFee: fee.fee + ' QORT', }); if (isSending) return; if (editorRef.current) { const htmlContent = editorRef.current.getHTML(); if (!htmlContent?.trim() || htmlContent?.trim() === '
') return; setIsSending(true); const message = { version: 1, extra: {}, message: htmlContent, }; const secretKeyObject = isPrivate === false ? null : await getSecretKey(false, true); const message64: any = await objectToBase64(message); const encryptSingle = isPrivate === false ? message64 : await encryptChatMessage(message64, secretKeyObject); const randomUid = uid.rnd(); const identifier = `cm-${selectedAnnouncement.identifier}-${randomUid}`; const res = await publishAnc({ encryptedData: encryptSingle, identifier, }); const dataToSaveToStorage = { name: myName, identifier, service: 'DOCUMENT', tempData: message, created: Date.now(), announcementId: selectedAnnouncement.identifier, }; await saveTempPublish({ data: dataToSaveToStorage, key: tempKey }); setTempData(); clearEditorContent(); } // send chat message } catch (error) { console.error(error); } finally { resumeAllQueues(); setIsSending(false); } }; const getComments = React.useCallback( async (selectedAnnouncement, isPrivate) => { try { setIsLoading(true); const offset = 0; // dispatch(setIsLoadingGlobal(true)) const identifier = `cm-${selectedAnnouncement.identifier}`; const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; const response = await fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }); const responseData = await response.json(); setTempData(); setComments(responseData); setIsLoading(false); for (const data of responseData) { getData({ name: data.name, identifier: data.identifier }, isPrivate); } } catch (error) { console.log(error); } finally { setIsLoading(false); // dispatch(setIsLoadingGlobal(false)) } }, [secretKey] ); const loadMore = async () => { try { setIsLoading(true); const offset = comments.length; const identifier = `cm-${selectedAnnouncement.identifier}`; const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; const response = await fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }); const responseData = await response.json(); setComments((prev) => [...prev, ...responseData]); setIsLoading(false); for (const data of responseData) { getData({ name: data.name, identifier: data.identifier }, isPrivate); } } catch (error) { console.log(error); } }; const combinedListTempAndReal = useMemo(() => { // Combine the two lists const combined = [...tempPublishedList, ...comments]; // Remove duplicates based on the "identifier" const uniqueItems = new Map(); combined.forEach((item) => { uniqueItems.set(item.identifier, item); // This will overwrite duplicates, keeping the last occurrence }); // Convert the map back to an array and sort by "created" timestamp in descending order const sortedList = Array.from(uniqueItems.values()).sort( (a, b) => b.created - a.created ); return sortedList; }, [tempPublishedList, comments]); React.useEffect(() => { if (!secretKey && isPrivate) return; if (selectedAnnouncement && !firstMountRef.current && isPrivate !== null) { getComments(selectedAnnouncement, isPrivate); firstMountRef.current = true; } }, [selectedAnnouncement, secretKey, isPrivate]); return (