mirror of
https://github.com/vector-im/element-web.git
synced 2026-05-07 13:16:41 +02:00
fix(docs): handle encrypted rooms by listening to MatrixEventEvent.Decrypted
In encrypted rooms, Room.timeline fires with type m.room.encrypted so the org.element.doc.delta type check always fails. The event only has its real type after decryption. Fix: also listen on client MatrixEventEvent.Decrypted, which fires once the event is fully decrypted with the correct type. Keep Room.timeline for unencrypted rooms. Both listeners share the same applyDeltaEvent handler. Since applyDeltaEvent is idempotent (receive_changes is a CRDT merge), it is safe for an unencrypted event to be processed by both listeners. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
a850212ea8
commit
e7bb3bf146
@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { type Room, type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { type Room, type MatrixClient, MatrixEventEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { useWysiwyg, type UseWysiwyg } from "@vector-im/matrix-wysiwyg";
|
||||
|
||||
@ -154,34 +154,44 @@ function useDocumentSync(
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const onTimeline = (event: import("matrix-js-sdk/src/matrix").MatrixEvent): void => {
|
||||
logger.info("[DocumentView] Registering delta listeners for room", room.roomId);
|
||||
|
||||
const applyDeltaEvent = (event: import("matrix-js-sdk/src/matrix").MatrixEvent): void => {
|
||||
if (event.getRoomId() !== room.roomId) return;
|
||||
if (event.getType() !== DOC_DELTA_EVENT_TYPE) return;
|
||||
// Skip events sent by this exact device — already in the local model.
|
||||
|
||||
const eventDeviceId = event.getUnsigned()?.["device_id"] as string | undefined;
|
||||
if (event.getSender() === client.getUserId() && eventDeviceId === client.getDeviceId()) return;
|
||||
|
||||
const model = composerModelRef.current;
|
||||
if (!isCollaborative(model)) return;
|
||||
if (!isCollaborative(model)) { logger.warn("[DocumentView] Model not collaborative yet, dropping delta"); return; }
|
||||
|
||||
const data = event.getContent<{ data?: string }>().data;
|
||||
if (!data) return;
|
||||
if (!data) { logger.warn("[DocumentView] Delta event has no data"); return; }
|
||||
try {
|
||||
model.receive_changes(base64Decode(data));
|
||||
if (editorRef.current) {
|
||||
editorRef.current.innerHTML = model.get_content_as_html();
|
||||
onContentChanged();
|
||||
}
|
||||
logger.info("[DocumentView] Applied remote delta successfully");
|
||||
} catch (e) {
|
||||
logger.warn("[DocumentView] Failed to apply remote delta", e);
|
||||
}
|
||||
};
|
||||
|
||||
// For unencrypted rooms: events arrive ready to use on Room.timeline.
|
||||
// For encrypted rooms: events arrive as m.room.encrypted on Room.timeline
|
||||
// and are only usable after MatrixEventEvent.Decrypted fires on the client.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
room.on("Room.timeline" as any, onTimeline);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return () => room.off("Room.timeline" as any, onTimeline);
|
||||
// Intentionally omit composerModel — we use composerModelRef instead.
|
||||
room.on("Room.timeline" as any, applyDeltaEvent);
|
||||
client.on(MatrixEventEvent.Decrypted, applyDeltaEvent);
|
||||
|
||||
return () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
room.off("Room.timeline" as any, applyDeltaEvent);
|
||||
client.off(MatrixEventEvent.Decrypted, applyDeltaEvent);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [room, client, editorRef, onContentChanged]);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user