Fix pinned messages card saying nothing pinned while loading (#10385)
This commit is contained in:
parent
0c38bd7beb
commit
87fb493783
2 changed files with 70 additions and 43 deletions
|
@ -45,50 +45,53 @@ interface IProps {
|
||||||
onClose(): void;
|
onClose(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPinnedEventIds(room?: Room): string[] {
|
||||||
|
return room?.currentState.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent()?.pinned ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
export const usePinnedEvents = (room?: Room): string[] => {
|
export const usePinnedEvents = (room?: Room): string[] => {
|
||||||
const [pinnedEvents, setPinnedEvents] = useState<string[]>([]);
|
const [pinnedEvents, setPinnedEvents] = useState<string[]>(getPinnedEventIds(room));
|
||||||
|
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
(ev?: MatrixEvent) => {
|
(ev?: MatrixEvent) => {
|
||||||
if (!room) return;
|
|
||||||
if (ev && ev.getType() !== EventType.RoomPinnedEvents) return;
|
if (ev && ev.getType() !== EventType.RoomPinnedEvents) return;
|
||||||
setPinnedEvents(
|
setPinnedEvents(getPinnedEventIds(room));
|
||||||
room.currentState.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent()?.pinned || [],
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
[room],
|
[room],
|
||||||
);
|
);
|
||||||
|
|
||||||
useTypedEventEmitter(room?.currentState, RoomStateEvent.Events, update);
|
useTypedEventEmitter(room?.currentState, RoomStateEvent.Events, update);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
update();
|
setPinnedEvents(getPinnedEventIds(room));
|
||||||
return () => {
|
return () => {
|
||||||
setPinnedEvents([]);
|
setPinnedEvents([]);
|
||||||
};
|
};
|
||||||
}, [update]);
|
}, [room]);
|
||||||
return pinnedEvents;
|
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 [readPinnedEvents, setReadPinnedEvents] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
(ev?: MatrixEvent) => {
|
(ev?: MatrixEvent) => {
|
||||||
if (!room) return;
|
|
||||||
if (ev && ev.getType() !== ReadPinsEventId) return;
|
if (ev && ev.getType() !== ReadPinsEventId) return;
|
||||||
const readPins = room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids;
|
setReadPinnedEvents(getReadPinnedEventIds(room));
|
||||||
setReadPinnedEvents(new Set(readPins || []));
|
|
||||||
},
|
},
|
||||||
[room],
|
[room],
|
||||||
);
|
);
|
||||||
|
|
||||||
useTypedEventEmitter(room, RoomEvent.AccountData, update);
|
useTypedEventEmitter(room, RoomEvent.AccountData, update);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
update();
|
setReadPinnedEvents(getReadPinnedEventIds(room));
|
||||||
return () => {
|
return () => {
|
||||||
setReadPinnedEvents(new Set());
|
setReadPinnedEvents(new Set());
|
||||||
};
|
};
|
||||||
}, [update]);
|
}, [room]);
|
||||||
return readPinnedEvents;
|
return readPinnedEvents;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -157,34 +160,8 @@ const PinnedMessagesCard: React.FC<IProps> = ({ room, onClose, permalinkCreator
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
let content;
|
let content: JSX.Element[] | JSX.Element | undefined;
|
||||||
if (!pinnedEvents) {
|
if (!pinnedEventIds.length) {
|
||||||
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 {
|
|
||||||
content = (
|
content = (
|
||||||
<div className="mx_PinnedMessagesCard_empty_wrapper">
|
<div className="mx_PinnedMessagesCard_empty_wrapper">
|
||||||
<div className="mx_PinnedMessagesCard_empty">
|
<div className="mx_PinnedMessagesCard_empty">
|
||||||
|
@ -215,6 +192,32 @@ const PinnedMessagesCard: React.FC<IProps> = ({ room, onClose, permalinkCreator
|
||||||
</div>
|
</div>
|
||||||
</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 (
|
return (
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
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 { mocked } from "jest-mock";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
import { EventType, RelationType, MsgType } from "matrix-js-sdk/src/@types/event";
|
import { EventType, RelationType, MsgType } from "matrix-js-sdk/src/@types/event";
|
||||||
|
@ -240,7 +240,7 @@ describe("<PinnedMessagesCard />", () => {
|
||||||
...PollResponseEvent.from([answers[option as number].id], poll.getId()!).serialize(),
|
...PollResponseEvent.from([answers[option as number].id], poll.getId()!).serialize(),
|
||||||
event: true,
|
event: true,
|
||||||
room: "!room:example.org",
|
room: "!room:example.org",
|
||||||
user: user as string,
|
user,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -284,4 +284,28 @@ describe("<PinnedMessagesCard />", () => {
|
||||||
expect(pinTile[0].querySelectorAll(".mx_PollOption_optionVoteCount")[0]).toHaveTextContent("2 votes");
|
expect(pinTile[0].querySelectorAll(".mx_PollOption_optionVoteCount")[0]).toHaveTextContent("2 votes");
|
||||||
expect([...pinTile[0].querySelectorAll(".mx_PollOption_optionVoteCount")].at(-1)).toHaveTextContent("1 vote");
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue