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

Commit ea3c5cf

Browse files
authored
Fix pin/unpin slowness and non refresh from the message action bar (#12934)
* Improve PinningUtils.ts doc and use common methods to check pin or unpin. Removed unused methods. * Send room account data and state event in parallel * Rerender MessageActionBar.tsx if there is a room pinned event * Update pinning util tests * Add test for room pinned events in MessageActionBar-test.tsx
1 parent 43941ef commit ea3c5cf

File tree

5 files changed

+57
-16
lines changed

5 files changed

+57
-16
lines changed

src/components/views/messages/MessageActionBar.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import {
2424
MsgType,
2525
RelationType,
2626
M_BEACON_INFO,
27+
EventTimeline,
28+
RoomStateEvent,
29+
EventType,
2730
} from "matrix-js-sdk/src/matrix";
2831
import classNames from "classnames";
2932
import { Icon as PinIcon } from "@vector-im/compound-design-tokens/icons/pin.svg";
@@ -278,12 +281,20 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
278281
this.props.mxEvent.once(MatrixEventEvent.Decrypted, this.onDecrypted);
279282
}
280283
this.props.mxEvent.on(MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction);
284+
this.context.room
285+
?.getLiveTimeline()
286+
.getState(EventTimeline.FORWARDS)
287+
?.on(RoomStateEvent.Events, this.onRoomEvent);
281288
}
282289

283290
public componentWillUnmount(): void {
284291
this.props.mxEvent.off(MatrixEventEvent.Status, this.onSent);
285292
this.props.mxEvent.off(MatrixEventEvent.Decrypted, this.onDecrypted);
286293
this.props.mxEvent.off(MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction);
294+
this.context.room
295+
?.getLiveTimeline()
296+
.getState(EventTimeline.FORWARDS)
297+
?.off(RoomStateEvent.Events, this.onRoomEvent);
287298
}
288299

289300
private onDecrypted = (): void => {
@@ -297,6 +308,12 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
297308
this.forceUpdate();
298309
};
299310

311+
private onRoomEvent = (event?: MatrixEvent): void => {
312+
// If the event is pinned or unpinned, rerender the component.
313+
if (!event || event.getType() !== EventType.RoomPinnedEvents) return;
314+
this.forceUpdate();
315+
};
316+
300317
private onSent = (): void => {
301318
// When an event is sent and echoed the possible actions change.
302319
this.forceUpdate();

src/utils/EventUtils.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,10 +286,6 @@ export function hasThreadSummary(event: MatrixEvent): boolean {
286286
return event.isThreadRoot && !!event.getThread()?.length && !!event.getThread()!.replyToEvent;
287287
}
288288

289-
export function canPinEvent(event: MatrixEvent): boolean {
290-
return !M_BEACON_INFO.matches(event.getType());
291-
}
292-
293289
export const highlightEvent = (roomId: string, eventId: string): void => {
294290
defaultDispatcher.dispatch<ViewRoomPayload>({
295291
action: Action.ViewRoom,

src/utils/PinningUtils.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ limitations under the License.
1616

1717
import { MatrixEvent, EventType, M_POLL_START, MatrixClient, EventTimeline } from "matrix-js-sdk/src/matrix";
1818

19-
import { canPinEvent, isContentActionable } from "./EventUtils";
19+
import { isContentActionable } from "./EventUtils";
2020
import SettingsStore from "../settings/SettingsStore";
2121
import { ReadPinsEventId } from "../components/views/right_panel/types";
2222

@@ -31,7 +31,8 @@ export default class PinningUtils {
3131
];
3232

3333
/**
34-
* Determines if the given event may be pinned.
34+
* Determines if the given event can be pinned.
35+
* This is a simple check to see if the event is of a type that can be pinned.
3536
* @param {MatrixEvent} event The event to check.
3637
* @return {boolean} True if the event may be pinned, false otherwise.
3738
*/
@@ -62,7 +63,8 @@ export default class PinningUtils {
6263
}
6364

6465
/**
65-
* Determines if the given event may be pinned or unpinned.
66+
* Determines if the given event may be pinned or unpinned by the current user.
67+
* This checks if the user has the necessary permissions to pin or unpin the event, and if the event is pinnable.
6668
* @param matrixClient
6769
* @param mxEvent
6870
*/
@@ -77,7 +79,7 @@ export default class PinningUtils {
7779
room
7880
.getLiveTimeline()
7981
.getState(EventTimeline.FORWARDS)
80-
?.mayClientSendStateEvent(EventType.RoomPinnedEvents, matrixClient) && canPinEvent(mxEvent),
82+
?.mayClientSendStateEvent(EventType.RoomPinnedEvents, matrixClient) && PinningUtils.isPinnable(mxEvent),
8183
);
8284
}
8385

@@ -101,16 +103,21 @@ export default class PinningUtils {
101103
?.getStateEvents(EventType.RoomPinnedEvents, "")
102104
?.getContent().pinned || [];
103105

106+
let roomAccountDataPromise: Promise<{} | void> = Promise.resolve();
104107
// If the event is already pinned, unpin it
105108
if (pinnedIds.includes(eventId)) {
106109
pinnedIds.splice(pinnedIds.indexOf(eventId), 1);
107110
} else {
108111
// Otherwise, pin it
109112
pinnedIds.push(eventId);
110-
await matrixClient.setRoomAccountData(room.roomId, ReadPinsEventId, {
113+
// We don't want to wait for the roomAccountDataPromise to resolve before sending the state event
114+
roomAccountDataPromise = matrixClient.setRoomAccountData(room.roomId, ReadPinsEventId, {
111115
event_ids: [...(room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids || []), eventId],
112116
});
113117
}
114-
await matrixClient.sendStateEvent(room.roomId, EventType.RoomPinnedEvents, { pinned: pinnedIds }, "");
118+
await Promise.all([
119+
matrixClient.sendStateEvent(room.roomId, EventType.RoomPinnedEvents, { pinned: pinnedIds }, ""),
120+
roomAccountDataPromise,
121+
]);
115122
}
116123
}

test/components/views/messages/MessageActionBar-test.tsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ limitations under the License.
1515
*/
1616

1717
import React from "react";
18-
import { act, render, fireEvent } from "@testing-library/react";
18+
import { act, render, fireEvent, screen, waitFor } from "@testing-library/react";
1919
import {
2020
EventType,
2121
EventStatus,
@@ -26,6 +26,7 @@ import {
2626
FeatureSupport,
2727
Thread,
2828
EventTimeline,
29+
RoomStateEvent,
2930
} from "matrix-js-sdk/src/matrix";
3031

3132
import MessageActionBar from "../../../../src/components/views/messages/MessageActionBar";
@@ -41,6 +42,7 @@ import { IRoomState } from "../../../../src/components/structures/RoomView";
4142
import dispatcher from "../../../../src/dispatcher/dispatcher";
4243
import SettingsStore from "../../../../src/settings/SettingsStore";
4344
import { Action } from "../../../../src/dispatcher/actions";
45+
import PinningUtils from "../../../../src/utils/PinningUtils";
4446

4547
jest.mock("../../../../src/dispatcher/dispatcher");
4648

@@ -119,6 +121,7 @@ describe("<MessageActionBar />", () => {
119121
timelineRenderingType: TimelineRenderingType.Room,
120122
canSendMessages: true,
121123
canReact: true,
124+
room,
122125
} as unknown as IRoomState;
123126
const getComponent = (props = {}, roomContext: Partial<IRoomState> = {}) =>
124127
render(
@@ -476,6 +479,7 @@ describe("<MessageActionBar />", () => {
476479
beforeEach(() => {
477480
// enable pin button
478481
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
482+
jest.spyOn(PinningUtils, "isPinned").mockReturnValue(false);
479483
});
480484

481485
afterEach(() => {
@@ -499,5 +503,25 @@ describe("<MessageActionBar />", () => {
499503
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
500504
expect(queryByLabelText("Pin")).toBeTruthy();
501505
});
506+
507+
it("should listen to room pinned events", async () => {
508+
getComponent({ mxEvent: alicesMessageEvent });
509+
expect(screen.getByLabelText("Pin")).toBeInTheDocument();
510+
511+
// Event is considered pinned
512+
jest.spyOn(PinningUtils, "isPinned").mockReturnValue(true);
513+
// Emit that the room pinned events have changed
514+
const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
515+
roomState.emit(
516+
RoomStateEvent.Events,
517+
{
518+
getType: () => EventType.RoomPinnedEvents,
519+
} as MatrixEvent,
520+
roomState,
521+
null,
522+
);
523+
524+
await waitFor(() => expect(screen.getByLabelText("Unpin")).toBeInTheDocument());
525+
});
502526
});
503527
});

test/utils/PinningUtils-test.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { mocked } from "jest-mock";
2020
import { createTestClient } from "../test-utils";
2121
import PinningUtils from "../../src/utils/PinningUtils";
2222
import SettingsStore from "../../src/settings/SettingsStore";
23-
import { canPinEvent, isContentActionable } from "../../src/utils/EventUtils";
23+
import { isContentActionable } from "../../src/utils/EventUtils";
2424
import { ReadPinsEventId } from "../../src/components/views/right_panel/types";
2525

2626
jest.mock("../../src/utils/EventUtils", () => {
@@ -35,7 +35,6 @@ describe("PinningUtils", () => {
3535
const userId = "@alice:example.org";
3636

3737
const mockedIsContentActionable = mocked(isContentActionable);
38-
const mockedCanPinEvent = mocked(canPinEvent);
3938

4039
let matrixClient: MatrixClient;
4140
let room: Room;
@@ -63,7 +62,6 @@ describe("PinningUtils", () => {
6362
// Enable feature pinning
6463
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
6564
mockedIsContentActionable.mockImplementation(() => true);
66-
mockedCanPinEvent.mockImplementation(() => true);
6765

6866
matrixClient = createTestClient();
6967
room = new Room(roomId, matrixClient, userId);
@@ -171,8 +169,7 @@ describe("PinningUtils", () => {
171169
});
172170

173171
test("should return false if event is not pinnable", () => {
174-
mockedCanPinEvent.mockReturnValue(false);
175-
const event = makePinEvent();
172+
const event = makePinEvent({ type: EventType.RoomCreate });
176173

177174
expect(PinningUtils.canPinOrUnpin(matrixClient, event)).toBe(false);
178175
});

0 commit comments

Comments
 (0)