Fix pinned message banner disappearing when a pinned message event is unkown (#33534)
* fix: remove unknown event when fetching pinned message events * test: update tsts
This commit is contained in:
@@ -21,7 +21,6 @@ import { useRoomState } from "../../../hooks/useRoomState";
|
|||||||
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
|
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
|
||||||
import { ReadPinsEventId } from "./types";
|
import { ReadPinsEventId } from "./types";
|
||||||
import { type RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
import { type RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||||
import { filterBoolean } from "../../../utils/arrays";
|
|
||||||
import Modal from "../../../Modal";
|
import Modal from "../../../Modal";
|
||||||
import { UnpinAllDialog } from "../dialogs/UnpinAllDialog";
|
import { UnpinAllDialog } from "../dialogs/UnpinAllDialog";
|
||||||
import EmptyState from "./EmptyState";
|
import EmptyState from "./EmptyState";
|
||||||
@@ -77,9 +76,7 @@ export function PinnedMessagesCard({ room, onClose, permalinkCreator }: PinnedMe
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (pinnedEvents?.length) {
|
} else if (pinnedEvents?.length) {
|
||||||
content = (
|
content = <PinnedMessages events={pinnedEvents} room={room} permalinkCreator={permalinkCreator} />;
|
||||||
<PinnedMessages events={filterBoolean(pinnedEvents)} room={room} permalinkCreator={permalinkCreator} />
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
content = <Spinner />;
|
content = <Spinner />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { useMatrixClientContext } from "../contexts/MatrixClientContext";
|
|||||||
import { useAsyncMemo } from "./useAsyncMemo";
|
import { useAsyncMemo } from "./useAsyncMemo";
|
||||||
import PinningUtils from "../utils/PinningUtils";
|
import PinningUtils from "../utils/PinningUtils";
|
||||||
import { batch } from "../utils/promise.ts";
|
import { batch } from "../utils/promise.ts";
|
||||||
|
import { filterBoolean } from "../utils/arrays.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the pinned event IDs from a room.
|
* Get the pinned event IDs from a room.
|
||||||
@@ -176,18 +177,19 @@ async function fetchPinnedEvent(room: Room, pinnedEventId: string, cli: MatrixCl
|
|||||||
* @param room
|
* @param room
|
||||||
* @param pinnedEventIds
|
* @param pinnedEventIds
|
||||||
*/
|
*/
|
||||||
export function useFetchedPinnedEvents(room: Room, pinnedEventIds: string[]): Array<MatrixEvent | null> | null {
|
export function useFetchedPinnedEvents(room: Room, pinnedEventIds: string[]): Array<MatrixEvent> {
|
||||||
const cli = useMatrixClientContext();
|
const cli = useMatrixClientContext();
|
||||||
|
|
||||||
return useAsyncMemo(
|
const events = useAsyncMemo(
|
||||||
() => {
|
() => {
|
||||||
const fetchPromises = pinnedEventIds.map((eventId) => () => fetchPinnedEvent(room, eventId, cli));
|
const fetchPromises = pinnedEventIds.map((eventId) => () => fetchPinnedEvent(room, eventId, cli));
|
||||||
// Fetch the pinned events in batches of 10
|
// Fetch the pinned events in batches of 10
|
||||||
return batch(fetchPromises, 10);
|
return batch(fetchPromises, 10);
|
||||||
},
|
},
|
||||||
[cli, room, pinnedEventIds],
|
[cli, room, pinnedEventIds],
|
||||||
null,
|
[],
|
||||||
);
|
);
|
||||||
|
return filterBoolean(events);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -196,15 +198,7 @@ export function useFetchedPinnedEvents(room: Room, pinnedEventIds: string[]): Ar
|
|||||||
* @param room
|
* @param room
|
||||||
* @param pinnedEventIds
|
* @param pinnedEventIds
|
||||||
*/
|
*/
|
||||||
export function useSortedFetchedPinnedEvents(room: Room, pinnedEventIds: string[]): Array<MatrixEvent | null> {
|
export function useSortedFetchedPinnedEvents(room: Room, pinnedEventIds: string[]): Array<MatrixEvent> {
|
||||||
const pinnedEvents = useFetchedPinnedEvents(room, pinnedEventIds);
|
const pinnedEvents = useFetchedPinnedEvents(room, pinnedEventIds);
|
||||||
return useMemo(() => {
|
return useMemo(() => pinnedEvents.sort((a, b) => a.getTs() - b.getTs()), [pinnedEvents]);
|
||||||
if (!pinnedEvents) return [];
|
|
||||||
|
|
||||||
return pinnedEvents.sort((a, b) => {
|
|
||||||
if (!a) return -1;
|
|
||||||
if (!b) return 1;
|
|
||||||
return a.getTs() - b.getTs();
|
|
||||||
});
|
|
||||||
}, [pinnedEvents]);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -294,6 +294,23 @@ describe("<PinnedMessagesCard />", () => {
|
|||||||
await initPinnedMessagesCard([], [pin]);
|
await initPinnedMessagesCard([], [pin]);
|
||||||
expect(screen.queryAllByRole("listitem")).toHaveLength(0);
|
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", () => {
|
describe("unpin all", () => {
|
||||||
|
|||||||
+9
-9
@@ -329,7 +329,7 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
aria-labelledby="_r_10f_"
|
aria-labelledby="_r_10u_"
|
||||||
class="_icon-button_1215g_8"
|
class="_icon-button_1215g_8"
|
||||||
data-kind="secondary"
|
data-kind="secondary"
|
||||||
data-testid="base-card-close-button"
|
data-testid="base-card-close-button"
|
||||||
@@ -387,13 +387,13 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
|
|||||||
class="mx_PinnedEventTile_top"
|
class="mx_PinnedEventTile_top"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
aria-labelledby="_r_10m_"
|
aria-labelledby="_r_115_"
|
||||||
class="mx_PinnedEventTile_sender mx_Username_color3"
|
class="mx_PinnedEventTile_sender mx_Username_color3"
|
||||||
>
|
>
|
||||||
@alice:example.org
|
@alice:example.org
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
aria-describedby="_r_10l_"
|
aria-describedby="_r_114_"
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="menu"
|
aria-haspopup="menu"
|
||||||
@@ -401,7 +401,7 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
|
|||||||
class="_icon-button_1215g_8"
|
class="_icon-button_1215g_8"
|
||||||
data-kind="primary"
|
data-kind="primary"
|
||||||
data-state="closed"
|
data-state="closed"
|
||||||
id="radix-_r_10r_"
|
id="radix-_r_11a_"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 24px;"
|
style="--cpd-icon-button-size: 24px;"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@@ -427,7 +427,7 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_MTextBody mx_EventTile_content _root_1hgc7_8 _text_1hgc7_13"
|
class="mx_MTextBody mx_EventTile_content _root_1hgc7_8 _text_1hgc7_13"
|
||||||
id="_r_10l_"
|
id="_r_114_"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_EventTile_body translate"
|
class="mx_EventTile_body translate"
|
||||||
@@ -468,13 +468,13 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
|
|||||||
class="mx_PinnedEventTile_top"
|
class="mx_PinnedEventTile_top"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
aria-labelledby="_r_10u_"
|
aria-labelledby="_r_11d_"
|
||||||
class="mx_PinnedEventTile_sender mx_Username_color3"
|
class="mx_PinnedEventTile_sender mx_Username_color3"
|
||||||
>
|
>
|
||||||
@alice:example.org
|
@alice:example.org
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
aria-describedby="_r_10t_"
|
aria-describedby="_r_11c_"
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="menu"
|
aria-haspopup="menu"
|
||||||
@@ -482,7 +482,7 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
|
|||||||
class="_icon-button_1215g_8"
|
class="_icon-button_1215g_8"
|
||||||
data-kind="primary"
|
data-kind="primary"
|
||||||
data-state="closed"
|
data-state="closed"
|
||||||
id="radix-_r_113_"
|
id="radix-_r_11i_"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 24px;"
|
style="--cpd-icon-button-size: 24px;"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@@ -508,7 +508,7 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_MTextBody mx_EventTile_content _root_1hgc7_8 _text_1hgc7_13"
|
class="mx_MTextBody mx_EventTile_content _root_1hgc7_8 _text_1hgc7_13"
|
||||||
id="_r_10t_"
|
id="_r_11c_"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_EventTile_body translate"
|
class="mx_EventTile_body translate"
|
||||||
|
|||||||
Reference in New Issue
Block a user