Синхронизация сообщений в группах
This commit is contained in:
@@ -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]);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user