Apply prettier formatting
This commit is contained in:
parent
1cac306093
commit
526645c791
1576 changed files with 65385 additions and 62478 deletions
|
@ -17,13 +17,13 @@ limitations under the License.
|
|||
import React, { useCallback, useContext, useEffect, useState } from "react";
|
||||
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { EventType, RelationType } from 'matrix-js-sdk/src/@types/event';
|
||||
import { EventType, RelationType } from "matrix-js-sdk/src/@types/event";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
||||
|
||||
import { Icon as ContextMenuIcon } from '../../../../res/img/element-icons/context-menu.svg';
|
||||
import { Icon as ContextMenuIcon } from "../../../../res/img/element-icons/context-menu.svg";
|
||||
import { Icon as EmojiIcon } from "../../../../res/img/element-icons/room/message-bar/emoji.svg";
|
||||
import { Icon as ReplyIcon } from '../../../../res/img/element-icons/room/message-bar/reply.svg';
|
||||
import { Icon as ReplyIcon } from "../../../../res/img/element-icons/room/message-bar/reply.svg";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import BaseCard from "./BaseCard";
|
||||
import Spinner from "../elements/Spinner";
|
||||
|
@ -35,7 +35,7 @@ import PinnedEventTile from "../rooms/PinnedEventTile";
|
|||
import { useRoomState } from "../../../hooks/useRoomState";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
|
||||
import { ReadPinsEventId } from "./types";
|
||||
import Heading from '../typography/Heading';
|
||||
import Heading from "../typography/Heading";
|
||||
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||
|
||||
interface IProps {
|
||||
|
@ -47,11 +47,16 @@ interface IProps {
|
|||
export const usePinnedEvents = (room: Room): string[] => {
|
||||
const [pinnedEvents, setPinnedEvents] = useState<string[]>([]);
|
||||
|
||||
const update = useCallback((ev?: MatrixEvent) => {
|
||||
if (!room) return;
|
||||
if (ev && ev.getType() !== EventType.RoomPinnedEvents) return;
|
||||
setPinnedEvents(room.currentState.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent()?.pinned || []);
|
||||
}, [room]);
|
||||
const update = useCallback(
|
||||
(ev?: MatrixEvent) => {
|
||||
if (!room) return;
|
||||
if (ev && ev.getType() !== EventType.RoomPinnedEvents) return;
|
||||
setPinnedEvents(
|
||||
room.currentState.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent()?.pinned || [],
|
||||
);
|
||||
},
|
||||
[room],
|
||||
);
|
||||
|
||||
useTypedEventEmitter(room?.currentState, RoomStateEvent.Events, update);
|
||||
useEffect(() => {
|
||||
|
@ -66,12 +71,15 @@ export const usePinnedEvents = (room: Room): string[] => {
|
|||
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 || []));
|
||||
}, [room]);
|
||||
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 || []));
|
||||
},
|
||||
[room],
|
||||
);
|
||||
|
||||
useTypedEventEmitter(room, RoomEvent.AccountData, update);
|
||||
useEffect(() => {
|
||||
|
@ -86,12 +94,12 @@ export const useReadPinnedEvents = (room: Room): Set<string> => {
|
|||
const PinnedMessagesCard = ({ room, onClose, permalinkCreator }: IProps) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const roomContext = useContext(RoomContext);
|
||||
const canUnpin = useRoomState(room, state => state.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli));
|
||||
const canUnpin = useRoomState(room, (state) => state.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli));
|
||||
const pinnedEventIds = usePinnedEvents(room);
|
||||
const readPinnedEvents = useReadPinnedEvents(room);
|
||||
|
||||
useEffect(() => {
|
||||
const newlyRead = pinnedEventIds.filter(id => !readPinnedEvents.has(id));
|
||||
const newlyRead = pinnedEventIds.filter((id) => !readPinnedEvents.has(id));
|
||||
if (newlyRead.length > 0) {
|
||||
// clear out any read pinned events which no longer are pinned
|
||||
cli.setRoomAccountData(room.roomId, ReadPinsEventId, {
|
||||
|
@ -100,40 +108,52 @@ const PinnedMessagesCard = ({ room, onClose, permalinkCreator }: IProps) => {
|
|||
}
|
||||
}, [cli, room.roomId, pinnedEventIds, readPinnedEvents]);
|
||||
|
||||
const pinnedEvents = useAsyncMemo(() => {
|
||||
const promises = pinnedEventIds.map(async eventId => {
|
||||
const timelineSet = room.getUnfilteredTimelineSet();
|
||||
const localEvent = timelineSet?.getTimelineForEvent(eventId)?.getEvents().find(e => e.getId() === eventId);
|
||||
if (localEvent) return PinningUtils.isPinnable(localEvent) ? localEvent : null;
|
||||
const pinnedEvents = useAsyncMemo(
|
||||
() => {
|
||||
const promises = pinnedEventIds.map(async (eventId) => {
|
||||
const timelineSet = room.getUnfilteredTimelineSet();
|
||||
const localEvent = timelineSet
|
||||
?.getTimelineForEvent(eventId)
|
||||
?.getEvents()
|
||||
.find((e) => e.getId() === eventId);
|
||||
if (localEvent) return PinningUtils.isPinnable(localEvent) ? localEvent : null;
|
||||
|
||||
try {
|
||||
// Fetch the event and latest edit in parallel
|
||||
const [evJson, { events: [edit] }] = await Promise.all([
|
||||
cli.fetchRoomEvent(room.roomId, eventId),
|
||||
cli.relations(room.roomId, eventId, RelationType.Replace, null, { limit: 1 }),
|
||||
]);
|
||||
const event = new MatrixEvent(evJson);
|
||||
if (event.isEncrypted()) {
|
||||
await cli.decryptEventIfNeeded(event); // TODO await?
|
||||
try {
|
||||
// Fetch the event and latest edit in parallel
|
||||
const [
|
||||
evJson,
|
||||
{
|
||||
events: [edit],
|
||||
},
|
||||
] = await Promise.all([
|
||||
cli.fetchRoomEvent(room.roomId, eventId),
|
||||
cli.relations(room.roomId, eventId, RelationType.Replace, null, { limit: 1 }),
|
||||
]);
|
||||
const event = new MatrixEvent(evJson);
|
||||
if (event.isEncrypted()) {
|
||||
await cli.decryptEventIfNeeded(event); // TODO await?
|
||||
}
|
||||
|
||||
if (event && PinningUtils.isPinnable(event)) {
|
||||
// Inject sender information
|
||||
event.sender = room.getMember(event.getSender());
|
||||
// Also inject any edits we've found
|
||||
if (edit) event.makeReplaced(edit);
|
||||
|
||||
return event;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error("Error looking up pinned event " + eventId + " in room " + room.roomId);
|
||||
logger.error(err);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (event && PinningUtils.isPinnable(event)) {
|
||||
// Inject sender information
|
||||
event.sender = room.getMember(event.getSender());
|
||||
// Also inject any edits we've found
|
||||
if (edit) event.makeReplaced(edit);
|
||||
|
||||
return event;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error("Error looking up pinned event " + eventId + " in room " + room.roomId);
|
||||
logger.error(err);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
}, [cli, room, pinnedEventIds], null);
|
||||
return Promise.all(promises);
|
||||
},
|
||||
[cli, room, pinnedEventIds],
|
||||
null,
|
||||
);
|
||||
|
||||
let content;
|
||||
if (!pinnedEvents) {
|
||||
|
@ -152,53 +172,72 @@ const PinnedMessagesCard = ({ room, onClose, permalinkCreator }: IProps) => {
|
|||
};
|
||||
|
||||
// show them in reverse, with latest pinned at the top
|
||||
content = pinnedEvents.filter(Boolean).reverse().map(ev => (
|
||||
<PinnedEventTile
|
||||
key={ev.getId()}
|
||||
event={ev}
|
||||
onUnpinClicked={canUnpin ? () => onUnpinClicked(ev) : undefined}
|
||||
permalinkCreator={permalinkCreator}
|
||||
/>
|
||||
));
|
||||
content = pinnedEvents
|
||||
.filter(Boolean)
|
||||
.reverse()
|
||||
.map((ev) => (
|
||||
<PinnedEventTile
|
||||
key={ev.getId()}
|
||||
event={ev}
|
||||
onUnpinClicked={canUnpin ? () => onUnpinClicked(ev) : undefined}
|
||||
permalinkCreator={permalinkCreator}
|
||||
/>
|
||||
));
|
||||
} else {
|
||||
content = <div className="mx_PinnedMessagesCard_empty_wrapper">
|
||||
<div className="mx_PinnedMessagesCard_empty">
|
||||
{ /* XXX: We reuse the classes for simplicity, but deliberately not the components for non-interactivity. */ }
|
||||
<div className="mx_MessageActionBar mx_PinnedMessagesCard_MessageActionBar">
|
||||
<div className="mx_MessageActionBar_iconButton">
|
||||
<EmojiIcon />
|
||||
content = (
|
||||
<div className="mx_PinnedMessagesCard_empty_wrapper">
|
||||
<div className="mx_PinnedMessagesCard_empty">
|
||||
{/* XXX: We reuse the classes for simplicity, but deliberately not the components for non-interactivity. */}
|
||||
<div className="mx_MessageActionBar mx_PinnedMessagesCard_MessageActionBar">
|
||||
<div className="mx_MessageActionBar_iconButton">
|
||||
<EmojiIcon />
|
||||
</div>
|
||||
<div className="mx_MessageActionBar_iconButton">
|
||||
<ReplyIcon />
|
||||
</div>
|
||||
<div className="mx_MessageActionBar_iconButton mx_MessageActionBar_optionsButton">
|
||||
<ContextMenuIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_MessageActionBar_iconButton">
|
||||
<ReplyIcon />
|
||||
</div>
|
||||
<div className="mx_MessageActionBar_iconButton mx_MessageActionBar_optionsButton">
|
||||
<ContextMenuIcon />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Heading size="h4" className="mx_PinnedMessagesCard_empty_header">{ _t("Nothing pinned, yet") }</Heading>
|
||||
{ _t("If you have permissions, open the menu on any message and select " +
|
||||
"<b>Pin</b> to stick them here.", {}, {
|
||||
b: sub => <b>{ sub }</b>,
|
||||
}) }
|
||||
<Heading size="h4" className="mx_PinnedMessagesCard_empty_header">
|
||||
{_t("Nothing pinned, yet")}
|
||||
</Heading>
|
||||
{_t(
|
||||
"If you have permissions, open the menu on any message and select " +
|
||||
"<b>Pin</b> to stick them here.",
|
||||
{},
|
||||
{
|
||||
b: (sub) => <b>{sub}</b>,
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
}
|
||||
|
||||
return <BaseCard
|
||||
header={<div className="mx_BaseCard_header_title">
|
||||
<Heading size="h4" className="mx_BaseCard_header_title_heading">{ _t("Pinned messages") }</Heading>
|
||||
</div>}
|
||||
className="mx_PinnedMessagesCard"
|
||||
onClose={onClose}
|
||||
>
|
||||
<RoomContext.Provider value={{
|
||||
...roomContext,
|
||||
timelineRenderingType: TimelineRenderingType.Pinned,
|
||||
}}>
|
||||
{ content }
|
||||
</RoomContext.Provider>
|
||||
</BaseCard>;
|
||||
return (
|
||||
<BaseCard
|
||||
header={
|
||||
<div className="mx_BaseCard_header_title">
|
||||
<Heading size="h4" className="mx_BaseCard_header_title_heading">
|
||||
{_t("Pinned messages")}
|
||||
</Heading>
|
||||
</div>
|
||||
}
|
||||
className="mx_PinnedMessagesCard"
|
||||
onClose={onClose}
|
||||
>
|
||||
<RoomContext.Provider
|
||||
value={{
|
||||
...roomContext,
|
||||
timelineRenderingType: TimelineRenderingType.Pinned,
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</RoomContext.Provider>
|
||||
</BaseCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default PinnedMessagesCard;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue