From 87fb4937834484417e57fa0992395d4311f42abe Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 15 Mar 2023 12:47:21 +0000 Subject: [PATCH] Fix pinned messages card saying nothing pinned while loading (#10385) --- .../views/right_panel/PinnedMessagesCard.tsx | 85 ++++++++++--------- .../right_panel/PinnedMessagesCard-test.tsx | 28 +++++- 2 files changed, 70 insertions(+), 43 deletions(-) diff --git a/src/components/views/right_panel/PinnedMessagesCard.tsx b/src/components/views/right_panel/PinnedMessagesCard.tsx index 736ff03f2b..7e1ec08f38 100644 --- a/src/components/views/right_panel/PinnedMessagesCard.tsx +++ b/src/components/views/right_panel/PinnedMessagesCard.tsx @@ -45,50 +45,53 @@ interface IProps { onClose(): void; } +function getPinnedEventIds(room?: Room): string[] { + return room?.currentState.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent()?.pinned ?? []; +} + export const usePinnedEvents = (room?: Room): string[] => { - const [pinnedEvents, setPinnedEvents] = useState([]); + const [pinnedEvents, setPinnedEvents] = useState(getPinnedEventIds(room)); const update = useCallback( (ev?: MatrixEvent) => { - if (!room) return; if (ev && ev.getType() !== EventType.RoomPinnedEvents) return; - setPinnedEvents( - room.currentState.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent()?.pinned || [], - ); + setPinnedEvents(getPinnedEventIds(room)); }, [room], ); useTypedEventEmitter(room?.currentState, RoomStateEvent.Events, update); useEffect(() => { - update(); + setPinnedEvents(getPinnedEventIds(room)); return () => { setPinnedEvents([]); }; - }, [update]); + }, [room]); return pinnedEvents; }; -export const useReadPinnedEvents = (room: Room): Set => { +function getReadPinnedEventIds(room?: Room): Set { + return new Set(room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids ?? []); +} + +export const useReadPinnedEvents = (room?: Room): Set => { const [readPinnedEvents, setReadPinnedEvents] = useState>(new Set()); const update = useCallback( (ev?: MatrixEvent) => { - if (!room) return; if (ev && ev.getType() !== ReadPinsEventId) return; - const readPins = room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids; - setReadPinnedEvents(new Set(readPins || [])); + setReadPinnedEvents(getReadPinnedEventIds(room)); }, [room], ); useTypedEventEmitter(room, RoomEvent.AccountData, update); useEffect(() => { - update(); + setReadPinnedEvents(getReadPinnedEventIds(room)); return () => { setReadPinnedEvents(new Set()); }; - }, [update]); + }, [room]); return readPinnedEvents; }; @@ -157,34 +160,8 @@ const PinnedMessagesCard: React.FC = ({ room, onClose, permalinkCreator null, ); - let content; - if (!pinnedEvents) { - content = ; - } else if (pinnedEvents.length > 0) { - const onUnpinClicked = async (event: MatrixEvent): Promise => { - const pinnedEvents = room.currentState.getStateEvents(EventType.RoomPinnedEvents, ""); - if (pinnedEvents?.getContent()?.pinned) { - const pinned = pinnedEvents.getContent().pinned; - const index = pinned.indexOf(event.getId()); - if (index !== -1) { - pinned.splice(index, 1); - await cli.sendStateEvent(room.roomId, EventType.RoomPinnedEvents, { pinned }, ""); - } - } - }; - - // show them in reverse, with latest pinned at the top - content = filterBoolean(pinnedEvents) - .reverse() - .map((ev) => ( - onUnpinClicked(ev) : undefined} - permalinkCreator={permalinkCreator} - /> - )); - } else { + let content: JSX.Element[] | JSX.Element | undefined; + if (!pinnedEventIds.length) { content = (
@@ -215,6 +192,32 @@ const PinnedMessagesCard: React.FC = ({ room, onClose, permalinkCreator
); + } else if (pinnedEvents?.length) { + const onUnpinClicked = async (event: MatrixEvent): Promise => { + const pinnedEvents = room.currentState.getStateEvents(EventType.RoomPinnedEvents, ""); + if (pinnedEvents?.getContent()?.pinned) { + const pinned = pinnedEvents.getContent().pinned; + const index = pinned.indexOf(event.getId()); + if (index !== -1) { + pinned.splice(index, 1); + await cli.sendStateEvent(room.roomId, EventType.RoomPinnedEvents, { pinned }, ""); + } + } + }; + + // show them in reverse, with latest pinned at the top + content = filterBoolean(pinnedEvents) + .reverse() + .map((ev) => ( + onUnpinClicked(ev) : undefined} + permalinkCreator={permalinkCreator} + /> + )); + } else { + content = ; } return ( diff --git a/test/components/views/right_panel/PinnedMessagesCard-test.tsx b/test/components/views/right_panel/PinnedMessagesCard-test.tsx index a1d5d11652..34d9eeb8b5 100644 --- a/test/components/views/right_panel/PinnedMessagesCard-test.tsx +++ b/test/components/views/right_panel/PinnedMessagesCard-test.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React from "react"; -import { render, act, RenderResult } from "@testing-library/react"; +import { render, act, RenderResult, fireEvent, waitForElementToBeRemoved, screen } from "@testing-library/react"; import { mocked } from "jest-mock"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { EventType, RelationType, MsgType } from "matrix-js-sdk/src/@types/event"; @@ -240,7 +240,7 @@ describe("", () => { ...PollResponseEvent.from([answers[option as number].id], poll.getId()!).serialize(), event: true, room: "!room:example.org", - user: user as string, + user, }), ); @@ -284,4 +284,28 @@ describe("", () => { expect(pinTile[0].querySelectorAll(".mx_PollOption_optionVoteCount")[0]).toHaveTextContent("2 votes"); expect([...pinTile[0].querySelectorAll(".mx_PollOption_optionVoteCount")].at(-1)).toHaveTextContent("1 vote"); }); + + it("should allow admins to unpin messages", async () => { + const nonLocalPins = [pin1]; + const room = mkRoom([], nonLocalPins); + jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true); + const sendStateEvent = jest.spyOn(cli, "sendStateEvent"); + + const pins = await mountPins(room); + const pinTile = pins.container.querySelectorAll(".mx_PinnedEventTile"); + expect(pinTile).toHaveLength(1); + + fireEvent.click(pinTile[0].querySelector(".mx_PinnedEventTile_unpinButton")!); + expect(sendStateEvent).toHaveBeenCalledWith(room.roomId, "m.room.pinned_events", { pinned: [] }, ""); + + nonLocalPins.pop(); + await Promise.all([waitForElementToBeRemoved(pinTile[0]), emitPinUpdates(room)]); + }); + + it("should show spinner whilst loading", async () => { + const room = mkRoom([], [pin1]); + mountPins(room); + const spinner = await screen.findByTestId("spinner"); + await waitForElementToBeRemoved(spinner); + }); });