2022-03-10 10:29:28 +00:00
|
|
|
/*
|
|
|
|
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
|
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
|
limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
2022-05-11 17:32:48 +01:00
|
|
|
import React, { useContext, useState } from "react";
|
2023-08-14 09:58:55 +01:00
|
|
|
import { Thread, ThreadEvent, IContent, MatrixEvent, MatrixEventEvent } from "matrix-js-sdk/src/matrix";
|
2022-03-10 10:29:28 +00:00
|
|
|
|
|
|
|
|
import { _t } from "../../../languageHandler";
|
2022-03-24 15:35:01 -06:00
|
|
|
import { CardContext } from "../right_panel/context";
|
2022-03-10 10:29:28 +00:00
|
|
|
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
|
|
|
|
|
import PosthogTrackers from "../../../PosthogTrackers";
|
2022-05-11 17:32:48 +01:00
|
|
|
import { useTypedEventEmitter, useTypedEventEmitterState } from "../../../hooks/useEventEmitter";
|
2022-03-10 10:29:28 +00:00
|
|
|
import RoomContext from "../../../contexts/RoomContext";
|
|
|
|
|
import { MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore";
|
|
|
|
|
import MemberAvatar from "../avatars/MemberAvatar";
|
|
|
|
|
import { useAsyncMemo } from "../../../hooks/useAsyncMemo";
|
|
|
|
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
2022-07-23 14:13:49 +02:00
|
|
|
import { Action } from "../../../dispatcher/actions";
|
|
|
|
|
import { ShowThreadPayload } from "../../../dispatcher/payloads/ShowThreadPayload";
|
|
|
|
|
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
2022-03-10 10:29:28 +00:00
|
|
|
|
|
|
|
|
interface IProps {
|
|
|
|
|
mxEvent: MatrixEvent;
|
|
|
|
|
thread: Thread;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
const ThreadSummary: React.FC<IProps> = ({ mxEvent, thread, ...props }) => {
|
2022-03-10 10:29:28 +00:00
|
|
|
const roomContext = useContext(RoomContext);
|
|
|
|
|
const cardContext = useContext(CardContext);
|
|
|
|
|
const count = useTypedEventEmitterState(thread, ThreadEvent.Update, () => thread.length);
|
|
|
|
|
if (!count) return null; // We don't want to show a thread summary if the thread doesn't have replies yet
|
|
|
|
|
|
|
|
|
|
let countSection: string | number = count;
|
|
|
|
|
if (!roomContext.narrow) {
|
|
|
|
|
countSection = _t("%(count)s reply", { count });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<AccessibleButton
|
2022-10-28 13:48:15 +02:00
|
|
|
{...props}
|
2022-04-27 18:10:27 +01:00
|
|
|
className="mx_ThreadSummary"
|
2022-03-10 10:29:28 +00:00
|
|
|
onClick={(ev: ButtonEvent) => {
|
2022-07-23 14:13:49 +02:00
|
|
|
defaultDispatcher.dispatch<ShowThreadPayload>({
|
|
|
|
|
action: Action.ShowThread,
|
2022-03-10 10:29:28 +00:00
|
|
|
rootEvent: mxEvent,
|
|
|
|
|
push: cardContext.isCard,
|
|
|
|
|
});
|
|
|
|
|
PosthogTrackers.trackInteraction("WebRoomTimelineThreadSummaryButton", ev);
|
|
|
|
|
}}
|
2023-09-29 08:49:26 +01:00
|
|
|
aria-label={_t("threads|open_thread")}
|
2022-03-10 10:29:28 +00:00
|
|
|
>
|
2022-07-04 19:07:50 +00:00
|
|
|
<span className="mx_ThreadSummary_replies_amount">{countSection}</span>
|
2022-03-11 19:24:18 +00:00
|
|
|
<ThreadMessagePreview thread={thread} showDisplayname={!roomContext.narrow} />
|
2022-04-27 18:10:27 +01:00
|
|
|
<div className="mx_ThreadSummary_chevron" />
|
2022-03-10 10:29:28 +00:00
|
|
|
</AccessibleButton>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2022-03-11 19:24:18 +00:00
|
|
|
interface IPreviewProps {
|
|
|
|
|
thread: Thread;
|
|
|
|
|
showDisplayname?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
export const ThreadMessagePreview: React.FC<IPreviewProps> = ({ thread, showDisplayname = false }) => {
|
2022-03-10 10:29:28 +00:00
|
|
|
const cli = useContext(MatrixClientContext);
|
2022-03-24 12:24:18 +00:00
|
|
|
|
2023-02-24 15:28:40 +00:00
|
|
|
const lastReply = useTypedEventEmitterState(thread, ThreadEvent.Update, () => thread.replyToEvent) ?? undefined;
|
2022-05-11 17:32:48 +01:00
|
|
|
// track the content as a means to regenerate the thread message preview upon edits & decryption
|
2023-02-24 15:28:40 +00:00
|
|
|
const [content, setContent] = useState<IContent | undefined>(lastReply?.getContent());
|
2022-05-11 17:32:48 +01:00
|
|
|
useTypedEventEmitter(lastReply, MatrixEventEvent.Replaced, () => {
|
2023-02-24 15:28:40 +00:00
|
|
|
setContent(lastReply!.getContent());
|
2022-05-11 17:32:48 +01:00
|
|
|
});
|
|
|
|
|
const awaitDecryption = lastReply?.shouldAttemptDecryption() || lastReply?.isBeingDecrypted();
|
2023-02-24 15:28:40 +00:00
|
|
|
useTypedEventEmitter(awaitDecryption ? lastReply : undefined, MatrixEventEvent.Decrypted, () => {
|
|
|
|
|
setContent(lastReply!.getContent());
|
2022-05-11 17:32:48 +01:00
|
|
|
});
|
2022-03-24 12:24:18 +00:00
|
|
|
|
2023-02-24 15:28:40 +00:00
|
|
|
const preview = useAsyncMemo(async (): Promise<string | undefined> => {
|
2022-03-10 13:34:24 +00:00
|
|
|
if (!lastReply) return;
|
2022-03-10 10:29:28 +00:00
|
|
|
await cli.decryptEventIfNeeded(lastReply);
|
|
|
|
|
return MessagePreviewStore.instance.generatePreviewForEvent(lastReply);
|
2022-05-11 17:32:48 +01:00
|
|
|
}, [lastReply, content]);
|
2022-10-28 13:48:15 +02:00
|
|
|
if (!preview || !lastReply) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2022-03-10 10:29:28 +00:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<MemberAvatar
|
2022-03-31 14:43:29 +01:00
|
|
|
member={lastReply.sender}
|
2022-03-10 10:29:28 +00:00
|
|
|
fallbackUserId={lastReply.getSender()}
|
2023-08-24 04:48:35 +01:00
|
|
|
size="24px"
|
2022-04-27 18:10:27 +01:00
|
|
|
className="mx_ThreadSummary_avatar"
|
2022-03-10 10:29:28 +00:00
|
|
|
/>
|
2022-04-27 18:10:27 +01:00
|
|
|
{showDisplayname && (
|
|
|
|
|
<div className="mx_ThreadSummary_sender">{lastReply.sender?.name ?? lastReply.getSender()}</div>
|
2022-03-11 19:24:18 +00:00
|
|
|
)}
|
2023-01-11 14:03:06 +01:00
|
|
|
|
|
|
|
|
{lastReply.isDecryptionFailure() ? (
|
|
|
|
|
<div
|
|
|
|
|
className="mx_ThreadSummary_content mx_DecryptionFailureBody"
|
2023-09-29 08:49:26 +01:00
|
|
|
title={_t("threads|unable_to_decrypt")}
|
2023-01-11 14:03:06 +01:00
|
|
|
>
|
2023-09-29 08:49:26 +01:00
|
|
|
<span className="mx_ThreadSummary_message-preview">{_t("threads|unable_to_decrypt")}</span>
|
2023-01-11 14:03:06 +01:00
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="mx_ThreadSummary_content" title={preview}>
|
|
|
|
|
<span className="mx_ThreadSummary_message-preview">{preview}</span>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2022-03-10 10:29:28 +00:00
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default ThreadSummary;
|