Add more debug tools for stories

This commit is contained in:
Fedor Indutny 2022-11-22 14:33:15 -08:00 committed by GitHub
parent 4d1cd05888
commit 1bff385805
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 116 additions and 30 deletions

View file

@ -15,6 +15,8 @@ import { SendStatus } from '../messages/MessageSendState';
import { Theme } from '../util/theme';
import { formatDateTimeLong } from '../util/timestamp';
import { DurationInSeconds } from '../util/durations';
import type { saveAttachment } from '../util/saveAttachment';
import type { AttachmentType } from '../types/Attachment';
import { ThemeType } from '../types/Util';
import { Time } from './Time';
import { groupBy } from '../util/mapUtil';
@ -23,10 +25,12 @@ import { format as formatRelativeTime } from '../util/expirationTimer';
export type PropsType = {
getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType;
isInternalUser?: boolean;
onClose: () => unknown;
saveAttachment: typeof saveAttachment;
sender: StoryViewType['sender'];
sendState?: Array<StorySendStateType>;
size?: number;
attachment?: AttachmentType;
expirationTimestamp: number | undefined;
timestamp: number;
};
@ -62,12 +66,14 @@ function getI18nKey(sendStatus: SendStatus | undefined): string {
}
export function StoryDetailsModal({
attachment,
getPreferredBadge,
i18n,
isInternalUser,
onClose,
saveAttachment,
sender,
sendState,
size,
timestamp,
expirationTimestamp,
}: PropsType): JSX.Element {
@ -193,6 +199,26 @@ export function StoryDetailsModal({
? DurationInSeconds.fromMillis(expirationTimestamp - Date.now())
: undefined;
const menuOptions = [
{
icon: 'StoryDetailsModal__copy-icon',
label: i18n('StoryDetailsModal__copy-timestamp'),
onClick: () => {
window.navigator.clipboard.writeText(String(timestamp));
},
},
];
if (isInternalUser && attachment) {
menuOptions.push({
icon: 'StoryDetailsModal__download-icon',
label: i18n('StoryDetailsModal__download-attachment'),
onClick: () => {
saveAttachment(attachment);
},
});
}
return (
<Modal
modalName="StoryDetailsModal"
@ -205,15 +231,7 @@ export function StoryDetailsModal({
title={
<ContextMenu
i18n={i18n}
menuOptions={[
{
icon: 'StoryDetailsModal__copy-icon',
label: i18n('StoryDetailsModal__copy-timestamp'),
onClick: () => {
window.navigator.clipboard.writeText(String(timestamp));
},
},
]}
menuOptions={menuOptions}
moduleClassName="StoryDetailsModal__debugger"
popperOptions={{
placement: 'bottom',
@ -235,14 +253,14 @@ export function StoryDetailsModal({
]}
/>
</div>
{size && (
{attachment && (
<div>
<Intl
i18n={i18n}
id="StoryDetailsModal__file-size"
components={[
<span className="StoryDetailsModal__debugger__button__text">
{formatFileSize(size)}
{formatFileSize(attachment.size)}
</span>,
]}
/>

View file

@ -45,6 +45,7 @@ import { getAvatarColor } from '../types/Colors';
import { getStoryBackground } from '../util/getStoryBackground';
import { getStoryDuration } from '../util/getStoryDuration';
import { graphemeAwareSlice } from '../util/graphemeAwareSlice';
import type { saveAttachment } from '../util/saveAttachment';
import { isVideoAttachment } from '../types/Attachment';
import { useEscapeHandling } from '../hooks/useEscapeHandling';
import { useRetryStorySend } from '../hooks/useRetryStorySend';
@ -75,6 +76,7 @@ export type PropsType = {
hasAllStoriesUnmuted: boolean;
hasViewReceiptSetting: boolean;
i18n: LocalizerType;
isInternalUser?: boolean;
isSignalConversation?: boolean;
isWindowActive: boolean;
loadStoryReplies: (conversationId: string, messageId: string) => unknown;
@ -98,6 +100,7 @@ export type PropsType = {
renderEmojiPicker: (props: RenderEmojiPickerProps) => JSX.Element;
replyState?: ReplyStateType;
retrySend: (messageId: string) => unknown;
saveAttachment: typeof saveAttachment;
setHasAllStoriesUnmuted: (isUnmuted: boolean) => unknown;
showToast: ShowToastActionCreatorType;
skinTone?: number;
@ -130,6 +133,7 @@ export function StoryViewer({
hasAllStoriesUnmuted,
hasViewReceiptSetting,
i18n,
isInternalUser,
isSignalConversation,
isWindowActive,
loadStoryReplies,
@ -148,6 +152,7 @@ export function StoryViewer({
renderEmojiPicker,
replyState,
retrySend,
saveAttachment,
setHasAllStoriesUnmuted,
showToast,
skinTone,
@ -886,12 +891,14 @@ export function StoryViewer({
</div>
{currentViewTarget === StoryViewTargetType.Details && (
<StoryDetailsModal
attachment={attachment}
getPreferredBadge={getPreferredBadge}
i18n={i18n}
isInternalUser={isInternalUser}
onClose={() => setCurrentViewTarget(null)}
saveAttachment={saveAttachment}
sender={story.sender}
sendState={sendState}
size={attachment?.size}
timestamp={timestamp}
expirationTimestamp={story.expirationTimestamp}
/>
@ -905,6 +912,7 @@ export function StoryViewer({
hasViewReceiptSetting={hasViewReceiptSetting}
hasViewsCapability={isSent}
i18n={i18n}
isInternalUser={isInternalUser}
group={group}
onClose={() => setCurrentViewTarget(null)}
onReact={emoji => {

View file

@ -88,6 +88,7 @@ export type PropsType = {
hasViewReceiptSetting: boolean;
hasViewsCapability: boolean;
i18n: LocalizerType;
isInternalUser?: boolean;
group: Pick<ConversationType, 'left'> | undefined;
onClose: () => unknown;
onReact: (emoji: string) => unknown;
@ -120,6 +121,7 @@ export function StoryViewsNRepliesModal({
hasViewReceiptSetting,
hasViewsCapability,
i18n,
isInternalUser,
group,
onClose,
onReact,
@ -325,6 +327,7 @@ export function StoryViewsNRepliesModal({
<ReplyOrReactionMessage
key={reply.id}
i18n={i18n}
isInternalUser={isInternalUser}
reply={reply}
deleteGroupStoryReply={() => setDeleteReplyId(reply.id)}
deleteGroupStoryReplyForEveryone={() =>
@ -501,6 +504,7 @@ export function StoryViewsNRepliesModal({
type ReplyOrReactionMessageProps = {
i18n: LocalizerType;
isInternalUser?: boolean;
reply: ReplyType;
deleteGroupStoryReply: (replyId: string) => void;
deleteGroupStoryReplyForEveryone: (replyId: string) => void;
@ -513,6 +517,7 @@ type ReplyOrReactionMessageProps = {
function ReplyOrReactionMessage({
i18n,
isInternalUser,
reply,
deleteGroupStoryReply,
deleteGroupStoryReplyForEveryone,
@ -595,23 +600,31 @@ function ReplyOrReactionMessage({
);
};
const menuOptions = [
{
icon: 'module-message__context--icon module-message__context__delete-message',
label: i18n('icu:StoryViewsNRepliesModal__delete-reply'),
onClick: () => deleteGroupStoryReply(reply.id),
},
{
icon: 'module-message__context--icon module-message__context__delete-message-for-everyone',
label: i18n('icu:StoryViewsNRepliesModal__delete-reply-for-everyone'),
onClick: () => deleteGroupStoryReplyForEveryone(reply.id),
},
];
if (isInternalUser) {
menuOptions.push({
icon: 'module-message__context--icon module-message__context__copy-timestamp',
label: i18n('icu:StoryViewsNRepliesModal__copy-reply-timestamp'),
onClick: () => {
window.navigator.clipboard.writeText(String(reply.timestamp));
},
});
}
return reply.author.isMe && !reply.deletedForEveryone ? (
<ContextMenu
i18n={i18n}
key={reply.id}
menuOptions={[
{
icon: 'module-message__context--icon module-message__context__delete-message',
label: i18n('icu:StoryViewsNRepliesModal__delete-reply'),
onClick: () => deleteGroupStoryReply(reply.id),
},
{
icon: 'module-message__context--icon module-message__context__delete-message-for-everyone',
label: i18n('icu:StoryViewsNRepliesModal__delete-reply-for-everyone'),
onClick: () => deleteGroupStoryReplyForEveryone(reply.id),
},
]}
>
<ContextMenu i18n={i18n} key={reply.id} menuOptions={menuOptions}>
{({ openMenu, menuNode }) => (
<>
{renderContent(openMenu)}