Синхронизация сообщений в группах

This commit is contained in:
RoyceDa
2026-02-24 16:06:21 +02:00
parent fbc4f73f3d
commit 089fa055d3

View File

@@ -21,31 +21,39 @@ import { Attachment, AttachmentType, PacketMessage } from "../ProtocolProvider/p
import { useUpdateSyncTime } from "./useUpdateSyncTime"; import { useUpdateSyncTime } from "./useUpdateSyncTime";
import { useFileStorage } from "@/app/hooks/useFileStorage"; import { useFileStorage } from "@/app/hooks/useFileStorage";
import { DeliveredMessageState, Message } from "./DialogProvider"; 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 { useMemory } from "../MemoryProvider/useMemory";
import { useDialogsCache } from "./useDialogsCache"; import { useDialogsCache } from "./useDialogsCache";
import { PacketRead } from "../ProtocolProvider/protocol/packets/packet.read"; 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() { export function useSynchronize() {
const [_, setProtocolState] = useProtocolState(); const [protocolState, setProtocolState] = useProtocolState();
const {getQuery, runQuery} = useDatabase(); const {getQuery, runQuery} = useDatabase();
const publicKey = usePublicKey(); const publicKey = usePublicKey();
const send = useSender(); const send = useSender();
const {protocol} = useProtocol(); const {protocol} = useProtocol();
const {parseGroupString, hasGroup} = useGroups(); const {parseGroupString, hasGroup, getGroupKey} = useGroups();
const privatePlain = usePrivatePlain(); const privatePlain = usePrivatePlain();
const {error, info} = useConsoleLogger('useSynchronize'); const {error, info} = useConsoleLogger('useSynchronize');
const log = useLogger('useSynchronize');
const {setInviteStatusByGroupId} = useGroupInviteStatus(''); const {setInviteStatusByGroupId} = useGroupInviteStatus('');
const updateGroupInformation = useUpdateGroupInformation(); const updateGroupInformation = useUpdateGroupInformation();
const {updateDialog} = useDialogsList(); const {updateDialog} = useDialogsList();
const idle = useIdle(TIME_TO_INACTIVE_FOR_MESSAGES_UNREAD * 1000);
const updateSyncTime = useUpdateSyncTime(); const updateSyncTime = useUpdateSyncTime();
const {writeFile} = useFileStorage(); const {writeFile} = useFileStorage();
const { getDialogCache, addOrUpdateDialogCache } = useDialogsCache(); const { getDialogCache, addOrUpdateDialogCache } = useDialogsCache();
const [currentDialogPublicKeyView, __] = useMemory("current-dialog-public-key-view", "", true); const [currentDialogPublicKeyView, __] = useMemory("current-dialog-public-key-view", "", true);
const [viewState] = useViewPanelsState();
const focused = useWindowFocus();
useEffect(() => { useEffect(() => {
@@ -238,15 +246,12 @@ export function useSynchronize() {
*/ */
return; return;
} }
console.info("PACKED_READ_SYNC");
await runQuery(`UPDATE messages SET read = 1 WHERE from_public_key = ? AND to_public_key = ? AND account = ?`, await runQuery(`UPDATE messages SET read = 1 WHERE from_public_key = ? AND to_public_key = ? AND account = ?`,
[toPublicKey, fromPublicKey, publicKey]); [toPublicKey, fromPublicKey, publicKey]);
console.info("updating with params ", [fromPublicKey, toPublicKey, publicKey]);
updateDialog(toPublicKey); updateDialog(toPublicKey);
addOrUpdateDialogCache(fromPublicKey, getDialogCache(fromPublicKey).map((message) => { addOrUpdateDialogCache(fromPublicKey, getDialogCache(fromPublicKey).map((message) => {
if (message.from_public_key == toPublicKey && !message.readed) { if (message.from_public_key == toPublicKey && !message.readed) {
console.info("Marking message as read in cache for dialog with " + fromPublicKey);
console.info({ fromPublicKey, toPublicKey }); console.info({ fromPublicKey, toPublicKey });
return { return {
...message, ...message,
@@ -257,4 +262,118 @@ export function useSynchronize() {
})); }));
}); });
}, [updateDialog, publicKey]); }, [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]);
} }