diff --git a/app/providers/DialogProvider/useSynchronize.ts b/app/providers/DialogProvider/useSynchronize.ts index b7248d3..1692014 100644 --- a/app/providers/DialogProvider/useSynchronize.ts +++ b/app/providers/DialogProvider/useSynchronize.ts @@ -21,31 +21,39 @@ import { Attachment, AttachmentType, PacketMessage } from "../ProtocolProvider/p import { useUpdateSyncTime } from "./useUpdateSyncTime"; import { useFileStorage } from "@/app/hooks/useFileStorage"; import { DeliveredMessageState, Message } from "./DialogProvider"; -import { MESSAGE_MAX_LOADED } from "@/app/constants"; +import { MESSAGE_MAX_LOADED, TIME_TO_INACTIVE_FOR_MESSAGES_UNREAD } from "@/app/constants"; import { useMemory } from "../MemoryProvider/useMemory"; import { useDialogsCache } from "./useDialogsCache"; import { PacketRead } from "../ProtocolProvider/protocol/packets/packet.read"; +import { useLogger } from "@/app/hooks/useLogger"; +import { useIdle } from "@mantine/hooks"; +import { useViewPanelsState, ViewPanelsState } from "@/app/hooks/useViewPanelsState"; +import { useWindowFocus } from "@/app/hooks/useWindowFocus"; /** * Хук отвечает за синхронизацию сообщений, запрос синхронизации * при подключении */ export function useSynchronize() { - const [_, setProtocolState] = useProtocolState(); + const [protocolState, setProtocolState] = useProtocolState(); const {getQuery, runQuery} = useDatabase(); const publicKey = usePublicKey(); const send = useSender(); const {protocol} = useProtocol(); - const {parseGroupString, hasGroup} = useGroups(); + const {parseGroupString, hasGroup, getGroupKey} = useGroups(); const privatePlain = usePrivatePlain(); const {error, info} = useConsoleLogger('useSynchronize'); + const log = useLogger('useSynchronize'); const {setInviteStatusByGroupId} = useGroupInviteStatus(''); const updateGroupInformation = useUpdateGroupInformation(); const {updateDialog} = useDialogsList(); + const idle = useIdle(TIME_TO_INACTIVE_FOR_MESSAGES_UNREAD * 1000); const updateSyncTime = useUpdateSyncTime(); const {writeFile} = useFileStorage(); const { getDialogCache, addOrUpdateDialogCache } = useDialogsCache(); const [currentDialogPublicKeyView, __] = useMemory("current-dialog-public-key-view", "", true); + const [viewState] = useViewPanelsState(); + const focused = useWindowFocus(); useEffect(() => { @@ -238,15 +246,12 @@ export function useSynchronize() { */ return; } - console.info("PACKED_READ_SYNC"); await runQuery(`UPDATE messages SET read = 1 WHERE from_public_key = ? AND to_public_key = ? AND account = ?`, [toPublicKey, fromPublicKey, publicKey]); - console.info("updating with params ", [fromPublicKey, toPublicKey, publicKey]); updateDialog(toPublicKey); addOrUpdateDialogCache(fromPublicKey, getDialogCache(fromPublicKey).map((message) => { if (message.from_public_key == toPublicKey && !message.readed) { - console.info("Marking message as read in cache for dialog with " + fromPublicKey); console.info({ fromPublicKey, toPublicKey }); return { ...message, @@ -257,4 +262,118 @@ export function useSynchronize() { })); }); }, [updateDialog, publicKey]); + + /** + * Обработчик сообщений для синхронизации своих же сообщений в группе + */ + usePacket(0x06, async (packet: PacketMessage) => { + runTaskInQueue(async () => { + const fromPublicKey = packet.getFromPublicKey(); + const toPublicKey = packet.getToPublicKey(); + const content = packet.getContent(); + const timestamp = packet.getTimestamp(); + const messageId = packet.getMessageId(); + if (!hasGroup(toPublicKey)) { + /** + * Если это личное сообщение, то игнорируем его здесь + * для него есть отдельный слушатель usePacket (снизу) + */ + return; + } + if (fromPublicKey != publicKey) { + /** + * Игнорируем если это сообщения не от нас + */ + return; + } + await updateSyncTime(timestamp); + const groupKey = await getGroupKey(toPublicKey); + if (!groupKey) { + log("Group key not found for group " + toPublicKey); + error("Message dropped because group key not found for group " + toPublicKey); + return; + } + info("New group message packet received from " + fromPublicKey); + + let decryptedContent = ''; + + try { + decryptedContent = await decodeWithPassword(groupKey, content); + } catch (e) { + decryptedContent = ''; + } + + let attachmentsMeta: any[] = []; + let messageAttachments: Attachment[] = []; + for (let i = 0; i < packet.getAttachments().length; i++) { + const attachment = packet.getAttachments()[i]; + log("Attachment received id " + attachment.id + " type " + attachment.type); + + let nextLength = messageAttachments.push({ + ...attachment, + blob: "" + }); + + if (attachment.type == AttachmentType.MESSAGES) { + /** + * Этот тип вложения приходит сразу в blob и не нуждается + * в последующем скачивании + */ + const decryptedBlob = await decodeWithPassword(groupKey, attachment.blob); + writeFile(`m/${await generateMd5(attachment.id + publicKey)}`, + Buffer.from(await encodeWithPassword(privatePlain, decryptedBlob)).toString('binary')); + messageAttachments[nextLength - 1].blob = decryptedBlob; + } + + attachmentsMeta.push({ + id: attachment.id, + type: attachment.type, + preview: attachment.preview + }); + } + + const newMessage: Message = { + from_public_key: fromPublicKey, + to_public_key: toPublicKey, + content: content, + timestamp: timestamp, + readed: idle ? 0 : 1, + chacha_key: groupKey, + from_me: fromPublicKey == publicKey ? 1 : 0, + plain_message: decryptedContent, + delivered: DeliveredMessageState.DELIVERED, + message_id: messageId, + attachments: messageAttachments + }; + + await runQuery(` + INSERT INTO messages + (from_public_key, to_public_key, content, timestamp, read, chacha_key, from_me, plain_message, account, message_id, delivered, attachments) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `, [fromPublicKey, + toPublicKey, + content, + timestamp, + /**если текущий открытый диалог == беседе (которая приходит в toPublicKey) */ + (currentDialogPublicKeyView == toPublicKey && !idle && viewState != ViewPanelsState.DIALOGS_PANEL_ONLY) ? 1 : 0, + '', + 0, + await encodeWithPassword(privatePlain, decryptedContent), + publicKey, + messageId, + DeliveredMessageState.DELIVERED, + JSON.stringify(attachmentsMeta)]); + + /** + * Так как у нас в toPublicKey приходит ID группы, + * то обновляем диалог по этому ID, а не по fromPublicKey + * как это сделано в личных сообщениях + */ + updateDialog(toPublicKey); + + let dialogCache = getDialogCache(toPublicKey); + if (currentDialogPublicKeyView !== toPublicKey && dialogCache.length > 0) { + addOrUpdateDialogCache(toPublicKey, [...dialogCache, newMessage].slice(-MESSAGE_MAX_LOADED)); + } + }); + }, [updateDialog, focused, currentDialogPublicKeyView, viewState, idle, protocolState]); } \ No newline at end of file