1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-25 05:23:13 +03:00

MSC3531: Hiding messages during moderation (#2041)

This commit is contained in:
David Teller
2022-01-12 11:27:33 +01:00
committed by GitHub
parent 6fc586598a
commit 96d1b30012
6 changed files with 390 additions and 11 deletions

View File

@@ -28,6 +28,7 @@ import {
EventType,
MsgType,
RelationType,
EVENT_VISIBILITY_CHANGE_TYPE,
} from "../@types/event";
import { Crypto, IEventDecryptionResult } from "../crypto";
import { deepSortedObjectEntries } from "../utils";
@@ -125,6 +126,35 @@ export interface IEventRelation {
key?: string;
}
export interface IVisibilityEventRelation extends IEventRelation {
visibility: "visible" | "hidden";
reason?: string;
}
/**
* When an event is a visibility change event, as per MSC3531,
* the visibility change implied by the event.
*/
export interface IVisibilityChange {
/**
* If `true`, the target event should be made visible.
* Otherwise, it should be hidden.
*/
visible: boolean;
/**
* The event id affected.
*/
eventId: string;
/**
* Optionally, a human-readable reason explaining why
* the event was hidden. Ignored if the event was made
* visible.
*/
reason: string | null;
}
export interface IClearEvent {
room_id?: string;
type: string;
@@ -143,6 +173,30 @@ export interface IDecryptOptions {
isRetry?: boolean;
}
/**
* Message hiding, as specified by https://github.com/matrix-org/matrix-doc/pull/3531.
*/
export type MessageVisibility = IMessageVisibilityHidden | IMessageVisibilityVisible;
/**
* Variant of `MessageVisibility` for the case in which the message should be displayed.
*/
export interface IMessageVisibilityVisible {
readonly visible: true;
}
/**
* Variant of `MessageVisibility` for the case in which the message should be hidden.
*/
export interface IMessageVisibilityHidden {
readonly visible: false;
/**
* Optionally, a human-readable reason to show to the user indicating why the
* message has been hidden (e.g. "Message Pending Moderation").
*/
readonly reason: string | null;
}
// A singleton implementing `IMessageVisibilityVisible`.
const MESSAGE_VISIBLE: IMessageVisibilityVisible = Object.freeze({ visible: true });
export class MatrixEvent extends EventEmitter {
private pushActions: IActionsObject = null;
private _replacingEvent: MatrixEvent = null;
@@ -150,6 +204,12 @@ export class MatrixEvent extends EventEmitter {
private _isCancelled = false;
private clearEvent?: IClearEvent;
/* Message hiding, as specified by https://github.com/matrix-org/matrix-doc/pull/3531.
Note: We're returning this object, so any value stored here MUST be frozen.
*/
private visibility: MessageVisibility = MESSAGE_VISIBLE;
/* curve25519 key which we believe belongs to the sender of the event. See
* getSenderKey()
*/
@@ -923,6 +983,53 @@ export class MatrixEvent extends EventEmitter {
this.event.unsigned.redacted_because = redactionEvent.event as IEvent;
}
/**
* Change the visibility of an event, as per https://github.com/matrix-org/matrix-doc/pull/3531 .
*
* @fires module:models/event.MatrixEvent#"Event.visibilityChange" if `visibilityEvent`
* caused a change in the actual visibility of this event, either by making it
* visible (if it was hidden), by making it hidden (if it was visible) or by
* changing the reason (if it was hidden).
* @param visibilityEvent event holding a hide/unhide payload, or nothing
* if the event is being reset to its original visibility (presumably
* by a visibility event being redacted).
*/
public applyVisibilityEvent(visibilityChange?: IVisibilityChange): void {
const visible = visibilityChange ? visibilityChange.visible : true;
const reason = visibilityChange ? visibilityChange.reason : null;
let change = false;
if (this.visibility.visible !== visibilityChange.visible) {
change = true;
} else if (!this.visibility.visible && this.visibility["reason"] !== reason) {
change = true;
}
if (change) {
if (visible) {
this.visibility = MESSAGE_VISIBLE;
} else {
this.visibility = Object.freeze({
visible: false,
reason: reason,
});
}
if (change) {
this.emit("Event.visibilityChange", this, visible);
}
}
}
/**
* Return instructions to display or hide the message.
*
* @returns Instructions determining whether the message
* should be displayed.
*/
public messageVisibility(): MessageVisibility {
// Note: We may return `this.visibility` without fear, as
// this is a shallow frozen object.
return this.visibility;
}
/**
* Update the content of an event in the same way it would be by the server
* if it were redacted before it was sent to us
@@ -992,6 +1099,54 @@ export class MatrixEvent extends EventEmitter {
return this.getType() === EventType.RoomRedaction;
}
/**
* Return the visibility change caused by this event,
* as per https://github.com/matrix-org/matrix-doc/pull/3531.
*
* @returns If the event is a well-formed visibility change event,
* an instance of `IVisibilityChange`, otherwise `null`.
*/
public asVisibilityChange(): IVisibilityChange | null {
if (!EVENT_VISIBILITY_CHANGE_TYPE.matches(this.getType())) {
// Not a visibility change event.
return null;
}
const relation = this.getRelation();
if (!relation || relation.rel_type != "m.reference") {
// Ill-formed, ignore this event.
return null;
}
const eventId = relation.event_id;
if (!eventId) {
// Ill-formed, ignore this event.
return null;
}
const content = this.getWireContent();
const visible = !!content.visible;
const reason = content.reason;
if (reason && typeof reason != "string") {
// Ill-formed, ignore this event.
return null;
}
// Well-formed visibility change event.
return {
visible,
reason,
eventId,
};
}
/**
* Check if this event alters the visibility of another event,
* as per https://github.com/matrix-org/matrix-doc/pull/3531.
*
* @returns {boolean} True if this event alters the visibility
* of another event.
*/
public isVisibilityEvent(): boolean {
return EVENT_VISIBILITY_CHANGE_TYPE.matches(this.getType());
}
/**
* Get the (decrypted, if necessary) redaction event JSON
* if event was redacted