diff --git a/apps/web/src/components/views/right_panel/PinnedMessagesCard.tsx b/apps/web/src/components/views/right_panel/PinnedMessagesCard.tsx
index 611b485b78..b2b4aa72cd 100644
--- a/apps/web/src/components/views/right_panel/PinnedMessagesCard.tsx
+++ b/apps/web/src/components/views/right_panel/PinnedMessagesCard.tsx
@@ -21,7 +21,6 @@ import { useRoomState } from "../../../hooks/useRoomState";
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
import { ReadPinsEventId } from "./types";
import { type RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
-import { filterBoolean } from "../../../utils/arrays";
import Modal from "../../../Modal";
import { UnpinAllDialog } from "../dialogs/UnpinAllDialog";
import EmptyState from "./EmptyState";
@@ -77,9 +76,7 @@ export function PinnedMessagesCard({ room, onClose, permalinkCreator }: PinnedMe
/>
);
} else if (pinnedEvents?.length) {
- content = (
-
- );
+ content = ;
} else {
content = ;
}
diff --git a/apps/web/src/hooks/usePinnedEvents.ts b/apps/web/src/hooks/usePinnedEvents.ts
index 98f544097b..ea0524c710 100644
--- a/apps/web/src/hooks/usePinnedEvents.ts
+++ b/apps/web/src/hooks/usePinnedEvents.ts
@@ -25,6 +25,7 @@ import { useMatrixClientContext } from "../contexts/MatrixClientContext";
import { useAsyncMemo } from "./useAsyncMemo";
import PinningUtils from "../utils/PinningUtils";
import { batch } from "../utils/promise.ts";
+import { filterBoolean } from "../utils/arrays.ts";
/**
* Get the pinned event IDs from a room.
@@ -176,18 +177,19 @@ async function fetchPinnedEvent(room: Room, pinnedEventId: string, cli: MatrixCl
* @param room
* @param pinnedEventIds
*/
-export function useFetchedPinnedEvents(room: Room, pinnedEventIds: string[]): Array | null {
+export function useFetchedPinnedEvents(room: Room, pinnedEventIds: string[]): Array {
const cli = useMatrixClientContext();
- return useAsyncMemo(
+ const events = useAsyncMemo(
() => {
const fetchPromises = pinnedEventIds.map((eventId) => () => fetchPinnedEvent(room, eventId, cli));
// Fetch the pinned events in batches of 10
return batch(fetchPromises, 10);
},
[cli, room, pinnedEventIds],
- null,
+ [],
);
+ return filterBoolean(events);
}
/**
@@ -196,15 +198,7 @@ export function useFetchedPinnedEvents(room: Room, pinnedEventIds: string[]): Ar
* @param room
* @param pinnedEventIds
*/
-export function useSortedFetchedPinnedEvents(room: Room, pinnedEventIds: string[]): Array {
+export function useSortedFetchedPinnedEvents(room: Room, pinnedEventIds: string[]): Array {
const pinnedEvents = useFetchedPinnedEvents(room, pinnedEventIds);
- return useMemo(() => {
- if (!pinnedEvents) return [];
-
- return pinnedEvents.sort((a, b) => {
- if (!a) return -1;
- if (!b) return 1;
- return a.getTs() - b.getTs();
- });
- }, [pinnedEvents]);
+ return useMemo(() => pinnedEvents.sort((a, b) => a.getTs() - b.getTs()), [pinnedEvents]);
}
diff --git a/apps/web/test/unit-tests/components/views/right_panel/PinnedMessagesCard-test.tsx b/apps/web/test/unit-tests/components/views/right_panel/PinnedMessagesCard-test.tsx
index 7196667d9c..e66930aabe 100644
--- a/apps/web/test/unit-tests/components/views/right_panel/PinnedMessagesCard-test.tsx
+++ b/apps/web/test/unit-tests/components/views/right_panel/PinnedMessagesCard-test.tsx
@@ -294,6 +294,23 @@ describe("", () => {
await initPinnedMessagesCard([], [pin]);
expect(screen.queryAllByRole("listitem")).toHaveLength(0);
});
+
+ it("should filter out null events returned by useFetchedPinnedEvents and only display valid events", async () => {
+ // RoomCreate is not a pinnable event type — useFetchedPinnedEvents returns null for it
+ const unpinnableEvent = mkEvent({
+ event: true,
+ type: EventType.RoomCreate,
+ content: {},
+ room: "!room:example.org",
+ user: "@alice:example.org",
+ });
+ // pin1 is a valid pinnable message, unpinnableEvent causes useFetchedPinnedEvents to return null
+ // useSortedFetchedPinnedEvents must filter out the null, leaving only pin1
+ await initPinnedMessagesCard([pin1], [unpinnableEvent]);
+
+ await waitFor(() => expect(screen.queryAllByRole("listitem")).toHaveLength(1));
+ expect(screen.getByText("First pinned message")).toBeInTheDocument();
+ });
});
describe("unpin all", () => {
diff --git a/apps/web/test/unit-tests/components/views/right_panel/__snapshots__/PinnedMessagesCard-test.tsx.snap b/apps/web/test/unit-tests/components/views/right_panel/__snapshots__/PinnedMessagesCard-test.tsx.snap
index 035744fd70..2a491c1fa8 100644
--- a/apps/web/test/unit-tests/components/views/right_panel/__snapshots__/PinnedMessagesCard-test.tsx.snap
+++ b/apps/web/test/unit-tests/components/views/right_panel/__snapshots__/PinnedMessagesCard-test.tsx.snap
@@ -329,7 +329,7 @@ exports[` unpin all should not allow to unpinall 1`] = `
unpin all should not allow to unpinall 1`] = `
class="mx_PinnedEventTile_top"
>
@alice:example.org
unpin all should not allow to unpinall 1`] = `
class="_icon-button_1215g_8"
data-kind="primary"
data-state="closed"
- id="radix-_r_10r_"
+ id="radix-_r_11a_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
@@ -427,7 +427,7 @@ exports[` unpin all should not allow to unpinall 1`] = `
unpin all should not allow to unpinall 1`] = `
class="mx_PinnedEventTile_top"
>
@alice:example.org
unpin all should not allow to unpinall 1`] = `
class="_icon-button_1215g_8"
data-kind="primary"
data-state="closed"
- id="radix-_r_113_"
+ id="radix-_r_11i_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
@@ -508,7 +508,7 @@ exports[` unpin all should not allow to unpinall 1`] = `