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

Commit 87fb493

Browse files
authored
Fix pinned messages card saying nothing pinned while loading (#10385)
1 parent 0c38bd7 commit 87fb493

File tree

2 files changed

+70
-43
lines changed

2 files changed

+70
-43
lines changed

src/components/views/right_panel/PinnedMessagesCard.tsx

Lines changed: 44 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -45,50 +45,53 @@ interface IProps {
4545
onClose(): void;
4646
}
4747

48+
function getPinnedEventIds(room?: Room): string[] {
49+
return room?.currentState.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent()?.pinned ?? [];
50+
}
51+
4852
export const usePinnedEvents = (room?: Room): string[] => {
49-
const [pinnedEvents, setPinnedEvents] = useState<string[]>([]);
53+
const [pinnedEvents, setPinnedEvents] = useState<string[]>(getPinnedEventIds(room));
5054

5155
const update = useCallback(
5256
(ev?: MatrixEvent) => {
53-
if (!room) return;
5457
if (ev && ev.getType() !== EventType.RoomPinnedEvents) return;
55-
setPinnedEvents(
56-
room.currentState.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent()?.pinned || [],
57-
);
58+
setPinnedEvents(getPinnedEventIds(room));
5859
},
5960
[room],
6061
);
6162

6263
useTypedEventEmitter(room?.currentState, RoomStateEvent.Events, update);
6364
useEffect(() => {
64-
update();
65+
setPinnedEvents(getPinnedEventIds(room));
6566
return () => {
6667
setPinnedEvents([]);
6768
};
68-
}, [update]);
69+
}, [room]);
6970
return pinnedEvents;
7071
};
7172

72-
export const useReadPinnedEvents = (room: Room): Set<string> => {
73+
function getReadPinnedEventIds(room?: Room): Set<string> {
74+
return new Set(room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids ?? []);
75+
}
76+
77+
export const useReadPinnedEvents = (room?: Room): Set<string> => {
7378
const [readPinnedEvents, setReadPinnedEvents] = useState<Set<string>>(new Set());
7479

7580
const update = useCallback(
7681
(ev?: MatrixEvent) => {
77-
if (!room) return;
7882
if (ev && ev.getType() !== ReadPinsEventId) return;
79-
const readPins = room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids;
80-
setReadPinnedEvents(new Set(readPins || []));
83+
setReadPinnedEvents(getReadPinnedEventIds(room));
8184
},
8285
[room],
8386
);
8487

8588
useTypedEventEmitter(room, RoomEvent.AccountData, update);
8689
useEffect(() => {
87-
update();
90+
setReadPinnedEvents(getReadPinnedEventIds(room));
8891
return () => {
8992
setReadPinnedEvents(new Set());
9093
};
91-
}, [update]);
94+
}, [room]);
9295
return readPinnedEvents;
9396
};
9497

@@ -157,34 +160,8 @@ const PinnedMessagesCard: React.FC<IProps> = ({ room, onClose, permalinkCreator
157160
null,
158161
);
159162

160-
let content;
161-
if (!pinnedEvents) {
162-
content = <Spinner />;
163-
} else if (pinnedEvents.length > 0) {
164-
const onUnpinClicked = async (event: MatrixEvent): Promise<void> => {
165-
const pinnedEvents = room.currentState.getStateEvents(EventType.RoomPinnedEvents, "");
166-
if (pinnedEvents?.getContent()?.pinned) {
167-
const pinned = pinnedEvents.getContent().pinned;
168-
const index = pinned.indexOf(event.getId());
169-
if (index !== -1) {
170-
pinned.splice(index, 1);
171-
await cli.sendStateEvent(room.roomId, EventType.RoomPinnedEvents, { pinned }, "");
172-
}
173-
}
174-
};
175-
176-
// show them in reverse, with latest pinned at the top
177-
content = filterBoolean(pinnedEvents)
178-
.reverse()
179-
.map((ev) => (
180-
<PinnedEventTile
181-
key={ev.getId()}
182-
event={ev}
183-
onUnpinClicked={canUnpin ? () => onUnpinClicked(ev) : undefined}
184-
permalinkCreator={permalinkCreator}
185-
/>
186-
));
187-
} else {
163+
let content: JSX.Element[] | JSX.Element | undefined;
164+
if (!pinnedEventIds.length) {
188165
content = (
189166
<div className="mx_PinnedMessagesCard_empty_wrapper">
190167
<div className="mx_PinnedMessagesCard_empty">
@@ -215,6 +192,32 @@ const PinnedMessagesCard: React.FC<IProps> = ({ room, onClose, permalinkCreator
215192
</div>
216193
</div>
217194
);
195+
} else if (pinnedEvents?.length) {
196+
const onUnpinClicked = async (event: MatrixEvent): Promise<void> => {
197+
const pinnedEvents = room.currentState.getStateEvents(EventType.RoomPinnedEvents, "");
198+
if (pinnedEvents?.getContent()?.pinned) {
199+
const pinned = pinnedEvents.getContent().pinned;
200+
const index = pinned.indexOf(event.getId());
201+
if (index !== -1) {
202+
pinned.splice(index, 1);
203+
await cli.sendStateEvent(room.roomId, EventType.RoomPinnedEvents, { pinned }, "");
204+
}
205+
}
206+
};
207+
208+
// show them in reverse, with latest pinned at the top
209+
content = filterBoolean(pinnedEvents)
210+
.reverse()
211+
.map((ev) => (
212+
<PinnedEventTile
213+
key={ev.getId()}
214+
event={ev}
215+
onUnpinClicked={canUnpin ? () => onUnpinClicked(ev) : undefined}
216+
permalinkCreator={permalinkCreator}
217+
/>
218+
));
219+
} else {
220+
content = <Spinner />;
218221
}
219222

220223
return (

test/components/views/right_panel/PinnedMessagesCard-test.tsx

Lines changed: 26 additions & 2 deletions
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 { render, act, RenderResult } from "@testing-library/react";
18+
import { render, act, RenderResult, fireEvent, waitForElementToBeRemoved, screen } from "@testing-library/react";
1919
import { mocked } from "jest-mock";
2020
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
2121
import { EventType, RelationType, MsgType } from "matrix-js-sdk/src/@types/event";
@@ -240,7 +240,7 @@ describe("<PinnedMessagesCard />", () => {
240240
...PollResponseEvent.from([answers[option as number].id], poll.getId()!).serialize(),
241241
event: true,
242242
room: "!room:example.org",
243-
user: user as string,
243+
user,
244244
}),
245245
);
246246

@@ -284,4 +284,28 @@ describe("<PinnedMessagesCard />", () => {
284284
expect(pinTile[0].querySelectorAll(".mx_PollOption_optionVoteCount")[0]).toHaveTextContent("2 votes");
285285
expect([...pinTile[0].querySelectorAll(".mx_PollOption_optionVoteCount")].at(-1)).toHaveTextContent("1 vote");
286286
});
287+
288+
it("should allow admins to unpin messages", async () => {
289+
const nonLocalPins = [pin1];
290+
const room = mkRoom([], nonLocalPins);
291+
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
292+
const sendStateEvent = jest.spyOn(cli, "sendStateEvent");
293+
294+
const pins = await mountPins(room);
295+
const pinTile = pins.container.querySelectorAll(".mx_PinnedEventTile");
296+
expect(pinTile).toHaveLength(1);
297+
298+
fireEvent.click(pinTile[0].querySelector(".mx_PinnedEventTile_unpinButton")!);
299+
expect(sendStateEvent).toHaveBeenCalledWith(room.roomId, "m.room.pinned_events", { pinned: [] }, "");
300+
301+
nonLocalPins.pop();
302+
await Promise.all([waitForElementToBeRemoved(pinTile[0]), emitPinUpdates(room)]);
303+
});
304+
305+
it("should show spinner whilst loading", async () => {
306+
const room = mkRoom([], [pin1]);
307+
mountPins(room);
308+
const spinner = await screen.findByTestId("spinner");
309+
await waitForElementToBeRemoved(spinner);
310+
});
287311
});

0 commit comments

Comments
 (0)