forked from mystiq/hydrogen-web
Refactor + put redaction in NonPersistedEventEntry
This commit is contained in:
parent
8cc04e4c25
commit
9d161a0bcf
5 changed files with 78 additions and 56 deletions
|
@ -254,7 +254,7 @@ export class Timeline {
|
||||||
/** @package */
|
/** @package */
|
||||||
addEntries(newEntries) {
|
addEntries(newEntries) {
|
||||||
this._addLocalRelationsToNewRemoteEntries(newEntries);
|
this._addLocalRelationsToNewRemoteEntries(newEntries);
|
||||||
this._updateEntriesNotInTimeline(newEntries);
|
this._updateEntriesFetchedFromHomeserver(newEntries);
|
||||||
this._moveEntryToRemoteEntries(newEntries);
|
this._moveEntryToRemoteEntries(newEntries);
|
||||||
this._remoteEntries.setManySorted(newEntries);
|
this._remoteEntries.setManySorted(newEntries);
|
||||||
this._loadContextEntriesWhereNeeded(newEntries);
|
this._loadContextEntriesWhereNeeded(newEntries);
|
||||||
|
@ -262,13 +262,17 @@ export class Timeline {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update entries based on newly received events.
|
* 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 specific to events that are not in the timeline but had to be fetched from the homeserver.
|
||||||
* This is only for the events that are not in the timeline but had to fetched from elsewhere to render reply previews.
|
|
||||||
*/
|
*/
|
||||||
_updateEntriesNotInTimeline(entries) {
|
_updateEntriesFetchedFromHomeserver(entries) {
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const relatedEntry = this._contextEntriesNotInTimeline.get(entry.relatedEventId);
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
const newEntry = this._createEntryFromRelatedEntries(entry, relatedEntry);
|
const newEntry = this._createEntryFromRelatedEntries(entry, relatedEntry);
|
||||||
|
@ -292,7 +296,7 @@ export class Timeline {
|
||||||
_createEntryFromRelatedEntries(entry, relatedEntry) {
|
_createEntryFromRelatedEntries(entry, relatedEntry) {
|
||||||
if (entry.isRedaction) {
|
if (entry.isRedaction) {
|
||||||
const newEntry = relatedEntry.clone();
|
const newEntry = relatedEntry.clone();
|
||||||
newEntry.setAsRedacted();
|
newEntry.redact(entry);
|
||||||
return newEntry;
|
return newEntry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,3 +17,50 @@ limitations under the License.
|
||||||
export function isValidFragmentId(id) {
|
export function isValidFragmentId(id) {
|
||||||
return typeof id === "number";
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ export class EventEntry extends BaseEventEntry {
|
||||||
this._decryptionResult = null;
|
this._decryptionResult = null;
|
||||||
this._contextEntry = null;
|
this._contextEntry = null;
|
||||||
this._contextForEntries = null;
|
this._contextForEntries = null;
|
||||||
this._markedAsRedacted = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
clone() {
|
||||||
|
@ -58,10 +57,6 @@ export class EventEntry extends BaseEventEntry {
|
||||||
this._contextForEntries.push(entry);
|
this._contextForEntries.push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAsRedacted() {
|
|
||||||
this._markedAsRedacted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
get contextForEntries() {
|
get contextForEntries() {
|
||||||
return this._contextForEntries;
|
return this._contextForEntries;
|
||||||
}
|
}
|
||||||
|
@ -160,7 +155,7 @@ export class EventEntry extends BaseEventEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
get isRedacted() {
|
get isRedacted() {
|
||||||
return this._markedAsRedacted || super.isRedacted || isRedacted(this._eventEntry.event);
|
return super.isRedacted || isRedacted(this._eventEntry.event);
|
||||||
}
|
}
|
||||||
|
|
||||||
get redactionReason() {
|
get redactionReason() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {EventEntry} from "./EventEntry.js";
|
import {EventEntry} from "./EventEntry.js";
|
||||||
|
import {redactEvent} from "../common.js";
|
||||||
|
|
||||||
// EventEntry but without the two properties that are populated via SyncWriter
|
// EventEntry but without the two properties that are populated via SyncWriter
|
||||||
// Useful if you want to create an EventEntry that is ephemeral
|
// Useful if you want to create an EventEntry that is ephemeral
|
||||||
|
@ -27,4 +28,21 @@ export class NonPersistedEventEntry extends EventEntry {
|
||||||
get entryIndex() {
|
get entryIndex() {
|
||||||
throw new Error("Cannot access entryIndex for non-persisted EventEntry");
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
import {EventEntry} from "../entries/EventEntry.js";
|
import {EventEntry} from "../entries/EventEntry.js";
|
||||||
import {REDACTION_TYPE, isRedacted} from "../../common.js";
|
import {REDACTION_TYPE, isRedacted} from "../../common.js";
|
||||||
import {ANNOTATION_RELATION_TYPE, getRelation} from "../relations.js";
|
import {ANNOTATION_RELATION_TYPE, getRelation} from "../relations.js";
|
||||||
|
import {redactEvent} from "../common.js";
|
||||||
|
|
||||||
export class RelationWriter {
|
export class RelationWriter {
|
||||||
constructor({roomId, ownUserId, fragmentIdComparer}) {
|
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
|
// check if we're the target of a relation and remove all relations then as well
|
||||||
txn.timelineRelations.removeAllForTarget(this._roomId, redactedEvent.event_id);
|
txn.timelineRelations.removeAllForTarget(this._roomId, redactedEvent.event_id);
|
||||||
|
|
||||||
for (const key of Object.keys(redactedEvent)) {
|
redactEvent(redactionEvent, 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;
|
|
||||||
|
|
||||||
delete redactedStorageEntry.annotations;
|
delete redactedStorageEntry.annotations;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -223,35 +210,6 @@ function isObjectEmpty(obj) {
|
||||||
return true;
|
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 {createMockStorage} from "../../../../mocks/Storage";
|
||||||
import {createEvent, withTextBody, withRedacts, withContent} from "../../../../mocks/event.js";
|
import {createEvent, withTextBody, withRedacts, withContent} from "../../../../mocks/event.js";
|
||||||
|
|
Loading…
Reference in a new issue