From 9d161a0bcf649335c7af22ffdd66ca5c6ca76ab3 Mon Sep 17 00:00:00 2001 From: RMidhunSuresh Date: Fri, 7 Jan 2022 19:38:57 +0530 Subject: [PATCH] Refactor + put redaction in NonPersistedEventEntry --- src/matrix/room/timeline/Timeline.js | 16 ++++--- src/matrix/room/timeline/common.js | 47 +++++++++++++++++++ .../room/timeline/entries/EventEntry.js | 7 +-- .../entries/NonPersistedEventEntry.js | 18 +++++++ .../timeline/persistence/RelationWriter.js | 46 +----------------- 5 files changed, 78 insertions(+), 56 deletions(-) diff --git a/src/matrix/room/timeline/Timeline.js b/src/matrix/room/timeline/Timeline.js index bb6c9ac5..eec848c7 100644 --- a/src/matrix/room/timeline/Timeline.js +++ b/src/matrix/room/timeline/Timeline.js @@ -254,7 +254,7 @@ export class Timeline { /** @package */ addEntries(newEntries) { this._addLocalRelationsToNewRemoteEntries(newEntries); - this._updateEntriesNotInTimeline(newEntries); + this._updateEntriesFetchedFromHomeserver(newEntries); this._moveEntryToRemoteEntries(newEntries); this._remoteEntries.setManySorted(newEntries); this._loadContextEntriesWhereNeeded(newEntries); @@ -262,13 +262,17 @@ export class Timeline { /** * Update entries based on newly received events. - * eg: a newly received redaction event may mark an existing event in contextEntriesNotInTimeline as being redacted - * This is only for the events that are not in the timeline but had to fetched from elsewhere to render reply previews. + * This is specific to events that are not in the timeline but had to be fetched from the homeserver. */ - _updateEntriesNotInTimeline(entries) { + _updateEntriesFetchedFromHomeserver(entries) { for (const entry of entries) { const relatedEntry = this._contextEntriesNotInTimeline.get(entry.relatedEventId); - if (!relatedEntry) { + if (!relatedEntry || !relatedEntry.isNonPersisted) { + /** + * Updates for entries in timeline is handled by remoteEntries observable collection + * Updates for entries not in timeline but fetched from storage is handled in this.replaceEntries() + * This code is specific to entries fetched from HomeServer i.e NonPersistedEventEntry + */ continue; } const newEntry = this._createEntryFromRelatedEntries(entry, relatedEntry); @@ -292,7 +296,7 @@ export class Timeline { _createEntryFromRelatedEntries(entry, relatedEntry) { if (entry.isRedaction) { const newEntry = relatedEntry.clone(); - newEntry.setAsRedacted(); + newEntry.redact(entry); return newEntry; } } diff --git a/src/matrix/room/timeline/common.js b/src/matrix/room/timeline/common.js index ec1ec499..7958f754 100644 --- a/src/matrix/room/timeline/common.js +++ b/src/matrix/room/timeline/common.js @@ -17,3 +17,50 @@ limitations under the License. export function isValidFragmentId(id) { return typeof id === "number"; } + +// copied over from matrix-js-sdk, copyright 2016 OpenMarket Ltd +/* _REDACT_KEEP_KEY_MAP gives the keys we keep when an event is redacted + * + * This is specified here: + * http://matrix.org/speculator/spec/HEAD/client_server/latest.html#redactions + * + * Also: + * - We keep 'unsigned' since that is created by the local server + * - We keep user_id for backwards-compat with v1 + */ +const _REDACT_KEEP_KEY_MAP = [ + 'event_id', 'type', 'room_id', 'user_id', 'sender', 'state_key', 'prev_state', + 'content', 'unsigned', 'origin_server_ts', +].reduce(function(ret, val) { + ret[val] = 1; return ret; +}, {}); + +// a map from event type to the .content keys we keep when an event is redacted +const _REDACT_KEEP_CONTENT_MAP = { + 'm.room.member': {'membership': 1}, + 'm.room.create': {'creator': 1}, + 'm.room.join_rules': {'join_rule': 1}, + 'm.room.power_levels': {'ban': 1, 'events': 1, 'events_default': 1, + 'kick': 1, 'redact': 1, 'state_default': 1, + 'users': 1, 'users_default': 1, + }, + 'm.room.aliases': {'aliases': 1}, +}; +// end of matrix-js-sdk code + +export function redactEvent(redactionEvent, redactedEvent) { + for (const key of Object.keys(redactedEvent)) { + if (!_REDACT_KEEP_KEY_MAP[key]) { + delete redactedEvent[key]; + } + } + const { content } = redactedEvent; + const keepMap = _REDACT_KEEP_CONTENT_MAP[redactedEvent.type]; + for (const key of Object.keys(content)) { + if (!keepMap?.[key]) { + delete content[key]; + } + } + redactedEvent.unsigned = redactedEvent.unsigned || {}; + redactedEvent.unsigned.redacted_because = redactionEvent; +} diff --git a/src/matrix/room/timeline/entries/EventEntry.js b/src/matrix/room/timeline/entries/EventEntry.js index 513d427a..ea019917 100644 --- a/src/matrix/room/timeline/entries/EventEntry.js +++ b/src/matrix/room/timeline/entries/EventEntry.js @@ -26,7 +26,6 @@ export class EventEntry extends BaseEventEntry { this._decryptionResult = null; this._contextEntry = null; this._contextForEntries = null; - this._markedAsRedacted = false; } clone() { @@ -58,10 +57,6 @@ export class EventEntry extends BaseEventEntry { this._contextForEntries.push(entry); } - setAsRedacted() { - this._markedAsRedacted = true; - } - get contextForEntries() { return this._contextForEntries; } @@ -160,7 +155,7 @@ export class EventEntry extends BaseEventEntry { } get isRedacted() { - return this._markedAsRedacted || super.isRedacted || isRedacted(this._eventEntry.event); + return super.isRedacted || isRedacted(this._eventEntry.event); } get redactionReason() { diff --git a/src/matrix/room/timeline/entries/NonPersistedEventEntry.js b/src/matrix/room/timeline/entries/NonPersistedEventEntry.js index 8ec9f72e..eebc9273 100644 --- a/src/matrix/room/timeline/entries/NonPersistedEventEntry.js +++ b/src/matrix/room/timeline/entries/NonPersistedEventEntry.js @@ -15,6 +15,7 @@ limitations under the License. */ import {EventEntry} from "./EventEntry.js"; +import {redactEvent} from "../common.js"; // EventEntry but without the two properties that are populated via SyncWriter // Useful if you want to create an EventEntry that is ephemeral @@ -27,4 +28,21 @@ export class NonPersistedEventEntry extends EventEntry { get entryIndex() { throw new Error("Cannot access entryIndex for non-persisted EventEntry"); } + + /** + * This method is needed because NonPersistedEventEntry cannot rely on RelationWriter to handle redactions + */ + redact(redactionEvent) { + redactEvent(redactionEvent.event, this.event); + } + + clone() { + const clone = new NonPersistedEventEntry(this._eventEntry, this._fragmentIdComparer); + clone.updateFrom(this); + return clone; + } + + get isNonPersisted() { + return true; + } } diff --git a/src/matrix/room/timeline/persistence/RelationWriter.js b/src/matrix/room/timeline/persistence/RelationWriter.js index 60a2b618..92f97671 100644 --- a/src/matrix/room/timeline/persistence/RelationWriter.js +++ b/src/matrix/room/timeline/persistence/RelationWriter.js @@ -17,6 +17,7 @@ limitations under the License. import {EventEntry} from "../entries/EventEntry.js"; import {REDACTION_TYPE, isRedacted} from "../../common.js"; import {ANNOTATION_RELATION_TYPE, getRelation} from "../relations.js"; +import {redactEvent} from "../common.js"; export class RelationWriter { constructor({roomId, ownUserId, fragmentIdComparer}) { @@ -127,21 +128,7 @@ export class RelationWriter { // check if we're the target of a relation and remove all relations then as well txn.timelineRelations.removeAllForTarget(this._roomId, redactedEvent.event_id); - for (const key of Object.keys(redactedEvent)) { - if (!_REDACT_KEEP_KEY_MAP[key]) { - delete redactedEvent[key]; - } - } - const {content} = redactedEvent; - const keepMap = _REDACT_KEEP_CONTENT_MAP[redactedEvent.type]; - for (const key of Object.keys(content)) { - if (!keepMap?.[key]) { - delete content[key]; - } - } - redactedEvent.unsigned = redactedEvent.unsigned || {}; - redactedEvent.unsigned.redacted_because = redactionEvent; - + redactEvent(redactionEvent, redactedEvent); delete redactedStorageEntry.annotations; return true; @@ -223,35 +210,6 @@ function isObjectEmpty(obj) { return true; } -// copied over from matrix-js-sdk, copyright 2016 OpenMarket Ltd -/* _REDACT_KEEP_KEY_MAP gives the keys we keep when an event is redacted - * - * This is specified here: - * http://matrix.org/speculator/spec/HEAD/client_server/latest.html#redactions - * - * Also: - * - We keep 'unsigned' since that is created by the local server - * - We keep user_id for backwards-compat with v1 - */ -const _REDACT_KEEP_KEY_MAP = [ - 'event_id', 'type', 'room_id', 'user_id', 'sender', 'state_key', 'prev_state', - 'content', 'unsigned', 'origin_server_ts', -].reduce(function(ret, val) { - ret[val] = 1; return ret; -}, {}); - -// a map from event type to the .content keys we keep when an event is redacted -const _REDACT_KEEP_CONTENT_MAP = { - 'm.room.member': {'membership': 1}, - 'm.room.create': {'creator': 1}, - 'm.room.join_rules': {'join_rule': 1}, - 'm.room.power_levels': {'ban': 1, 'events': 1, 'events_default': 1, - 'kick': 1, 'redact': 1, 'state_default': 1, - 'users': 1, 'users_default': 1, - }, - 'm.room.aliases': {'aliases': 1}, -}; -// end of matrix-js-sdk code import {createMockStorage} from "../../../../mocks/Storage"; import {createEvent, withTextBody, withRedacts, withContent} from "../../../../mocks/event.js";