Skip to content

Commit

Permalink
Fix pinned messages card saying nothing pinned while loading (#10385)
Browse files Browse the repository at this point in the history
  • Loading branch information
t3chguy committed Mar 15, 2023
1 parent 0c38bd7 commit 87fb493
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 43 deletions.
85 changes: 44 additions & 41 deletions src/components/views/right_panel/PinnedMessagesCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string[]>([]);
const [pinnedEvents, setPinnedEvents] = useState<string[]>(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<string> => {
function getReadPinnedEventIds(room?: Room): Set<string> {
return new Set(room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids ?? []);
}

export const useReadPinnedEvents = (room?: Room): Set<string> => {
const [readPinnedEvents, setReadPinnedEvents] = useState<Set<string>>(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;
};

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

let content;
if (!pinnedEvents) {
content = <Spinner />;
} else if (pinnedEvents.length > 0) {
const onUnpinClicked = async (event: MatrixEvent): Promise<void> => {
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) => (
<PinnedEventTile
key={ev.getId()}
event={ev}
onUnpinClicked={canUnpin ? () => onUnpinClicked(ev) : undefined}
permalinkCreator={permalinkCreator}
/>
));
} else {
let content: JSX.Element[] | JSX.Element | undefined;
if (!pinnedEventIds.length) {
content = (
<div className="mx_PinnedMessagesCard_empty_wrapper">
<div className="mx_PinnedMessagesCard_empty">
Expand Down Expand Up @@ -215,6 +192,32 @@ const PinnedMessagesCard: React.FC<IProps> = ({ room, onClose, permalinkCreator
</div>
</div>
);
} else if (pinnedEvents?.length) {
const onUnpinClicked = async (event: MatrixEvent): Promise<void> => {
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) => (
<PinnedEventTile
key={ev.getId()}
event={ev}
onUnpinClicked={canUnpin ? () => onUnpinClicked(ev) : undefined}
permalinkCreator={permalinkCreator}
/>
));
} else {
content = <Spinner />;
}

return (
Expand Down
28 changes: 26 additions & 2 deletions test/components/views/right_panel/PinnedMessagesCard-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -240,7 +240,7 @@ describe("<PinnedMessagesCard />", () => {
...PollResponseEvent.from([answers[option as number].id], poll.getId()!).serialize(),
event: true,
room: "!room:example.org",
user: user as string,
user,
}),
);

Expand Down Expand Up @@ -284,4 +284,28 @@ describe("<PinnedMessagesCard />", () => {
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);
});
});

0 comments on commit 87fb493

Please sign in to comment.