Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Highlight event when any version triggered a highlight #10502

Merged
merged 6 commits into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions src/components/views/rooms/EventTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -653,15 +653,26 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
return true;
}

/**
* Determine whether an event should be highlighted
* For edited events, if a previous version of the event was highlighted
* the event should remain highlighted as the user may have been notified
* (Clearer explanation of why an event is highlighted is planned -
* https://github.com/vector-im/element-web/issues/24927)
* @returns boolean
*/
private shouldHighlight(): boolean {
if (this.props.forExport) return false;
if (this.context.timelineRenderingType === TimelineRenderingType.Notification) return false;
if (this.context.timelineRenderingType === TimelineRenderingType.ThreadsList) return false;

const actions = MatrixClientPeg.get().getPushActionsForEvent(
this.props.mxEvent.replacingEvent() || this.props.mxEvent,
);
if (!actions || !actions.tweaks) {
const cli = MatrixClientPeg.get();
const actions = cli.getPushActionsForEvent(this.props.mxEvent.replacingEvent() || this.props.mxEvent);
// get the actions for the previous version of the event too if it is an edit
const previousActions = this.props.mxEvent.replacingEvent()
? cli.getPushActionsForEvent(this.props.mxEvent)
: undefined;
if (!actions?.tweaks && !previousActions?.tweaks) {
return false;
}

Expand All @@ -670,13 +681,13 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
return false;
}

return actions.tweaks.highlight;
return !!(actions?.tweaks.highlight || previousActions?.tweaks.highlight);
}

private onSenderProfileClick = (): void => {
dis.dispatch<ComposerInsertPayload>({
action: Action.ComposerInsert,
userId: this.props.mxEvent.getSender(),
userId: this.props.mxEvent.getSender()!,
timelineRenderingType: this.context.timelineRenderingType,
});
};
Expand Down
104 changes: 102 additions & 2 deletions test/components/views/rooms/EventTile-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ limitations under the License.
*/

import * as React from "react";
import { render, waitFor, screen, act, fireEvent } from "@testing-library/react";
import { mocked } from "jest-mock";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { MatrixClient, PendingEventOrdering } from "matrix-js-sdk/src/client";
import { TweakName } from "matrix-js-sdk/src/matrix";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room";
import { DeviceTrustLevel, UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
import { IEncryptedEventInfo } from "matrix-js-sdk/src/crypto/api";
import { render, waitFor, screen, act, fireEvent } from "@testing-library/react";

import EventTile, { EventTileProps } from "../../../../src/components/views/rooms/EventTile";
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
Expand Down Expand Up @@ -73,7 +75,7 @@ describe("EventTile", () => {
stubClient();
client = MatrixClientPeg.get();

room = new Room(ROOM_ID, client, client.getUserId()!, {
room = new Room(ROOM_ID, client, client.getSafeUserId(), {
pendingEventOrdering: PendingEventOrdering.Detached,
});

Expand Down Expand Up @@ -373,4 +375,102 @@ describe("EventTile", () => {
);
});
});

describe("event highlighting", () => {
const isHighlighted = (container: HTMLElement): boolean =>
!!container.getElementsByClassName("mx_EventTile_highlight").length;

beforeEach(() => {
mocked(client.getPushActionsForEvent).mockReturnValue(null);
});

it("does not highlight message where message matches no push actions", () => {
const { container } = getComponent();

expect(client.getPushActionsForEvent).toHaveBeenCalledWith(mxEvent);
expect(isHighlighted(container)).toBeFalsy();
});

it(`does not highlight when message's push actions does not have a highlight tweak`, () => {
mocked(client.getPushActionsForEvent).mockReturnValue({ notify: true, tweaks: {} });
const { container } = getComponent();

expect(isHighlighted(container)).toBeFalsy();
});

it(`highlights when message's push actions have a highlight tweak`, () => {
mocked(client.getPushActionsForEvent).mockReturnValue({
notify: true,
tweaks: { [TweakName.Highlight]: true },
});
const { container } = getComponent();

expect(isHighlighted(container)).toBeTruthy();
});

describe("when a message has been edited", () => {
let editingEvent: MatrixEvent;

beforeEach(() => {
editingEvent = new MatrixEvent({
type: "m.room.message",
room_id: ROOM_ID,
sender: "@alice:example.org",
content: {
"msgtype": "m.text",
"body": "* edited body",
"m.new_content": {
msgtype: "m.text",
body: "edited body",
},
"m.relates_to": {
rel_type: "m.replace",
event_id: mxEvent.getId(),
},
},
});
mxEvent.makeReplaced(editingEvent);
});

it("does not highlight message where no version of message matches any push actions", () => {
const { container } = getComponent();

// get push actions for both events
expect(client.getPushActionsForEvent).toHaveBeenCalledWith(mxEvent);
expect(client.getPushActionsForEvent).toHaveBeenCalledWith(editingEvent);
expect(isHighlighted(container)).toBeFalsy();
});

it(`does not highlight when no version of message's push actions have a highlight tweak`, () => {
mocked(client.getPushActionsForEvent).mockReturnValue({ notify: true, tweaks: {} });
const { container } = getComponent();

expect(isHighlighted(container)).toBeFalsy();
});

it(`highlights when previous version of message's push actions have a highlight tweak`, () => {
mocked(client.getPushActionsForEvent).mockImplementation((event: MatrixEvent) => {
if (event === mxEvent) {
return { notify: true, tweaks: { [TweakName.Highlight]: true } };
}
return { notify: false, tweaks: {} };
});
const { container } = getComponent();

expect(isHighlighted(container)).toBeTruthy();
});

it(`highlights when new version of message's push actions have a highlight tweak`, () => {
mocked(client.getPushActionsForEvent).mockImplementation((event: MatrixEvent) => {
if (event === editingEvent) {
return { notify: true, tweaks: { [TweakName.Highlight]: true } };
}
return { notify: false, tweaks: {} };
});
const { container } = getComponent();

expect(isHighlighted(container)).toBeTruthy();
});
});
});
});