Skip to content

Commit

Permalink
Merge branch 'develop' into renovate/all-minor-patch
Browse files Browse the repository at this point in the history
  • Loading branch information
t3chguy authored Oct 18, 2023
2 parents abe5c74 + 6468d79 commit 02894e4
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 151 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cypress.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
# from creeping in. They take a long time to run and consume 4 concurrent runners.
if: github.event.workflow_run.event == 'merge_group'

uses: matrix-org/matrix-react-sdk/.github/workflows/cypress.yaml@66854039a33ed6cfe1fc635ff2daa8bb261c0b56
uses: matrix-org/matrix-react-sdk/.github/workflows/cypress.yaml@v3.83.0-rc.1
permissions:
actions: read
issues: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pull_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
if: github.event.action == 'opened'
steps:
- name: Check membership
uses: tspascoal/get-user-teams-membership@37c08f7b52a72ca95d12af2e7ab2553ca9adf13b # v2
uses: tspascoal/get-user-teams-membership@ba78054988f58bea69b7c6136d563236f8ed2fc0 # v3
id: teams
with:
username: ${{ github.event.pull_request.user.login }}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
"eslint-plugin-tsdoc": "^0.2.17",
"eslint-plugin-unicorn": "^48.0.0",
"exorcist": "^2.0.0",
"fake-indexeddb": "^4.0.0",
"fake-indexeddb": "^5.0.0",
"fetch-mock-jest": "^1.5.1",
"jest": "^29.0.0",
"jest-environment-jsdom": "^29.0.0",
Expand Down
110 changes: 108 additions & 2 deletions spec/unit/models/event.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { MockedObject } from "jest-mock";

import { MatrixEvent, MatrixEventEvent } from "../../../src/models/event";
import { emitPromise } from "../../test-utils/test-utils";
import { Crypto, IEventDecryptionResult } from "../../../src/crypto";
import { IAnnotatedPushRule, PushRuleActionName, TweakName } from "../../../src";
import {
IAnnotatedPushRule,
MatrixClient,
PushRuleActionName,
Room,
THREAD_RELATION_TYPE,
TweakName,
} from "../../../src";

describe("MatrixEvent", () => {
it("should create copies of itself", () => {
Expand Down Expand Up @@ -77,17 +86,98 @@ describe("MatrixEvent", () => {
expect(ev.getWireContent().body).toBeUndefined();
expect(ev.getWireContent().ciphertext).toBe("xyz");

const mockClient = {} as unknown as MockedObject<MatrixClient>;
const room = new Room("!roomid:e.xyz", mockClient, "myname");
const redaction = new MatrixEvent({
type: "m.room.redaction",
redacts: ev.getId(),
});

ev.makeRedacted(redaction);
ev.makeRedacted(redaction, room);
expect(ev.getContent().body).toBeUndefined();
expect(ev.getWireContent().body).toBeUndefined();
expect(ev.getWireContent().ciphertext).toBeUndefined();
});

it("should remain in the main timeline when redacted", async () => {
// Given an event in the main timeline
const mockClient = {
supportsThreads: jest.fn().mockReturnValue(true),
decryptEventIfNeeded: jest.fn().mockReturnThis(),
getUserId: jest.fn().mockReturnValue("@user:server"),
} as unknown as MockedObject<MatrixClient>;
const room = new Room("!roomid:e.xyz", mockClient, "myname");
const ev = new MatrixEvent({
type: "m.room.message",
content: {
body: "Test",
},
event_id: "$event1:server",
});

await room.addLiveEvents([ev]);
await room.createThreadsTimelineSets();
expect(ev.threadRootId).toBeUndefined();
expect(mainTimelineLiveEventIds(room)).toEqual(["$event1:server"]);

// When I redact it
const redaction = new MatrixEvent({
type: "m.room.redaction",
redacts: ev.getId(),
});
ev.makeRedacted(redaction, room);

// Then it remains in the main timeline
expect(ev.threadRootId).toBeUndefined();
expect(mainTimelineLiveEventIds(room)).toEqual(["$event1:server"]);
});

it("should move into the main timeline when redacted", async () => {
// Given an event in a thread
const mockClient = {
supportsThreads: jest.fn().mockReturnValue(true),
decryptEventIfNeeded: jest.fn().mockReturnThis(),
getUserId: jest.fn().mockReturnValue("@user:server"),
} as unknown as MockedObject<MatrixClient>;
const room = new Room("!roomid:e.xyz", mockClient, "myname");
const threadRoot = new MatrixEvent({
type: "m.room.message",
content: {
body: "threadRoot",
},
event_id: "$threadroot:server",
});
const ev = new MatrixEvent({
type: "m.room.message",
content: {
"body": "Test",
"m.relates_to": {
rel_type: THREAD_RELATION_TYPE.name,
event_id: "$threadroot:server",
},
},
event_id: "$event1:server",
});

await room.addLiveEvents([threadRoot, ev]);
await room.createThreadsTimelineSets();
expect(ev.threadRootId).toEqual("$threadroot:server");
expect(mainTimelineLiveEventIds(room)).toEqual(["$threadroot:server"]);
expect(threadLiveEventIds(room, 0)).toEqual(["$threadroot:server", "$event1:server"]);

// When I redact it
const redaction = new MatrixEvent({
type: "m.room.redaction",
redacts: ev.getId(),
});
ev.makeRedacted(redaction, room);

// Then it disappears from the thread and appears in the main timeline
expect(ev.threadRootId).toBeUndefined();
expect(mainTimelineLiveEventIds(room)).toEqual(["$threadroot:server", "$event1:server"]);
expect(threadLiveEventIds(room, 0)).not.toContain("$event1:server");
});

describe("applyVisibilityEvent", () => {
it("should emit VisibilityChange if a change was made", async () => {
const ev = new MatrixEvent({
Expand Down Expand Up @@ -330,3 +420,19 @@ describe("MatrixEvent", () => {
expect(stateEvent.threadRootId).toBeUndefined();
});
});

function mainTimelineLiveEventIds(room: Room): Array<string> {
return room
.getLiveTimeline()
.getEvents()
.map((e) => e.getId()!);
}

function threadLiveEventIds(room: Room, threadIndex: number): Array<string> {
return room
.getThreads()
[threadIndex].getUnfilteredTimelineSet()
.getLiveTimeline()
.getEvents()
.map((e) => e.getId()!);
}
9 changes: 7 additions & 2 deletions spec/unit/room-state.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { M_BEACON } from "../../src/@types/beacon";
import { MatrixClient } from "../../src/client";
import { DecryptionError } from "../../src/crypto/algorithms";
import { defer } from "../../src/utils";
import { Room } from "../../src/models/room";

describe("RoomState", function () {
const roomId = "!foo:bar";
Expand Down Expand Up @@ -362,9 +363,11 @@ describe("RoomState", function () {
});

it("does not add redacted beacon info events to state", () => {
const mockClient = {} as unknown as MockedObject<MatrixClient>;
const redactedBeaconEvent = makeBeaconInfoEvent(userA, roomId);
const redactionEvent = new MatrixEvent({ type: "m.room.redaction" });
redactedBeaconEvent.makeRedacted(redactionEvent);
const room = new Room(roomId, mockClient, userA);
redactedBeaconEvent.makeRedacted(redactionEvent, room);
const emitSpy = jest.spyOn(state, "emit");

state.setStateEvents([redactedBeaconEvent]);
Expand Down Expand Up @@ -394,11 +397,13 @@ describe("RoomState", function () {
});

it("destroys and removes redacted beacon events", () => {
const mockClient = {} as unknown as MockedObject<MatrixClient>;
const beaconId = "$beacon1";
const beaconEvent = makeBeaconInfoEvent(userA, roomId, { isLive: true }, beaconId);
const redactedBeaconEvent = makeBeaconInfoEvent(userA, roomId, { isLive: true }, beaconId);
const redactionEvent = new MatrixEvent({ type: "m.room.redaction", redacts: beaconEvent.getId() });
redactedBeaconEvent.makeRedacted(redactionEvent);
const room = new Room(roomId, mockClient, userA);
redactedBeaconEvent.makeRedacted(redactionEvent, room);

state.setStateEvents([beaconEvent]);
const beaconInstance = state.beacons.get(getBeaconInfoIdentifier(beaconEvent));
Expand Down
2 changes: 1 addition & 1 deletion spec/unit/room.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3564,7 +3564,7 @@ describe("Room", function () {
expect(room.polls.get(pollStartEvent.getId()!)).toBeTruthy();

const redactedEvent = new MatrixEvent({ type: "m.room.redaction" });
pollStartEvent.makeRedacted(redactedEvent);
pollStartEvent.makeRedacted(redactedEvent, room);

await flushPromises();

Expand Down
46 changes: 39 additions & 7 deletions src/models/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import { DecryptionError } from "../crypto/algorithms";
import { CryptoBackend } from "../common-crypto/CryptoBackend";
import { WITHHELD_MESSAGES } from "../crypto/OlmDevice";
import { IAnnotatedPushRule } from "../@types/PushRules";
import { Room } from "./room";
import { EventTimeline } from "./event-timeline";

export { EventStatus } from "./event-status";

Expand Down Expand Up @@ -414,9 +416,12 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
}

/**
* Gets the event as though it would appear unencrypted. If the event is already not
* encrypted, it is simply returned as-is.
* @returns The event in wire format.
* Gets the event as it would appear if it had been sent unencrypted.
*
* If the event is encrypted, we attempt to mock up an event as it would have looked had the sender not encrypted it.
* If the event is not encrypted, a copy of it is simply returned as-is.
*
* @returns A shallow copy of the event, in wire format, as it would have been had it not been encrypted.
*/
public getEffectiveEvent(): IEvent {
const content = Object.assign({}, this.getContent()); // clone for mutation
Expand Down Expand Up @@ -1132,13 +1137,19 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
return this.visibility;
}

/**
* @deprecated In favor of the overload that includes a Room argument
*/
public makeRedacted(redactionEvent: MatrixEvent): void;
/**
* 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
*
* @param redactionEvent - event causing the redaction
* @param room - the room in which the event exists
*/
public makeRedacted(redactionEvent: MatrixEvent): void {
public makeRedacted(redactionEvent: MatrixEvent, room: Room): void;
public makeRedacted(redactionEvent: MatrixEvent, room?: Room): void {
// quick sanity-check
if (!redactionEvent.event) {
throw new Error("invalid redactionEvent in makeRedacted");
Expand Down Expand Up @@ -1182,6 +1193,21 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
}
}

// If the redacted event was in a thread
if (room && this.threadRootId && this.threadRootId !== this.getId()) {
// Remove it from its thread
this.thread?.timelineSet.removeEvent(this.getId()!);
this.setThread(undefined);

// And insert it into the main timeline
const timeline = room.getLiveTimeline();
// We use insertEventIntoTimeline to insert it in timestamp order,
// because we don't know where it should go (until we have MSC4033).
timeline
.getTimelineSet()
.insertEventIntoTimeline(this, timeline, timeline.getState(EventTimeline.FORWARDS)!);
}

this.invalidateExtensibleEvent();
}

Expand Down Expand Up @@ -1593,15 +1619,21 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
}

/**
* Summarise the event as JSON. This is currently used by React SDK's view
* event source feature and Seshat's event indexing, so take care when
* adjusting the output here.
* Summarise the event as JSON.
*
* If encrypted, include both the decrypted and encrypted view of the event.
*
* This is named `toJSON` for use with `JSON.stringify` which checks objects
* for functions named `toJSON` and will call them to customise the output
* if they are defined.
*
* **WARNING** Do not log the result of this method; otherwise, it will end up
* in rageshakes, leading to a privacy violation.
*
* @deprecated Prefer to use {@link MatrixEvent#getEffectiveEvent} or similar.
* This method will be removed soon; it is too easy to use it accidentally
* and cause a privacy violation (cf https://github.com/vector-im/element-web/issues/26380).
* In any case, the value it returns is not a faithful serialization of the object.
*/
public toJSON(): object {
const event = this.getEffectiveEvent();
Expand Down
2 changes: 1 addition & 1 deletion src/models/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2329,7 +2329,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
// if we know about this event, redact its contents now.
const redactedEvent = redactId ? this.findEventById(redactId) : undefined;
if (redactedEvent) {
redactedEvent.makeRedacted(event);
redactedEvent.makeRedacted(event, this);

// If this is in the current state, replace it with the redacted version
if (redactedEvent.isState()) {
Expand Down
Loading

0 comments on commit 02894e4

Please sign in to comment.