Merge delete for me/everyone into one modal
This commit is contained in:
parent
c956c0e025
commit
822b162136
43 changed files with 658 additions and 672 deletions
|
@ -2383,7 +2383,7 @@
|
|||
},
|
||||
"icu:deleteMessage": {
|
||||
"messageformat": "Delete message for me",
|
||||
"description": "Shown on the drop-down menu for an individual message, deletes single message"
|
||||
"description": "(deleted 04/06/2023) Shown on the drop-down menu for an individual message, deletes single message"
|
||||
},
|
||||
"deleteMessageForEveryone": {
|
||||
"message": "Delete message for everyone",
|
||||
|
@ -2391,7 +2391,11 @@
|
|||
},
|
||||
"icu:deleteMessageForEveryone": {
|
||||
"messageformat": "Delete message for everyone",
|
||||
"description": "Shown on the drop-down menu for an individual message, deletes single message for everyone"
|
||||
"description": "(deleted 04/06/2023) Shown on the drop-down menu for an individual message, deletes single message for everyone"
|
||||
},
|
||||
"icu:MessageContextMenu__deleteMessage": {
|
||||
"messageformat": "Delete message",
|
||||
"description": "Show on the drop-down menu for an individual message, opens a modal to select if you want to 'delete for me' or 'delete for everyone'"
|
||||
},
|
||||
"deleteMessages": {
|
||||
"message": "Delete",
|
||||
|
@ -8953,6 +8957,10 @@
|
|||
},
|
||||
"icu:ConfirmDeleteForMeModal--title": {
|
||||
"messageformat": "Delete {count, plural, one {# message} other {# messages}}?",
|
||||
"description": "(deleted 04/06/2023) delete selected messages > confirmation modal > title"
|
||||
},
|
||||
"icu:DeleteMessagesModal--title": {
|
||||
"messageformat": "Delete {count, plural, one {message} other {# messages}}?",
|
||||
"description": "delete selected messages > confirmation modal > title"
|
||||
},
|
||||
"icu:SelectModeActions__confirmDelete--description": {
|
||||
|
@ -8961,6 +8969,10 @@
|
|||
},
|
||||
"icu:ConfirmDeleteForMeModal--description": {
|
||||
"messageformat": "{count, plural, one {This message} other {These messages}} will be deleted from this device.",
|
||||
"description": "(deleted 04/06/2023) delete selected messages > confirmation modal > description"
|
||||
},
|
||||
"icu:DeleteMessagesModal--description": {
|
||||
"messageformat": "Who would you like to delete {count, plural, one {this message} other {these messages}} for?",
|
||||
"description": "delete selected messages > confirmation modal > description"
|
||||
},
|
||||
"icu:SelectModeActions__confirmDelete--confirm": {
|
||||
|
@ -8969,7 +8981,19 @@
|
|||
},
|
||||
"icu:ConfirmDeleteForMeModal--confirm": {
|
||||
"messageformat": "Delete for me",
|
||||
"description": "delete selected messages > confirmation modal > button"
|
||||
"description": "(deleted 03/24/2023) delete selected messages > confirmation modal > button"
|
||||
},
|
||||
"icu:DeleteMessagesModal--deleteForMe": {
|
||||
"messageformat": "Delete for me",
|
||||
"description": "delete selected messages > confirmation modal > delete for me"
|
||||
},
|
||||
"icu:DeleteMessagesModal--deleteForEveryone": {
|
||||
"messageformat": "Delete for everyone",
|
||||
"description": "delete selected messages > confirmation modal > delete for everyone"
|
||||
},
|
||||
"icu:DeleteMessagesModal__toast--TooManyMessagesToDeleteForEveryone": {
|
||||
"messageformat": "You can only select up to {count, plural, one {# message} other {# messages}} to delete for everyone",
|
||||
"description": "delete selected messages > confirmation modal > deleted for everyone (disabled) > toast > too many messages to 'delete for everyone'"
|
||||
},
|
||||
"icu:SelectModeActions__toast--TooManyMessagesToForward": {
|
||||
"messageformat": "You can only forward up to 30 messages",
|
||||
|
|
6
stylesheets/components/DeleteMessagesModal.scss
Normal file
6
stylesheets/components/DeleteMessagesModal.scss
Normal file
|
@ -0,0 +1,6 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
.DeleteMessagesModal__ModalHost__width-container {
|
||||
min-width: fit-content;
|
||||
}
|
|
@ -71,6 +71,7 @@
|
|||
@import './components/CustomColorEditor.scss';
|
||||
@import './components/CustomizingPreferredReactionsModal.scss';
|
||||
@import './components/DebugLogWindow.scss';
|
||||
@import './components/DeleteMessagesModal.scss';
|
||||
@import './components/DisappearingTimeDialog.scss';
|
||||
@import './components/DisappearingTimerSelect.scss';
|
||||
@import './components/EditConversationAttributesModal.scss';
|
||||
|
|
|
@ -1730,22 +1730,13 @@ export async function startApp(): Promise<void> {
|
|||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
showConfirmationDialog({
|
||||
dialogName: 'ConfirmDeleteForMeModal',
|
||||
confirmStyle: 'negative',
|
||||
title: window.i18n('icu:ConfirmDeleteForMeModal--title', {
|
||||
count: messageIds.length,
|
||||
}),
|
||||
description: window.i18n(
|
||||
'icu:ConfirmDeleteForMeModal--description',
|
||||
{ count: messageIds.length }
|
||||
),
|
||||
okText: window.i18n('icu:ConfirmDeleteForMeModal--confirm'),
|
||||
resolve: () => {
|
||||
window.reduxActions.conversations.deleteMessages({
|
||||
window.reduxActions.globalModals.toggleDeleteMessagesModal({
|
||||
conversationId: conversation.id,
|
||||
messageIds,
|
||||
});
|
||||
onDelete() {
|
||||
if (selectedMessageIds != null) {
|
||||
window.reduxActions.conversations.toggleSelectMode(false);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -1771,7 +1762,12 @@ export async function startApp(): Promise<void> {
|
|||
event.stopPropagation();
|
||||
|
||||
window.reduxActions.globalModals.toggleForwardMessagesModal(
|
||||
messageIds
|
||||
messageIds,
|
||||
() => {
|
||||
if (selectedMessageIds != null) {
|
||||
window.reduxActions.conversations.toggleSelectMode(false);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
|
|
|
@ -8,11 +8,7 @@ import Measure from 'react-measure';
|
|||
import type { ListRowProps } from 'react-virtualized';
|
||||
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type {
|
||||
LocalizerType,
|
||||
ReplacementValuesType,
|
||||
ThemeType,
|
||||
} from '../types/Util';
|
||||
import type { LocalizerType, ThemeType } from '../types/Util';
|
||||
import { ToastType } from '../types/Toast';
|
||||
import { filterAndSortConversationsByRecent } from '../util/filterAndSortConversations';
|
||||
import { ConfirmationDialog } from './ConfirmationDialog';
|
||||
|
@ -26,6 +22,7 @@ import { SearchInput } from './SearchInput';
|
|||
import { useRestoreFocus } from '../hooks/useRestoreFocus';
|
||||
import { ListView } from './ListView';
|
||||
import { ListTile } from './ListTile';
|
||||
import type { ShowToastAction } from '../state/ducks/toast';
|
||||
|
||||
type OwnProps = {
|
||||
i18n: LocalizerType;
|
||||
|
@ -45,7 +42,7 @@ type DispatchProps = {
|
|||
onFailure?: () => unknown;
|
||||
}
|
||||
) => void;
|
||||
showToast: (toastType: ToastType, parameters?: ReplacementValuesType) => void;
|
||||
showToast: ShowToastAction;
|
||||
};
|
||||
|
||||
export type Props = OwnProps & DispatchProps;
|
||||
|
@ -225,14 +222,20 @@ export function AddUserToAnotherGroupModal({
|
|||
text: i18n('icu:AddUserToAnotherGroupModal__confirm-add'),
|
||||
style: 'affirmative',
|
||||
action: () => {
|
||||
showToast(ToastType.AddingUserToGroup, {
|
||||
showToast({
|
||||
toastType: ToastType.AddingUserToGroup,
|
||||
parameters: {
|
||||
contact: contact.title,
|
||||
},
|
||||
});
|
||||
addMembersToGroup(selectedGroupId, [contact.id], {
|
||||
onSuccess: () =>
|
||||
showToast(ToastType.UserAddedToGroup, {
|
||||
showToast({
|
||||
toastType: ToastType.UserAddedToGroup,
|
||||
parameters: {
|
||||
contact: contact.title,
|
||||
group: selectedGroup.title,
|
||||
},
|
||||
}),
|
||||
});
|
||||
toggleAddUserToAnotherGroupModal(undefined);
|
||||
|
|
|
@ -7,9 +7,9 @@ import classNames from 'classnames';
|
|||
|
||||
import type { ExecuteMenuRoleType } from './TitleBarContainer';
|
||||
import type { MenuOptionsType, MenuActionType } from '../types/menu';
|
||||
import type { ToastType } from '../types/Toast';
|
||||
import type { AnyToast } from '../types/Toast';
|
||||
import type { ViewStoryActionCreatorType } from '../state/ducks/stories';
|
||||
import type { LocalizerType, ReplacementValuesType } from '../types/Util';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { ThemeType } from '../types/Util';
|
||||
import { AppViewType } from '../state/ducks/app';
|
||||
import { SmartInstallScreen } from '../state/smart/InstallScreen';
|
||||
|
@ -51,10 +51,7 @@ type PropsType = {
|
|||
executeMenuAction: (action: MenuActionType) => void;
|
||||
hideToast: () => unknown;
|
||||
titleBarDoubleClick: () => void;
|
||||
toast?: {
|
||||
toastType: ToastType;
|
||||
parameters?: ReplacementValuesType;
|
||||
};
|
||||
toast?: AnyToast;
|
||||
scrollToMessage: (conversationId: string, messageId: string) => unknown;
|
||||
toggleStoriesView: () => unknown;
|
||||
viewStory: ViewStoryActionCreatorType;
|
||||
|
|
|
@ -132,7 +132,6 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
renderSmartCompositionRecordingDraft: _ => <div>RECORDING DRAFT</div>,
|
||||
// Select mode
|
||||
selectedMessageIds: undefined,
|
||||
lastSelectedMessage: undefined,
|
||||
toggleSelectMode: action('toggleSelectMode'),
|
||||
toggleForwardMessagesModal: action('toggleForwardMessagesModal'),
|
||||
});
|
||||
|
|
|
@ -39,7 +39,6 @@ import { AudioCapture } from './conversation/AudioCapture';
|
|||
import { CompositionUpload } from './CompositionUpload';
|
||||
import type {
|
||||
ConversationType,
|
||||
MessageTimestamps,
|
||||
PushPanelForConversationActionType,
|
||||
ShowConversationType,
|
||||
} from '../state/ducks/conversations';
|
||||
|
@ -149,7 +148,6 @@ export type OwnProps = Readonly<{
|
|||
props: SmartCompositionRecordingDraftProps
|
||||
) => JSX.Element | null;
|
||||
selectedMessageIds: ReadonlyArray<string> | undefined;
|
||||
lastSelectedMessage: MessageTimestamps | undefined;
|
||||
toggleSelectMode: (on: boolean) => void;
|
||||
toggleForwardMessagesModal: (
|
||||
messageIds: ReadonlyArray<string>,
|
||||
|
@ -287,7 +285,6 @@ export function CompositionArea({
|
|||
renderSmartCompositionRecordingDraft,
|
||||
// Selected messages
|
||||
selectedMessageIds,
|
||||
lastSelectedMessage,
|
||||
toggleSelectMode,
|
||||
toggleForwardMessagesModal,
|
||||
}: Props): JSX.Element | null {
|
||||
|
@ -560,12 +557,13 @@ export function CompositionArea({
|
|||
toggleSelectMode(false);
|
||||
}}
|
||||
onDeleteMessages={() => {
|
||||
window.reduxActions.conversations.deleteMessages({
|
||||
window.reduxActions.globalModals.toggleDeleteMessagesModal({
|
||||
conversationId,
|
||||
lastSelectedMessage,
|
||||
messageIds: selectedMessageIds,
|
||||
});
|
||||
onDelete() {
|
||||
toggleSelectMode(false);
|
||||
},
|
||||
});
|
||||
}}
|
||||
onForwardMessages={() => {
|
||||
if (selectedMessageIds.length > 0) {
|
||||
|
|
|
@ -16,6 +16,8 @@ export type ActionSpec = {
|
|||
action: () => unknown;
|
||||
style?: 'affirmative' | 'negative';
|
||||
autoClose?: boolean;
|
||||
disabled?: boolean;
|
||||
'aria-disabled'?: boolean;
|
||||
} & (
|
||||
| {
|
||||
text: string;
|
||||
|
@ -130,7 +132,8 @@ export const ConfirmationDialog = React.memo(function ConfirmationDialogInner({
|
|||
? action.id ?? action.text
|
||||
: action.id
|
||||
}
|
||||
disabled={isSpinning}
|
||||
disabled={action.disabled || isSpinning}
|
||||
aria-disabled={action['aria-disabled']}
|
||||
onClick={() => {
|
||||
action.action();
|
||||
if (action.autoClose !== false) {
|
||||
|
@ -165,6 +168,9 @@ export const ConfirmationDialog = React.memo(function ConfirmationDialogInner({
|
|||
onTopOfEverything={onTopOfEverything}
|
||||
overlayStyles={overlayStyles}
|
||||
theme={theme}
|
||||
moduleClassName={
|
||||
moduleClassName ? `${moduleClassName}__ModalHost` : undefined
|
||||
}
|
||||
>
|
||||
<animated.div style={modalStyles}>
|
||||
<ModalPage
|
||||
|
|
76
ts/components/DeleteMessagesModal.tsx
Normal file
76
ts/components/DeleteMessagesModal.tsx
Normal file
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import type { ActionSpec } from './ConfirmationDialog';
|
||||
import { ConfirmationDialog } from './ConfirmationDialog';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { ShowToastAction } from '../state/ducks/toast';
|
||||
import { ToastType } from '../types/Toast';
|
||||
|
||||
type DeleteMessagesModalProps = Readonly<{
|
||||
canDeleteForEveryone: boolean;
|
||||
i18n: LocalizerType;
|
||||
messageCount: number;
|
||||
onClose: () => void;
|
||||
onDeleteForMe: () => void;
|
||||
onDeleteForEveryone: () => void;
|
||||
showToast: ShowToastAction;
|
||||
}>;
|
||||
|
||||
const MAX_DELETE_FOR_EVERYONE = 30;
|
||||
|
||||
export default function DeleteMessagesModal({
|
||||
canDeleteForEveryone,
|
||||
i18n,
|
||||
messageCount,
|
||||
onClose,
|
||||
onDeleteForMe,
|
||||
onDeleteForEveryone,
|
||||
showToast,
|
||||
}: DeleteMessagesModalProps): JSX.Element {
|
||||
const actions: Array<ActionSpec> = [];
|
||||
|
||||
actions.push({
|
||||
action: onDeleteForMe,
|
||||
style: 'negative',
|
||||
text: i18n('icu:DeleteMessagesModal--deleteForMe'),
|
||||
});
|
||||
|
||||
if (canDeleteForEveryone) {
|
||||
const tooManyMessages = messageCount > MAX_DELETE_FOR_EVERYONE;
|
||||
actions.push({
|
||||
'aria-disabled': tooManyMessages,
|
||||
autoClose: !tooManyMessages,
|
||||
action: () => {
|
||||
if (tooManyMessages) {
|
||||
showToast({
|
||||
toastType: ToastType.TooManyMessagesToDeleteForEveryone,
|
||||
parameters: { count: MAX_DELETE_FOR_EVERYONE },
|
||||
});
|
||||
} else {
|
||||
onDeleteForEveryone();
|
||||
}
|
||||
},
|
||||
style: 'negative',
|
||||
text: i18n('icu:DeleteMessagesModal--deleteForEveryone'),
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfirmationDialog
|
||||
actions={actions}
|
||||
dialogName="ConfirmDeleteForMeModal"
|
||||
i18n={i18n}
|
||||
onClose={onClose}
|
||||
title={i18n('icu:DeleteMessagesModal--title', {
|
||||
count: messageCount,
|
||||
})}
|
||||
moduleClassName="DeleteMessagesModal"
|
||||
>
|
||||
{i18n('icu:DeleteMessagesModal--description', {
|
||||
count: messageCount,
|
||||
})}
|
||||
</ConfirmationDialog>
|
||||
);
|
||||
}
|
|
@ -38,7 +38,7 @@ export class ErrorBoundary extends React.PureComponent<Props, State> {
|
|||
`\nerrorInfo: ${errorInfo.componentStack}`
|
||||
);
|
||||
if (window.reduxActions) {
|
||||
window.reduxActions.toast.showToast(ToastType.Error);
|
||||
window.reduxActions.toast.showToast({ toastType: ToastType.Error });
|
||||
}
|
||||
if (closeView) {
|
||||
closeView();
|
||||
|
|
|
@ -127,7 +127,7 @@ export function ForwardMessagesModal({
|
|||
|
||||
const forwardMessages = React.useCallback(() => {
|
||||
if (!canForwardMessages) {
|
||||
showToast(ToastType.CannotForwardEmptyMessage);
|
||||
showToast({ toastType: ToastType.CannotForwardEmptyMessage });
|
||||
return;
|
||||
}
|
||||
const conversationIds = selectedContacts.map(contact => contact.id);
|
||||
|
|
|
@ -5,6 +5,7 @@ import React from 'react';
|
|||
import type {
|
||||
AuthorizeArtCreatorDataType,
|
||||
ContactModalStateType,
|
||||
DeleteMessagesPropsType,
|
||||
EditHistoryMessagesType,
|
||||
ForwardMessagesPropsType,
|
||||
SafetyNumberChangedBlockingDataType,
|
||||
|
@ -38,6 +39,9 @@ export type PropsType = {
|
|||
description?: string;
|
||||
title?: string;
|
||||
}) => JSX.Element;
|
||||
// DeleteMessageModal
|
||||
deleteMessagesProps: DeleteMessagesPropsType | undefined;
|
||||
renderDeleteMessagesModal: () => JSX.Element;
|
||||
// ForwardMessageModal
|
||||
forwardMessagesProps: ForwardMessagesPropsType | undefined;
|
||||
renderForwardMessagesModal: () => JSX.Element;
|
||||
|
@ -92,6 +96,9 @@ export function GlobalModalContainer({
|
|||
// ErrorModal
|
||||
errorModalProps,
|
||||
renderErrorModal,
|
||||
// DeleteMessageModal
|
||||
deleteMessagesProps,
|
||||
renderDeleteMessagesModal,
|
||||
// ForwardMessageModal
|
||||
forwardMessagesProps,
|
||||
renderForwardMessagesModal,
|
||||
|
@ -158,6 +165,10 @@ export function GlobalModalContainer({
|
|||
return renderEditHistoryMessagesModal();
|
||||
}
|
||||
|
||||
if (deleteMessagesProps) {
|
||||
return renderDeleteMessagesModal();
|
||||
}
|
||||
|
||||
if (forwardMessagesProps) {
|
||||
return renderForwardMessagesModal();
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import classNames from 'classnames';
|
|||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { MyStoryType, StoryViewType } from '../types/Stories';
|
||||
import type { ShowToastActionCreatorType } from '../state/ducks/toast';
|
||||
import type { ShowToastAction } from '../state/ducks/toast';
|
||||
import { Avatar, AvatarSize } from './Avatar';
|
||||
import { HasStories, ResolvedSendStatus } from '../types/Stories';
|
||||
import { MessageTimestamp } from './conversation/MessageTimestamp';
|
||||
|
@ -24,7 +24,7 @@ export type PropsType = {
|
|||
onClick: () => unknown;
|
||||
onMediaPlaybackStart: () => void;
|
||||
queueStoryDownload: (storyId: string) => unknown;
|
||||
showToast: ShowToastActionCreatorType;
|
||||
showToast: ShowToastAction;
|
||||
};
|
||||
|
||||
function getNewestMyStory(story: MyStoryType): StoryViewType {
|
||||
|
|
|
@ -28,7 +28,7 @@ import { PanelRow } from './conversation/conversation-details/PanelRow';
|
|||
import type { ProfileDataType } from '../state/ducks/conversations';
|
||||
import { UsernameEditState } from '../state/ducks/usernameEnums';
|
||||
import { ToastType } from '../types/Toast';
|
||||
import type { ShowToastActionCreatorType } from '../state/ducks/toast';
|
||||
import type { ShowToastAction } from '../state/ducks/toast';
|
||||
import { getEmojiData, unifiedToEmoji } from './emoji/lib';
|
||||
import { assertDev } from '../util/assert';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
|
@ -85,7 +85,7 @@ type PropsActionType = {
|
|||
saveAvatarToDisk: SaveAvatarToDiskActionType;
|
||||
setUsernameEditState: (editState: UsernameEditState) => void;
|
||||
deleteUsername: () => void;
|
||||
showToast: ShowToastActionCreatorType;
|
||||
showToast: ShowToastAction;
|
||||
openUsernameReservationModal: () => void;
|
||||
};
|
||||
|
||||
|
@ -525,7 +525,7 @@ export function ProfileEditor({
|
|||
'Should not be visible without username'
|
||||
);
|
||||
void window.navigator.clipboard.writeText(username);
|
||||
showToast(ToastType.CopiedUsername);
|
||||
showToast({ toastType: ToastType.CopiedUsername });
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -540,7 +540,7 @@ export function ProfileEditor({
|
|||
void window.navigator.clipboard.writeText(
|
||||
generateUsernameLink(username)
|
||||
);
|
||||
showToast(ToastType.CopiedUsernameLink);
|
||||
showToast({ toastType: ToastType.CopiedUsernameLink });
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -14,7 +14,7 @@ import type {
|
|||
} from '../types/Stories';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import type { ShowToastActionCreatorType } from '../state/ducks/toast';
|
||||
import type { ShowToastAction } from '../state/ducks/toast';
|
||||
import type {
|
||||
AddStoryData,
|
||||
ViewUserStoriesActionCreatorType,
|
||||
|
@ -48,7 +48,7 @@ export type PropsType = {
|
|||
setAddStoryData: (data: AddStoryData) => unknown;
|
||||
showConversation: ShowConversationType;
|
||||
showStoriesSettings: () => unknown;
|
||||
showToast: ShowToastActionCreatorType;
|
||||
showToast: ShowToastAction;
|
||||
stories: Array<ConversationStoryType>;
|
||||
toggleHideStories: (conversationId: string) => unknown;
|
||||
toggleStoriesView: () => unknown;
|
||||
|
|
|
@ -5,7 +5,7 @@ import type { ReactNode } from 'react';
|
|||
import React, { useState, useCallback } from 'react';
|
||||
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { ShowToastActionCreatorType } from '../state/ducks/toast';
|
||||
import type { ShowToastAction } from '../state/ducks/toast';
|
||||
import { ContextMenu } from './ContextMenu';
|
||||
import { Theme } from '../util/theme';
|
||||
import { ToastType } from '../types/Toast';
|
||||
|
@ -22,7 +22,7 @@ export type PropsType = {
|
|||
moduleClassName?: string;
|
||||
onAddStory: (file?: File) => unknown;
|
||||
onContextMenuShowingChanged?: (value: boolean) => void;
|
||||
showToast: ShowToastActionCreatorType;
|
||||
showToast: ShowToastAction;
|
||||
};
|
||||
|
||||
export function StoriesAddStoryButton({
|
||||
|
@ -55,7 +55,7 @@ export function StoriesAddStoryButton({
|
|||
result.reason === ReasonVideoNotGood.UnsupportedCodec ||
|
||||
result.reason === ReasonVideoNotGood.UnsupportedContainer
|
||||
) {
|
||||
showToast(ToastType.StoryVideoUnsupported);
|
||||
showToast({ toastType: ToastType.StoryVideoUnsupported });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ export function StoriesAddStoryButton({
|
|||
}
|
||||
|
||||
if (result.reason !== ReasonVideoNotGood.AllGoodNevermind) {
|
||||
showToast(ToastType.StoryVideoError);
|
||||
showToast({ toastType: ToastType.StoryVideoError });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import type {
|
|||
import type { ConversationStoryType, MyStoryType } from '../types/Stories';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import type { ShowToastActionCreatorType } from '../state/ducks/toast';
|
||||
import type { ShowToastAction } from '../state/ducks/toast';
|
||||
import type { ViewUserStoriesActionCreatorType } from '../state/ducks/stories';
|
||||
import { ContextMenu } from './ContextMenu';
|
||||
import { MyStoryButton } from './MyStoryButton';
|
||||
|
@ -68,7 +68,7 @@ export type PropsType = {
|
|||
onMediaPlaybackStart: () => void;
|
||||
queueStoryDownload: (storyId: string) => unknown;
|
||||
showConversation: ShowConversationType;
|
||||
showToast: ShowToastActionCreatorType;
|
||||
showToast: ShowToastAction;
|
||||
stories: Array<ConversationStoryType>;
|
||||
toggleHideStories: (conversationId: string) => unknown;
|
||||
toggleStoriesView: () => unknown;
|
||||
|
|
|
@ -21,7 +21,7 @@ import type { EmojiPickDataType } from './emoji/EmojiPicker';
|
|||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import type { RenderEmojiPickerProps } from './conversation/ReactionPicker';
|
||||
import type { ReplyStateType, StoryViewType } from '../types/Stories';
|
||||
import type { ShowToastActionCreatorType } from '../state/ducks/toast';
|
||||
import type { ShowToastAction } from '../state/ducks/toast';
|
||||
import type { ViewStoryActionCreatorType } from '../state/ducks/stories';
|
||||
import * as log from '../logging/log';
|
||||
import { AnimatedEmojiGalore } from './AnimatedEmojiGalore';
|
||||
|
@ -113,7 +113,7 @@ export type PropsType = {
|
|||
saveAttachment: SaveAttachmentActionCreatorType;
|
||||
setHasAllStoriesUnmuted: (isUnmuted: boolean) => unknown;
|
||||
showContactModal: (contactId: string, conversationId?: string) => void;
|
||||
showToast: ShowToastActionCreatorType;
|
||||
showToast: ShowToastAction;
|
||||
skinTone?: number;
|
||||
story: StoryViewType;
|
||||
storyViewMode: StoryViewModeType;
|
||||
|
@ -786,7 +786,7 @@ export function StoryViewer({
|
|||
onClick={
|
||||
hasAudio
|
||||
? () => setHasAllStoriesUnmuted(!hasAllStoriesUnmuted)
|
||||
: () => showToast(ToastType.StoryMuted)
|
||||
: () => showToast({ toastType: ToastType.StoryMuted })
|
||||
}
|
||||
type="button"
|
||||
/>
|
||||
|
@ -940,14 +940,14 @@ export function StoryViewer({
|
|||
onReactToStory(emoji, story);
|
||||
if (!isGroupStory) {
|
||||
setCurrentViewTarget(null);
|
||||
showToast(ToastType.StoryReact);
|
||||
showToast({ toastType: ToastType.StoryReact });
|
||||
}
|
||||
setReactionEmoji(emoji);
|
||||
}}
|
||||
onReply={(message, mentions, replyTimestamp) => {
|
||||
if (!isGroupStory) {
|
||||
setCurrentViewTarget(null);
|
||||
showToast(ToastType.StoryReply);
|
||||
showToast({ toastType: ToastType.StoryReply });
|
||||
}
|
||||
onReplyToStory(message, mentions, replyTimestamp, story);
|
||||
}}
|
||||
|
|
|
@ -4,14 +4,136 @@
|
|||
import type { Meta, Story } from '@storybook/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { PropsType } from './ToastManager';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
import { ToastManager } from './ToastManager';
|
||||
import type { AnyToast } from '../types/Toast';
|
||||
import { ToastType } from '../types/Toast';
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
import type { PropsType } from './ToastManager';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
function getToast(toastType: ToastType): AnyToast {
|
||||
switch (toastType) {
|
||||
case ToastType.AddingUserToGroup:
|
||||
return { toastType, parameters: { contact: 'Sam Mirete' } };
|
||||
case ToastType.AlreadyGroupMember:
|
||||
return { toastType: ToastType.AlreadyGroupMember };
|
||||
case ToastType.AlreadyRequestedToJoin:
|
||||
return { toastType: ToastType.AlreadyRequestedToJoin };
|
||||
case ToastType.Blocked:
|
||||
return { toastType: ToastType.Blocked };
|
||||
case ToastType.BlockedGroup:
|
||||
return { toastType: ToastType.BlockedGroup };
|
||||
case ToastType.CannotForwardEmptyMessage:
|
||||
return { toastType: ToastType.CannotForwardEmptyMessage };
|
||||
case ToastType.CannotMixMultiAndNonMultiAttachments:
|
||||
return { toastType: ToastType.CannotMixMultiAndNonMultiAttachments };
|
||||
case ToastType.CannotOpenGiftBadgeIncoming:
|
||||
return { toastType: ToastType.CannotOpenGiftBadgeIncoming };
|
||||
case ToastType.CannotOpenGiftBadgeOutgoing:
|
||||
return { toastType: ToastType.CannotOpenGiftBadgeOutgoing };
|
||||
case ToastType.CannotStartGroupCall:
|
||||
return { toastType: ToastType.CannotStartGroupCall };
|
||||
case ToastType.ConversationArchived:
|
||||
return {
|
||||
toastType: ToastType.ConversationArchived,
|
||||
parameters: { conversationId: 'some-conversation-id' },
|
||||
};
|
||||
case ToastType.ConversationMarkedUnread:
|
||||
return { toastType: ToastType.ConversationMarkedUnread };
|
||||
case ToastType.ConversationRemoved:
|
||||
return {
|
||||
toastType: ToastType.ConversationRemoved,
|
||||
parameters: { title: 'Alice' },
|
||||
};
|
||||
case ToastType.ConversationUnarchived:
|
||||
return { toastType: ToastType.ConversationUnarchived };
|
||||
case ToastType.CopiedUsername:
|
||||
return { toastType: ToastType.CopiedUsername };
|
||||
case ToastType.CopiedUsernameLink:
|
||||
return { toastType: ToastType.CopiedUsernameLink };
|
||||
case ToastType.DangerousFileType:
|
||||
return { toastType: ToastType.DangerousFileType };
|
||||
case ToastType.DeleteForEveryoneFailed:
|
||||
return { toastType: ToastType.DeleteForEveryoneFailed };
|
||||
case ToastType.Error:
|
||||
return { toastType: ToastType.Error };
|
||||
case ToastType.Expired:
|
||||
return { toastType: ToastType.Expired };
|
||||
case ToastType.FailedToDeleteUsername:
|
||||
return { toastType: ToastType.FailedToDeleteUsername };
|
||||
case ToastType.FileSaved:
|
||||
return {
|
||||
toastType: ToastType.FileSaved,
|
||||
parameters: { fullPath: '/image.png' },
|
||||
};
|
||||
case ToastType.FileSize:
|
||||
return {
|
||||
toastType: ToastType.FileSize,
|
||||
parameters: { limit: 100, units: 'MB' },
|
||||
};
|
||||
case ToastType.InvalidConversation:
|
||||
return { toastType: ToastType.InvalidConversation };
|
||||
case ToastType.LeftGroup:
|
||||
return { toastType: ToastType.LeftGroup };
|
||||
case ToastType.MaxAttachments:
|
||||
return { toastType: ToastType.MaxAttachments };
|
||||
case ToastType.MessageBodyTooLong:
|
||||
return { toastType: ToastType.MessageBodyTooLong };
|
||||
case ToastType.OriginalMessageNotFound:
|
||||
return { toastType: ToastType.OriginalMessageNotFound };
|
||||
case ToastType.PinnedConversationsFull:
|
||||
return { toastType: ToastType.PinnedConversationsFull };
|
||||
case ToastType.ReactionFailed:
|
||||
return { toastType: ToastType.ReactionFailed };
|
||||
case ToastType.ReportedSpamAndBlocked:
|
||||
return { toastType: ToastType.ReportedSpamAndBlocked };
|
||||
case ToastType.StoryMuted:
|
||||
return { toastType: ToastType.StoryMuted };
|
||||
case ToastType.StoryReact:
|
||||
return { toastType: ToastType.StoryReact };
|
||||
case ToastType.StoryReply:
|
||||
return { toastType: ToastType.StoryReply };
|
||||
case ToastType.StoryVideoError:
|
||||
return { toastType: ToastType.StoryVideoError };
|
||||
case ToastType.StoryVideoUnsupported:
|
||||
return { toastType: ToastType.StoryVideoUnsupported };
|
||||
case ToastType.TapToViewExpiredIncoming:
|
||||
return { toastType: ToastType.TapToViewExpiredIncoming };
|
||||
case ToastType.TapToViewExpiredOutgoing:
|
||||
return { toastType: ToastType.TapToViewExpiredOutgoing };
|
||||
case ToastType.TooManyMessagesToDeleteForEveryone:
|
||||
return {
|
||||
toastType: ToastType.TooManyMessagesToDeleteForEveryone,
|
||||
parameters: { count: 30 },
|
||||
};
|
||||
case ToastType.TooManyMessagesToForward:
|
||||
return { toastType: ToastType.TooManyMessagesToForward };
|
||||
case ToastType.UnableToLoadAttachment:
|
||||
return { toastType: ToastType.UnableToLoadAttachment };
|
||||
case ToastType.UnsupportedMultiAttachment:
|
||||
return { toastType: ToastType.UnsupportedMultiAttachment };
|
||||
case ToastType.UnsupportedOS:
|
||||
return { toastType: ToastType.UnsupportedOS };
|
||||
case ToastType.UserAddedToGroup:
|
||||
return {
|
||||
toastType: ToastType.UserAddedToGroup,
|
||||
parameters: {
|
||||
contact: 'Sam Mirete',
|
||||
group: 'Hike Group 🏔',
|
||||
},
|
||||
};
|
||||
default:
|
||||
throw missingCaseError(toastType);
|
||||
}
|
||||
}
|
||||
|
||||
type Args = Omit<PropsType, 'toast'> & {
|
||||
toastType: ToastType;
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'Components/ToastManager',
|
||||
component: ToastManager,
|
||||
|
@ -22,331 +144,26 @@ export default {
|
|||
i18n: {
|
||||
defaultValue: i18n,
|
||||
},
|
||||
toast: {
|
||||
defaultValue: undefined,
|
||||
toastType: {
|
||||
defaultValue: ToastType.AddingUserToGroup,
|
||||
options: ToastType,
|
||||
control: { type: 'select' },
|
||||
},
|
||||
OS: {
|
||||
defaultValue: 'macOS',
|
||||
},
|
||||
},
|
||||
} as Meta;
|
||||
} as Meta<Args>;
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
const Template: Story<PropsType> = args => <ToastManager {...args} />;
|
||||
|
||||
export const UndefinedToast = Template.bind({});
|
||||
UndefinedToast.args = {};
|
||||
|
||||
export const InvalidToast = Template.bind({});
|
||||
InvalidToast.args = {
|
||||
toast: {
|
||||
toastType: 'this is a toast that does not exist' as ToastType,
|
||||
},
|
||||
const Template: Story<Args> = args => {
|
||||
const { toastType, ...rest } = args;
|
||||
return (
|
||||
<>
|
||||
<p>Select a toast type in controls</p>
|
||||
<ToastManager toast={getToast(toastType)} {...rest} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const AddingUserToGroup = Template.bind({});
|
||||
AddingUserToGroup.args = {
|
||||
toast: {
|
||||
toastType: ToastType.AddingUserToGroup,
|
||||
parameters: {
|
||||
contact: 'Sam Mirete',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const AlreadyGroupMember = Template.bind({});
|
||||
AlreadyGroupMember.args = {
|
||||
toast: {
|
||||
toastType: ToastType.AlreadyGroupMember,
|
||||
},
|
||||
};
|
||||
|
||||
export const AlreadyRequestedToJoin = Template.bind({});
|
||||
AlreadyRequestedToJoin.args = {
|
||||
toast: {
|
||||
toastType: ToastType.AlreadyRequestedToJoin,
|
||||
},
|
||||
};
|
||||
|
||||
export const Blocked = Template.bind({});
|
||||
Blocked.args = {
|
||||
toast: {
|
||||
toastType: ToastType.Blocked,
|
||||
},
|
||||
};
|
||||
|
||||
export const BlockedGroup = Template.bind({});
|
||||
BlockedGroup.args = {
|
||||
toast: {
|
||||
toastType: ToastType.BlockedGroup,
|
||||
},
|
||||
};
|
||||
|
||||
export const CannotMixMultiAndNonMultiAttachments = Template.bind({});
|
||||
CannotMixMultiAndNonMultiAttachments.args = {
|
||||
toast: {
|
||||
toastType: ToastType.CannotMixMultiAndNonMultiAttachments,
|
||||
},
|
||||
};
|
||||
|
||||
export const CannotOpenGiftBadgeIncoming = Template.bind({});
|
||||
CannotOpenGiftBadgeIncoming.args = {
|
||||
toast: {
|
||||
toastType: ToastType.CannotOpenGiftBadgeIncoming,
|
||||
},
|
||||
};
|
||||
|
||||
export const CannotOpenGiftBadgeOutgoing = Template.bind({});
|
||||
CannotOpenGiftBadgeOutgoing.args = {
|
||||
toast: {
|
||||
toastType: ToastType.CannotOpenGiftBadgeOutgoing,
|
||||
},
|
||||
};
|
||||
|
||||
export const CannotStartGroupCall = Template.bind({});
|
||||
CannotStartGroupCall.args = {
|
||||
toast: {
|
||||
toastType: ToastType.CannotStartGroupCall,
|
||||
},
|
||||
};
|
||||
|
||||
export const ConversationArchived = Template.bind({});
|
||||
ConversationArchived.args = {
|
||||
toast: {
|
||||
toastType: ToastType.ConversationArchived,
|
||||
parameters: {
|
||||
conversationId: 'some-conversation-id',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const ConversationMarkedUnread = Template.bind({});
|
||||
ConversationMarkedUnread.args = {
|
||||
toast: {
|
||||
toastType: ToastType.ConversationMarkedUnread,
|
||||
},
|
||||
};
|
||||
|
||||
export const ConversationRemoved = Template.bind({});
|
||||
ConversationRemoved.args = {
|
||||
toast: {
|
||||
toastType: ToastType.ConversationRemoved,
|
||||
parameters: {
|
||||
title: 'Alice',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const ConversationUnarchived = Template.bind({});
|
||||
ConversationUnarchived.args = {
|
||||
toast: {
|
||||
toastType: ToastType.ConversationUnarchived,
|
||||
},
|
||||
};
|
||||
|
||||
export const CopiedUsername = Template.bind({});
|
||||
CopiedUsername.args = {
|
||||
toast: {
|
||||
toastType: ToastType.CopiedUsername,
|
||||
},
|
||||
};
|
||||
|
||||
export const CopiedUsernameLink = Template.bind({});
|
||||
CopiedUsernameLink.args = {
|
||||
toast: {
|
||||
toastType: ToastType.CopiedUsernameLink,
|
||||
},
|
||||
};
|
||||
|
||||
export const DangerousFileType = Template.bind({});
|
||||
DangerousFileType.args = {
|
||||
toast: {
|
||||
toastType: ToastType.DangerousFileType,
|
||||
},
|
||||
};
|
||||
|
||||
export const DeleteForEveryoneFailed = Template.bind({});
|
||||
DeleteForEveryoneFailed.args = {
|
||||
toast: {
|
||||
toastType: ToastType.DeleteForEveryoneFailed,
|
||||
},
|
||||
};
|
||||
|
||||
export const Error = Template.bind({});
|
||||
Error.args = {
|
||||
toast: {
|
||||
toastType: ToastType.Error,
|
||||
},
|
||||
};
|
||||
|
||||
export const Expired = Template.bind({});
|
||||
Expired.args = {
|
||||
toast: {
|
||||
toastType: ToastType.Expired,
|
||||
},
|
||||
};
|
||||
|
||||
export const FailedToDeleteUsername = Template.bind({});
|
||||
FailedToDeleteUsername.args = {
|
||||
toast: {
|
||||
toastType: ToastType.FailedToDeleteUsername,
|
||||
},
|
||||
};
|
||||
|
||||
export const FileSaved = Template.bind({});
|
||||
FileSaved.args = {
|
||||
toast: {
|
||||
toastType: ToastType.FileSaved,
|
||||
parameters: {
|
||||
fullPath: '/image.png',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const FileSize = Template.bind({});
|
||||
FileSize.args = {
|
||||
toast: {
|
||||
toastType: ToastType.FileSize,
|
||||
parameters: {
|
||||
limit: '100',
|
||||
units: 'MB',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const InvalidConversation = Template.bind({});
|
||||
InvalidConversation.args = {
|
||||
toast: {
|
||||
toastType: ToastType.InvalidConversation,
|
||||
},
|
||||
};
|
||||
|
||||
export const LeftGroup = Template.bind({});
|
||||
LeftGroup.args = {
|
||||
toast: {
|
||||
toastType: ToastType.LeftGroup,
|
||||
},
|
||||
};
|
||||
|
||||
export const MaxAttachments = Template.bind({});
|
||||
MaxAttachments.args = {
|
||||
toast: {
|
||||
toastType: ToastType.MaxAttachments,
|
||||
},
|
||||
};
|
||||
|
||||
export const OriginalMessageNotFound = Template.bind({});
|
||||
OriginalMessageNotFound.args = {
|
||||
toast: {
|
||||
toastType: ToastType.OriginalMessageNotFound,
|
||||
},
|
||||
};
|
||||
|
||||
export const MessageBodyTooLong = Template.bind({});
|
||||
MessageBodyTooLong.args = {
|
||||
toast: {
|
||||
toastType: ToastType.MessageBodyTooLong,
|
||||
},
|
||||
};
|
||||
|
||||
export const PinnedConversationsFull = Template.bind({});
|
||||
PinnedConversationsFull.args = {
|
||||
toast: {
|
||||
toastType: ToastType.PinnedConversationsFull,
|
||||
},
|
||||
};
|
||||
|
||||
export const ReactionFailed = Template.bind({});
|
||||
ReactionFailed.args = {
|
||||
toast: {
|
||||
toastType: ToastType.ReactionFailed,
|
||||
},
|
||||
};
|
||||
|
||||
export const ReportedSpamAndBlocked = Template.bind({});
|
||||
ReportedSpamAndBlocked.args = {
|
||||
toast: {
|
||||
toastType: ToastType.ReportedSpamAndBlocked,
|
||||
},
|
||||
};
|
||||
|
||||
export const StoryMuted = Template.bind({});
|
||||
StoryMuted.args = {
|
||||
toast: {
|
||||
toastType: ToastType.StoryMuted,
|
||||
},
|
||||
};
|
||||
|
||||
export const StoryReact = Template.bind({});
|
||||
StoryReact.args = {
|
||||
toast: {
|
||||
toastType: ToastType.StoryReact,
|
||||
},
|
||||
};
|
||||
|
||||
export const StoryReply = Template.bind({});
|
||||
StoryReply.args = {
|
||||
toast: {
|
||||
toastType: ToastType.StoryReply,
|
||||
},
|
||||
};
|
||||
|
||||
export const StoryVideoError = Template.bind({});
|
||||
StoryVideoError.args = {
|
||||
toast: {
|
||||
toastType: ToastType.StoryVideoError,
|
||||
},
|
||||
};
|
||||
|
||||
export const StoryVideoUnsupported = Template.bind({});
|
||||
StoryVideoUnsupported.args = {
|
||||
toast: {
|
||||
toastType: ToastType.StoryVideoUnsupported,
|
||||
},
|
||||
};
|
||||
|
||||
export const TapToViewExpiredIncoming = Template.bind({});
|
||||
TapToViewExpiredIncoming.args = {
|
||||
toast: {
|
||||
toastType: ToastType.TapToViewExpiredIncoming,
|
||||
},
|
||||
};
|
||||
|
||||
export const TapToViewExpiredOutgoing = Template.bind({});
|
||||
TapToViewExpiredOutgoing.args = {
|
||||
toast: {
|
||||
toastType: ToastType.TapToViewExpiredOutgoing,
|
||||
},
|
||||
};
|
||||
|
||||
export const UnableToLoadAttachment = Template.bind({});
|
||||
UnableToLoadAttachment.args = {
|
||||
toast: {
|
||||
toastType: ToastType.UnableToLoadAttachment,
|
||||
},
|
||||
};
|
||||
|
||||
export const UnsupportedMultiAttachment = Template.bind({});
|
||||
UnsupportedMultiAttachment.args = {
|
||||
toast: {
|
||||
toastType: ToastType.UnsupportedMultiAttachment,
|
||||
},
|
||||
};
|
||||
|
||||
export const UnsupportedOS = Template.bind({});
|
||||
UnsupportedOS.args = {
|
||||
toast: {
|
||||
toastType: ToastType.UnsupportedOS,
|
||||
},
|
||||
};
|
||||
|
||||
export const UserAddedToGroup = Template.bind({});
|
||||
UserAddedToGroup.args = {
|
||||
toast: {
|
||||
toastType: ToastType.UserAddedToGroup,
|
||||
parameters: {
|
||||
contact: 'Sam Mirete',
|
||||
group: 'Hike Group 🏔',
|
||||
},
|
||||
},
|
||||
};
|
||||
export const BasicUsage = Template.bind({});
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import type { LocalizerType, ReplacementValuesType } from '../types/Util';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { SECOND } from '../util/durations';
|
||||
import { Toast } from './Toast';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
import type { AnyToast } from '../types/Toast';
|
||||
import { ToastType } from '../types/Toast';
|
||||
|
||||
export type PropsType = {
|
||||
|
@ -14,10 +15,7 @@ export type PropsType = {
|
|||
openFileInFolder: (target: string) => unknown;
|
||||
OS: string;
|
||||
onUndoArchive: (conversaetionId: string) => unknown;
|
||||
toast?: {
|
||||
toastType: ToastType;
|
||||
parameters?: ReplacementValuesType;
|
||||
};
|
||||
toast?: AnyToast;
|
||||
};
|
||||
|
||||
const SHORT_TIMEOUT = 3 * SECOND;
|
||||
|
@ -40,7 +38,7 @@ export function ToastManager({
|
|||
return (
|
||||
<Toast onClose={hideToast} timeout={SHORT_TIMEOUT}>
|
||||
{i18n('icu:AddUserToAnotherGroupModal__toast--adding-user-to-group', {
|
||||
contact: toast.parameters?.contact,
|
||||
contact: toast.parameters.contact,
|
||||
})}
|
||||
</Toast>
|
||||
);
|
||||
|
@ -117,9 +115,7 @@ export function ToastManager({
|
|||
toastAction={{
|
||||
label: i18n('icu:conversationArchivedUndo'),
|
||||
onClick: () => {
|
||||
if (toast.parameters && 'conversationId' in toast.parameters) {
|
||||
onUndoArchive(String(toast.parameters.conversationId));
|
||||
}
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
@ -138,7 +134,7 @@ export function ToastManager({
|
|||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('icu:Toast--ConversationRemoved', {
|
||||
title: toast?.parameters?.title ?? '',
|
||||
title: toast.parameters.title,
|
||||
})}
|
||||
</Toast>
|
||||
);
|
||||
|
@ -212,9 +208,7 @@ export function ToastManager({
|
|||
toastAction={{
|
||||
label: i18n('icu:attachmentSavedShow'),
|
||||
onClick: () => {
|
||||
if (toast.parameters && 'fullPath' in toast.parameters) {
|
||||
openFileInFolder(String(toast.parameters.fullPath));
|
||||
}
|
||||
openFileInFolder(toast.parameters.fullPath);
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
@ -227,8 +221,8 @@ export function ToastManager({
|
|||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('icu:fileSizeWarning', {
|
||||
limit: toast.parameters?.limit,
|
||||
units: toast.parameters?.units,
|
||||
limit: toast.parameters.limit,
|
||||
units: toast.parameters.units,
|
||||
})}
|
||||
</Toast>
|
||||
);
|
||||
|
@ -330,6 +324,17 @@ export function ToastManager({
|
|||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.TooManyMessagesToDeleteForEveryone) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n(
|
||||
'icu:DeleteMessagesModal__toast--TooManyMessagesToDeleteForEveryone',
|
||||
{ count: toast.parameters.count }
|
||||
)}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.TooManyMessagesToForward) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
|
@ -364,8 +369,8 @@ export function ToastManager({
|
|||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('icu:AddUserToAnotherGroupModal__toast--user-added-to-group', {
|
||||
contact: toast.parameters?.contact,
|
||||
group: toast.parameters?.group,
|
||||
contact: toast.parameters.contact,
|
||||
group: toast.parameters.group,
|
||||
})}
|
||||
</Toast>
|
||||
);
|
||||
|
|
|
@ -97,8 +97,6 @@ const defaultMessageProps: TimelineMessagesProps = {
|
|||
conversationId: 'conversationId',
|
||||
conversationTitle: 'Conversation Title',
|
||||
conversationType: 'direct', // override
|
||||
deleteMessages: action('default--deleteMessages'),
|
||||
deleteMessageForEveryone: action('default--deleteMessageForEveryone'),
|
||||
direction: 'incoming',
|
||||
showLightboxForViewOnceMedia: action('default--showLightboxForViewOnceMedia'),
|
||||
doubleCheckMissingQuoteReference: action(
|
||||
|
@ -145,6 +143,7 @@ const defaultMessageProps: TimelineMessagesProps = {
|
|||
showExpiredOutgoingTapToViewToast: action(
|
||||
'showExpiredOutgoingTapToViewToast'
|
||||
),
|
||||
toggleDeleteMessagesModal: action('default--toggleDeleteMessagesModal'),
|
||||
toggleForwardMessagesModal: action('default--toggleForwardMessagesModal'),
|
||||
showLightbox: action('default--showLightbox'),
|
||||
startConversation: action('default--startConversation'),
|
||||
|
|
|
@ -2,11 +2,10 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import classNames from 'classnames';
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import type { ShowToastAction } from '../../state/ducks/toast';
|
||||
import { ToastType } from '../../types/Toast';
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import { ConfirmationDialog } from '../ConfirmationDialog';
|
||||
|
||||
// Keep this in sync with iOS and Android
|
||||
const MAX_FORWARD_COUNT = 30;
|
||||
|
@ -28,8 +27,6 @@ export default function SelectModeActions({
|
|||
showToast,
|
||||
i18n,
|
||||
}: SelectModeActionsProps): JSX.Element {
|
||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
||||
|
||||
const hasSelectedMessages = selectedMessageIds.length >= 1;
|
||||
const tooManyMessagesToForward =
|
||||
selectedMessageIds.length > MAX_FORWARD_COUNT;
|
||||
|
@ -38,7 +35,6 @@ export default function SelectModeActions({
|
|||
const canDelete = hasSelectedMessages;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="SelectModeActions">
|
||||
<button
|
||||
type="button"
|
||||
|
@ -62,9 +58,7 @@ export default function SelectModeActions({
|
|||
'SelectModeActions__button--disabled': !canDelete,
|
||||
})}
|
||||
disabled={!canDelete}
|
||||
onClick={() => {
|
||||
setConfirmDelete(true);
|
||||
}}
|
||||
onClick={onDeleteMessages}
|
||||
aria-label={i18n('icu:SelectModeActions--deleteSelectedMessages')}
|
||||
>
|
||||
<span
|
||||
|
@ -82,9 +76,7 @@ export default function SelectModeActions({
|
|||
if (canForward) {
|
||||
onForwardMessages();
|
||||
} else if (tooManyMessagesToForward) {
|
||||
showToast(ToastType.TooManyMessagesToForward, {
|
||||
count: MAX_FORWARD_COUNT,
|
||||
});
|
||||
showToast({ toastType: ToastType.TooManyMessagesToForward });
|
||||
}
|
||||
}}
|
||||
aria-label={i18n('icu:SelectModeActions--forwardSelectedMessages')}
|
||||
|
@ -95,31 +87,5 @@ export default function SelectModeActions({
|
|||
/>
|
||||
</button>
|
||||
</div>
|
||||
{confirmDelete && (
|
||||
<ConfirmationDialog
|
||||
actions={[
|
||||
{
|
||||
action: () => {
|
||||
onDeleteMessages();
|
||||
},
|
||||
style: 'negative',
|
||||
text: i18n('icu:ConfirmDeleteForMeModal--confirm'),
|
||||
},
|
||||
]}
|
||||
dialogName="ConfirmDeleteForMeModal"
|
||||
title={i18n('icu:ConfirmDeleteForMeModal--title', {
|
||||
count: selectedMessageIds.length,
|
||||
})}
|
||||
i18n={i18n}
|
||||
onClose={() => {
|
||||
setConfirmDelete(false);
|
||||
}}
|
||||
>
|
||||
{i18n('icu:ConfirmDeleteForMeModal--description', {
|
||||
count: selectedMessageIds.length,
|
||||
})}
|
||||
</ConfirmationDialog>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -282,8 +282,6 @@ const actions = () => ({
|
|||
setQuoteByMessageId: action('setQuoteByMessageId'),
|
||||
retryDeleteForEveryone: action('retryDeleteForEveryone'),
|
||||
retryMessageSend: action('retryMessageSend'),
|
||||
deleteMessages: action('deleteMessages'),
|
||||
deleteMessageForEveryone: action('deleteMessageForEveryone'),
|
||||
saveAttachment: action('saveAttachment'),
|
||||
pushPanelForConversation: action('pushPanelForConversation'),
|
||||
showContactDetail: action('showContactDetail'),
|
||||
|
@ -305,6 +303,7 @@ const actions = () => ({
|
|||
showExpiredOutgoingTapToViewToast: action(
|
||||
'showExpiredOutgoingTapToViewToast'
|
||||
),
|
||||
toggleDeleteMessagesModal: action('toggleDeleteMessagesModal'),
|
||||
toggleForwardMessagesModal: action('toggleForwardMessagesModal'),
|
||||
|
||||
toggleSafetyNumberModal: action('toggleSafetyNumberModal'),
|
||||
|
|
|
@ -71,8 +71,6 @@ const getDefaultProps = () => ({
|
|||
retryDeleteForEveryone: action('retryDeleteForEveryone'),
|
||||
retryMessageSend: action('retryMessageSend'),
|
||||
blockGroupLinkRequests: action('blockGroupLinkRequests'),
|
||||
deleteMessages: action('deleteMessages'),
|
||||
deleteMessageForEveryone: action('deleteMessageForEveryone'),
|
||||
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||
markAttachmentAsCorrupted: action('markAttachmentAsCorrupted'),
|
||||
messageExpanded: action('messageExpanded'),
|
||||
|
@ -82,6 +80,7 @@ const getDefaultProps = () => ({
|
|||
pushPanelForConversation: action('pushPanelForConversation'),
|
||||
showContactModal: action('showContactModal'),
|
||||
showLightbox: action('showLightbox'),
|
||||
toggleDeleteMessagesModal: action('toggleDeleteMessagesModal'),
|
||||
toggleForwardMessagesModal: action('toggleForwardMessagesModal'),
|
||||
showLightboxForViewOnceMedia: action('showLightboxForViewOnceMedia'),
|
||||
doubleCheckMissingQuoteReference: action('doubleCheckMissingQuoteReference'),
|
||||
|
|
|
@ -265,8 +265,6 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
conversationType: overrideProps.conversationType || 'direct',
|
||||
contact: overrideProps.contact,
|
||||
deletedForEveryone: overrideProps.deletedForEveryone,
|
||||
deleteMessages: action('deleteMessages'),
|
||||
deleteMessageForEveryone: action('deleteMessageForEveryone'),
|
||||
// disableMenu: overrideProps.disableMenu,
|
||||
disableScroll: overrideProps.disableScroll,
|
||||
direction: overrideProps.direction || 'incoming',
|
||||
|
@ -350,6 +348,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
showExpiredOutgoingTapToViewToast: action(
|
||||
'showExpiredOutgoingTapToViewToast'
|
||||
),
|
||||
toggleDeleteMessagesModal: action('toggleDeleteMessagesModal'),
|
||||
toggleForwardMessagesModal: action('toggleForwardMessagesModal'),
|
||||
showLightbox: action('showLightbox'),
|
||||
startConversation: action('startConversation'),
|
||||
|
|
|
@ -26,9 +26,9 @@ import type {
|
|||
import type { PushPanelForConversationActionType } from '../../state/ducks/conversations';
|
||||
import { doesMessageBodyOverflow } from './MessageBodyReadMore';
|
||||
import type { Props as ReactionPickerProps } from './ReactionPicker';
|
||||
import { ConfirmationDialog } from '../ConfirmationDialog';
|
||||
import { useToggleReactionPicker } from '../../hooks/useKeyboardShortcuts';
|
||||
import { PanelType } from '../../types/Panels';
|
||||
import type { DeleteMessagesPropsType } from '../../state/ducks/globalModals';
|
||||
|
||||
export type PropsData = {
|
||||
canDownload: boolean;
|
||||
|
@ -41,13 +41,9 @@ export type PropsData = {
|
|||
} & Omit<MessagePropsData, 'renderingContext' | 'menu'>;
|
||||
|
||||
export type PropsActions = {
|
||||
deleteMessages: (options: {
|
||||
conversationId: string;
|
||||
messageIds: ReadonlyArray<string>;
|
||||
}) => void;
|
||||
deleteMessageForEveryone: (id: string) => void;
|
||||
pushPanelForConversation: PushPanelForConversationActionType;
|
||||
toggleForwardMessagesModal: (id: Array<string>) => void;
|
||||
toggleDeleteMessagesModal: (props: DeleteMessagesPropsType) => void;
|
||||
toggleForwardMessagesModal: (messageIds: Array<string>) => void;
|
||||
reactToMessage: (
|
||||
id: string,
|
||||
{ emoji, remove }: { emoji: string; remove: boolean }
|
||||
|
@ -83,7 +79,6 @@ export function TimelineMessage(props: Props): JSX.Element {
|
|||
const {
|
||||
attachments,
|
||||
author,
|
||||
canDeleteForEveryone,
|
||||
canDownload,
|
||||
canReact,
|
||||
canReply,
|
||||
|
@ -93,8 +88,6 @@ export function TimelineMessage(props: Props): JSX.Element {
|
|||
containerElementRef,
|
||||
containerWidthBreakpoint,
|
||||
conversationId,
|
||||
deleteMessages,
|
||||
deleteMessageForEveryone,
|
||||
deletedForEveryone,
|
||||
direction,
|
||||
giftBadge,
|
||||
|
@ -116,6 +109,7 @@ export function TimelineMessage(props: Props): JSX.Element {
|
|||
setQuoteByMessageId,
|
||||
text,
|
||||
timestamp,
|
||||
toggleDeleteMessagesModal,
|
||||
toggleForwardMessagesModal,
|
||||
toggleSelectMessage,
|
||||
} = props;
|
||||
|
@ -259,9 +253,6 @@ export function TimelineMessage(props: Props): JSX.Element {
|
|||
}
|
||||
}, [canReact, toggleReactionPicker]);
|
||||
|
||||
const [hasDOEConfirmation, setHasDOEConfirmation] = useState(false);
|
||||
const [hasDeleteConfirmation, setHasDeleteConfirmation] = useState(false);
|
||||
|
||||
const toggleReactionPickerKeyboard = useToggleReactionPicker(
|
||||
handleReact || noop
|
||||
);
|
||||
|
@ -343,48 +334,6 @@ export function TimelineMessage(props: Props): JSX.Element {
|
|||
|
||||
return (
|
||||
<>
|
||||
{hasDOEConfirmation && canDeleteForEveryone && (
|
||||
<ConfirmationDialog
|
||||
actions={[
|
||||
{
|
||||
action: () => deleteMessageForEveryone(id),
|
||||
style: 'negative',
|
||||
text: i18n('icu:delete'),
|
||||
},
|
||||
]}
|
||||
dialogName="TimelineMessage/deleteMessageForEveryone"
|
||||
i18n={i18n}
|
||||
onClose={() => setHasDOEConfirmation(false)}
|
||||
>
|
||||
{i18n('icu:deleteForEveryoneWarning')}
|
||||
</ConfirmationDialog>
|
||||
)}
|
||||
{hasDeleteConfirmation && (
|
||||
<ConfirmationDialog
|
||||
actions={[
|
||||
{
|
||||
action: () =>
|
||||
deleteMessages({
|
||||
conversationId,
|
||||
messageIds: [id],
|
||||
}),
|
||||
style: 'negative',
|
||||
text: i18n('icu:ConfirmDeleteForMeModal--confirm'),
|
||||
},
|
||||
]}
|
||||
dialogName="ConfirmDeleteForMeModal"
|
||||
i18n={i18n}
|
||||
onClose={() => setHasDeleteConfirmation(false)}
|
||||
title={i18n('icu:ConfirmDeleteForMeModal--title', {
|
||||
count: 1,
|
||||
})}
|
||||
>
|
||||
{i18n('icu:ConfirmDeleteForMeModal--description', {
|
||||
count: 1,
|
||||
})}
|
||||
</ConfirmationDialog>
|
||||
)}
|
||||
|
||||
<Message
|
||||
{...props}
|
||||
renderingContext="conversation/TimelineItem"
|
||||
|
@ -413,10 +362,12 @@ export function TimelineMessage(props: Props): JSX.Element {
|
|||
onForward={
|
||||
canForward ? () => toggleForwardMessagesModal([id]) : undefined
|
||||
}
|
||||
onDeleteForMe={() => setHasDeleteConfirmation(true)}
|
||||
onDeleteForEveryone={
|
||||
canDeleteForEveryone ? () => setHasDOEConfirmation(true) : undefined
|
||||
}
|
||||
onDeleteMessage={() => {
|
||||
toggleDeleteMessagesModal({
|
||||
conversationId,
|
||||
messageIds: [id],
|
||||
});
|
||||
}}
|
||||
onMoreInfo={() =>
|
||||
pushPanelForConversation({
|
||||
type: PanelType.MessageDetails,
|
||||
|
@ -594,8 +545,7 @@ type MessageContextProps = {
|
|||
onRetryMessageSend: (() => void) | undefined;
|
||||
onRetryDeleteForEveryone: (() => void) | undefined;
|
||||
onForward: (() => void) | undefined;
|
||||
onDeleteForMe: () => void;
|
||||
onDeleteForEveryone: (() => void) | undefined;
|
||||
onDeleteMessage: () => void;
|
||||
onMoreInfo: () => void;
|
||||
onSelect: () => void;
|
||||
};
|
||||
|
@ -612,8 +562,7 @@ const MessageContextMenu = ({
|
|||
onRetryMessageSend,
|
||||
onRetryDeleteForEveryone,
|
||||
onForward,
|
||||
onDeleteForMe,
|
||||
onDeleteForEveryone,
|
||||
onDeleteMessage,
|
||||
}: MessageContextProps): JSX.Element => {
|
||||
const menu = (
|
||||
<ContextMenu id={triggerId}>
|
||||
|
@ -746,27 +695,11 @@ const MessageContextMenu = ({
|
|||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
onDeleteForMe();
|
||||
onDeleteMessage();
|
||||
}}
|
||||
>
|
||||
{i18n('icu:deleteMessage')}
|
||||
{i18n('icu:MessageContextMenu__deleteMessage')}
|
||||
</MenuItem>
|
||||
{onDeleteForEveryone && (
|
||||
<MenuItem
|
||||
attributes={{
|
||||
className:
|
||||
'module-message__context--icon module-message__context__delete-message-for-everyone',
|
||||
}}
|
||||
onClick={(event: React.MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
onDeleteForEveryone();
|
||||
}}
|
||||
>
|
||||
{i18n('icu:deleteMessageForEveryone')}
|
||||
</MenuItem>
|
||||
)}
|
||||
</ContextMenu>
|
||||
);
|
||||
|
||||
|
|
|
@ -72,7 +72,9 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
window.reduxActions.conversations.showConversation({
|
||||
conversationId: existingConversation.id,
|
||||
});
|
||||
window.reduxActions.toast.showToast(ToastType.AlreadyGroupMember);
|
||||
window.reduxActions.toast.showToast({
|
||||
toastType: ToastType.AlreadyGroupMember,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -166,7 +168,9 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
conversationId: existingConversation.id,
|
||||
});
|
||||
|
||||
window.reduxActions.toast.showToast(ToastType.AlreadyRequestedToJoin);
|
||||
window.reduxActions.toast.showToast({
|
||||
toastType: ToastType.AlreadyRequestedToJoin,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ import type {
|
|||
} from '../../types/Attachment';
|
||||
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
|
||||
import type { DraftBodyRangeMention } from '../../types/BodyRange';
|
||||
import type { ReplacementValuesType } from '../../types/Util';
|
||||
import type { LinkPreviewType } from '../../types/message/LinkPreviews';
|
||||
import type { MessageAttributesType } from '../../model-types.d';
|
||||
import type { NoopActionType } from './noop';
|
||||
|
@ -35,6 +34,7 @@ import { LinkPreviewSourceType } from '../../types/LinkPreview';
|
|||
import { completeRecording } from './audioRecorder';
|
||||
import { RecordingState } from '../../types/AudioRecorder';
|
||||
import { SHOW_TOAST } from './toast';
|
||||
import type { AnyToast } from '../../types/Toast';
|
||||
import { ToastType } from '../../types/Toast';
|
||||
import { SafetyNumberChangeSource } from '../../components/SafetyNumberChangeDialog';
|
||||
import { UUID } from '../../types/UUID';
|
||||
|
@ -436,13 +436,11 @@ function sendMultiMediaMessage(
|
|||
|
||||
conversation.clearTypingTimers();
|
||||
|
||||
const toastType = shouldShowInvalidMessageToast(conversation.attributes);
|
||||
if (toastType) {
|
||||
const toast = shouldShowInvalidMessageToast(conversation.attributes);
|
||||
if (toast != null) {
|
||||
dispatch({
|
||||
type: SHOW_TOAST,
|
||||
payload: {
|
||||
toastType,
|
||||
},
|
||||
payload: toast,
|
||||
});
|
||||
dispatch(setComposerDisabledState(conversationId, false));
|
||||
return;
|
||||
|
@ -561,13 +559,11 @@ function sendStickerMessage(
|
|||
return;
|
||||
}
|
||||
|
||||
const toastType = shouldShowInvalidMessageToast(conversation.attributes);
|
||||
if (toastType) {
|
||||
const toast = shouldShowInvalidMessageToast(conversation.attributes);
|
||||
if (toast != null) {
|
||||
dispatch({
|
||||
type: SHOW_TOAST,
|
||||
payload: {
|
||||
toastType,
|
||||
},
|
||||
payload: toast,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -906,9 +902,7 @@ function processAttachments({
|
|||
return;
|
||||
}
|
||||
|
||||
let toastToShow:
|
||||
| { toastType: ToastType; parameters?: ReplacementValuesType }
|
||||
| undefined;
|
||||
let toastToShow: AnyToast | undefined;
|
||||
|
||||
const nextDraftAttachments = (
|
||||
conversation.get('draftAttachments') || []
|
||||
|
@ -986,7 +980,7 @@ function processAttachments({
|
|||
function preProcessAttachment(
|
||||
file: File,
|
||||
draftAttachments: Array<AttachmentDraftType>
|
||||
): { toastType: ToastType; parameters?: ReplacementValuesType } | undefined {
|
||||
): AnyToast | undefined {
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1001,7 +1001,7 @@ export const actions = {
|
|||
deleteAvatarFromDisk,
|
||||
deleteConversation,
|
||||
deleteMessages,
|
||||
deleteMessageForEveryone,
|
||||
deleteMessagesForEveryone,
|
||||
destroyMessages,
|
||||
discardMessages,
|
||||
doubleCheckMissingQuoteReference,
|
||||
|
@ -2857,8 +2857,8 @@ function popPanelForConversation(): ThunkAction<
|
|||
};
|
||||
}
|
||||
|
||||
function deleteMessageForEveryone(
|
||||
messageId: string
|
||||
function deleteMessagesForEveryone(
|
||||
messageIds: ReadonlyArray<string>
|
||||
): ThunkAction<
|
||||
void,
|
||||
RootStateType,
|
||||
|
@ -2866,6 +2866,11 @@ function deleteMessageForEveryone(
|
|||
NoopActionType | ShowToastActionType
|
||||
> {
|
||||
return async dispatch => {
|
||||
let hasError = false;
|
||||
|
||||
await Promise.all(
|
||||
messageIds.map(async messageId => {
|
||||
try {
|
||||
const message = window.MessageController.getById(messageId);
|
||||
if (!message) {
|
||||
throw new Error(
|
||||
|
@ -2878,27 +2883,33 @@ function deleteMessageForEveryone(
|
|||
throw new Error('deleteMessageForEveryone: no conversation');
|
||||
}
|
||||
|
||||
try {
|
||||
await sendDeleteForEveryoneMessage(conversation.attributes, {
|
||||
id: message.id,
|
||||
timestamp: message.get('sent_at'),
|
||||
});
|
||||
dispatch({
|
||||
type: 'NOOP',
|
||||
payload: null,
|
||||
});
|
||||
} catch (error) {
|
||||
hasError = true;
|
||||
log.error(
|
||||
'Error sending delete-for-everyone',
|
||||
'Error queuing delete-for-everyone job',
|
||||
Errors.toLogFormat(error),
|
||||
messageId
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
if (hasError) {
|
||||
dispatch({
|
||||
type: SHOW_TOAST,
|
||||
payload: {
|
||||
toastType: ToastType.DeleteForEveryoneFailed,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: 'NOOP',
|
||||
payload: null,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -45,8 +45,10 @@ import type { ShowToastActionType } from './toast';
|
|||
export type EditHistoryMessagesType = ReadonlyDeep<
|
||||
Array<MessageAttributesType>
|
||||
>;
|
||||
export type ConfirmDeleteForMeModalProps = ReadonlyDeep<{
|
||||
count: number;
|
||||
export type DeleteMessagesPropsType = ReadonlyDeep<{
|
||||
conversationId: string;
|
||||
messageIds: ReadonlyArray<string>;
|
||||
onDelete?: () => void;
|
||||
}>;
|
||||
export type ForwardMessagePropsType = ReadonlyDeep<MessagePropsType>;
|
||||
export type ForwardMessagesPropsType = ReadonlyDeep<{
|
||||
|
@ -76,6 +78,7 @@ export type GlobalModalsStateType = ReadonlyDeep<{
|
|||
description?: string;
|
||||
title?: string;
|
||||
};
|
||||
deleteMessagesProps?: DeleteMessagesPropsType;
|
||||
forwardMessagesProps?: ForwardMessagesPropsType;
|
||||
gv2MigrationProps?: MigrateToGV2PropsType;
|
||||
hasConfirmationModal: boolean;
|
||||
|
@ -103,6 +106,8 @@ const HIDE_UUID_NOT_FOUND_MODAL = 'globalModals/HIDE_UUID_NOT_FOUND_MODAL';
|
|||
const SHOW_UUID_NOT_FOUND_MODAL = 'globalModals/SHOW_UUID_NOT_FOUND_MODAL';
|
||||
const SHOW_STORIES_SETTINGS = 'globalModals/SHOW_STORIES_SETTINGS';
|
||||
const HIDE_STORIES_SETTINGS = 'globalModals/HIDE_STORIES_SETTINGS';
|
||||
const TOGGLE_DELETE_MESSAGES_MODAL =
|
||||
'globalModals/TOGGLE_DELETE_MESSAGES_MODAL';
|
||||
const TOGGLE_FORWARD_MESSAGES_MODAL =
|
||||
'globalModals/TOGGLE_FORWARD_MESSAGES_MODAL';
|
||||
const TOGGLE_PROFILE_EDITOR = 'globalModals/TOGGLE_PROFILE_EDITOR';
|
||||
|
@ -175,6 +180,11 @@ export type ShowUserNotFoundModalActionType = ReadonlyDeep<{
|
|||
payload: UserNotFoundModalStateType;
|
||||
}>;
|
||||
|
||||
type ToggleDeleteMessagesModalActionType = ReadonlyDeep<{
|
||||
type: typeof TOGGLE_DELETE_MESSAGES_MODAL;
|
||||
payload: DeleteMessagesPropsType | undefined;
|
||||
}>;
|
||||
|
||||
type ToggleForwardMessagesModalActionType = ReadonlyDeep<{
|
||||
type: typeof TOGGLE_FORWARD_MESSAGES_MODAL;
|
||||
payload: ForwardMessagesPropsType | undefined;
|
||||
|
@ -321,6 +331,7 @@ export type GlobalModalsActionType = ReadonlyDeep<
|
|||
| ShowWhatsNewModalActionType
|
||||
| StartMigrationToGV2ActionType
|
||||
| ToggleAddUserToAnotherGroupModalActionType
|
||||
| ToggleDeleteMessagesModalActionType
|
||||
| ToggleForwardMessagesModalActionType
|
||||
| ToggleProfileEditorActionType
|
||||
| ToggleProfileEditorErrorActionType
|
||||
|
@ -357,6 +368,7 @@ export const actions = {
|
|||
showWhatsNewModal,
|
||||
toggleAddUserToAnotherGroupModal,
|
||||
toggleConfirmationModal,
|
||||
toggleDeleteMessagesModal,
|
||||
toggleForwardMessagesModal,
|
||||
toggleProfileEditor,
|
||||
toggleProfileEditorHasError,
|
||||
|
@ -473,6 +485,15 @@ function closeGV2MigrationDialog(): CloseGV2MigrationDialogActionType {
|
|||
};
|
||||
}
|
||||
|
||||
function toggleDeleteMessagesModal(
|
||||
props: DeleteMessagesPropsType | undefined
|
||||
): ToggleDeleteMessagesModalActionType {
|
||||
return {
|
||||
type: TOGGLE_DELETE_MESSAGES_MODAL,
|
||||
payload: props,
|
||||
};
|
||||
}
|
||||
|
||||
function toggleForwardMessagesModal(
|
||||
messageIds?: ReadonlyArray<string>,
|
||||
onForward?: () => void
|
||||
|
@ -855,6 +876,13 @@ export function reducer(
|
|||
};
|
||||
}
|
||||
|
||||
if (action.type === TOGGLE_DELETE_MESSAGES_MODAL) {
|
||||
return {
|
||||
...state,
|
||||
deleteMessagesProps: action.payload,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === TOGGLE_FORWARD_MESSAGES_MODAL) {
|
||||
return {
|
||||
...state,
|
||||
|
|
|
@ -6,18 +6,14 @@ import { ipcRenderer } from 'electron';
|
|||
import type { ReadonlyDeep } from 'type-fest';
|
||||
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
|
||||
import type { NoopActionType } from './noop';
|
||||
import type { ReplacementValuesType } from '../../types/Util';
|
||||
import { useBoundActions } from '../../hooks/useBoundActions';
|
||||
import type { ToastType } from '../../types/Toast';
|
||||
import type { AnyToast } from '../../types/Toast';
|
||||
|
||||
// State
|
||||
|
||||
// eslint-disable-next-line local-rules/type-alias-readonlydeep
|
||||
export type ToastStateType = {
|
||||
toast?: {
|
||||
toastType: ToastType;
|
||||
parameters?: ReplacementValuesType;
|
||||
};
|
||||
toast?: AnyToast;
|
||||
};
|
||||
|
||||
// Actions
|
||||
|
@ -32,10 +28,7 @@ type HideToastActionType = ReadonlyDeep<{
|
|||
// eslint-disable-next-line local-rules/type-alias-readonlydeep
|
||||
export type ShowToastActionType = {
|
||||
type: typeof SHOW_TOAST;
|
||||
payload: {
|
||||
toastType: ToastType;
|
||||
parameters?: ReplacementValuesType;
|
||||
};
|
||||
payload: AnyToast;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line local-rules/type-alias-readonlydeep
|
||||
|
@ -57,29 +50,14 @@ function openFileInFolder(target: string): NoopActionType {
|
|||
};
|
||||
}
|
||||
|
||||
export type ShowToastActionCreatorType = ReadonlyDeep<
|
||||
(
|
||||
toastType: ToastType,
|
||||
parameters?: ReplacementValuesType
|
||||
) => ShowToastActionType
|
||||
>;
|
||||
export type ShowToastAction = ReadonlyDeep<(toast: AnyToast) => void>;
|
||||
|
||||
export type ShowToastAction = ReadonlyDeep<
|
||||
(toastType: ToastType, parameters?: ReplacementValuesType) => void
|
||||
>;
|
||||
|
||||
export const showToast: ShowToastActionCreatorType = (
|
||||
toastType,
|
||||
parameters
|
||||
) => {
|
||||
export function showToast(toast: AnyToast): ShowToastActionType {
|
||||
return {
|
||||
type: SHOW_TOAST,
|
||||
payload: {
|
||||
toastType,
|
||||
parameters,
|
||||
},
|
||||
};
|
||||
payload: toast,
|
||||
};
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
hideToast,
|
||||
|
|
|
@ -244,7 +244,7 @@ export function deleteUsername({
|
|||
try {
|
||||
await doDeleteUsername(username);
|
||||
} catch {
|
||||
dispatch(showToast(ToastType.FailedToDeleteUsername));
|
||||
dispatch(showToast({ toastType: ToastType.FailedToDeleteUsername }));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ import {
|
|||
getSelectedMessageIds,
|
||||
getTargetedMessage,
|
||||
isMissingRequiredProfileSharing,
|
||||
getMessages,
|
||||
} from './conversations';
|
||||
import {
|
||||
getIntl,
|
||||
|
@ -1799,6 +1800,16 @@ export function canDeleteForEveryone(
|
|||
);
|
||||
}
|
||||
|
||||
export const canDeleteMessagesForEveryone = createSelector(
|
||||
[getMessages, (_state, messageIds: ReadonlyArray<string>) => messageIds],
|
||||
(messagesLookup, messageIds) => {
|
||||
return messageIds.every(messageId => {
|
||||
const message = getOwn(messagesLookup, messageId);
|
||||
return message != null && canDeleteForEveryone(message);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
export function canRetryDeleteForEveryone(
|
||||
message: Pick<
|
||||
MessageWithUIFieldsType,
|
||||
|
|
|
@ -19,7 +19,6 @@ import { getEmojiSkinTone } from '../selectors/items';
|
|||
import {
|
||||
getConversationSelector,
|
||||
getGroupAdminsSelector,
|
||||
getLastSelectedMessage,
|
||||
getSelectedMessageIds,
|
||||
isMissingRequiredProfileSharing,
|
||||
} from '../selectors/conversations';
|
||||
|
@ -93,7 +92,6 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
|||
const recentEmojis = selectRecentEmojis(state);
|
||||
|
||||
const selectedMessageIds = getSelectedMessageIds(state);
|
||||
const lastSelectedMessage = getLastSelectedMessage(state);
|
||||
|
||||
return {
|
||||
// Base
|
||||
|
@ -170,7 +168,6 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
|||
|
||||
// Select Mode
|
||||
selectedMessageIds,
|
||||
lastSelectedMessage,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ export function SmartConversationView(): JSX.Element {
|
|||
const hasOpenModal = useSelector((state: StateType) => {
|
||||
return (
|
||||
state.globalModals.forwardMessagesProps != null ||
|
||||
state.globalModals.deleteMessagesProps != null ||
|
||||
state.globalModals.hasConfirmationModal
|
||||
);
|
||||
});
|
||||
|
|
61
ts/state/smart/DeleteMessagesModal.tsx
Normal file
61
ts/state/smart/DeleteMessagesModal.tsx
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import type { DeleteMessagesPropsType } from '../ducks/globalModals';
|
||||
import type { StateType } from '../reducer';
|
||||
import { getIntl } from '../selectors/user';
|
||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||
import DeleteMessagesModal from '../../components/DeleteMessagesModal';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
import { canDeleteMessagesForEveryone } from '../selectors/message';
|
||||
import { useConversationsActions } from '../ducks/conversations';
|
||||
import { useToastActions } from '../ducks/toast';
|
||||
|
||||
export function SmartDeleteMessagesModal(): JSX.Element | null {
|
||||
const deleteMessagesProps = useSelector<
|
||||
StateType,
|
||||
DeleteMessagesPropsType | undefined
|
||||
>(state => state.globalModals.deleteMessagesProps);
|
||||
strictAssert(
|
||||
deleteMessagesProps != null,
|
||||
'Cannot render delete messages modal without messages'
|
||||
);
|
||||
const { conversationId, messageIds, onDelete } = deleteMessagesProps;
|
||||
const canDeleteForEveryone = useSelector((state: StateType) => {
|
||||
return canDeleteMessagesForEveryone(state, messageIds);
|
||||
});
|
||||
const lastSelectedMessage = useSelector((state: StateType) => {
|
||||
return state.conversations.lastSelectedMessage;
|
||||
});
|
||||
const i18n = useSelector(getIntl);
|
||||
const { toggleDeleteMessagesModal } = useGlobalModalActions();
|
||||
const { deleteMessages, deleteMessagesForEveryone } =
|
||||
useConversationsActions();
|
||||
const { showToast } = useToastActions();
|
||||
|
||||
return (
|
||||
<DeleteMessagesModal
|
||||
canDeleteForEveryone={canDeleteForEveryone}
|
||||
i18n={i18n}
|
||||
messageCount={deleteMessagesProps.messageIds.length}
|
||||
onClose={() => {
|
||||
toggleDeleteMessagesModal(undefined);
|
||||
}}
|
||||
onDeleteForMe={() => {
|
||||
deleteMessages({
|
||||
conversationId,
|
||||
messageIds,
|
||||
lastSelectedMessage,
|
||||
});
|
||||
onDelete?.();
|
||||
}}
|
||||
onDeleteForEveryone={() => {
|
||||
deleteMessagesForEveryone(messageIds);
|
||||
onDelete?.();
|
||||
}}
|
||||
showToast={showToast}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -21,6 +21,7 @@ import { SmartStoriesSettingsModal } from './StoriesSettingsModal';
|
|||
import { getConversationsStoppingSend } from '../selectors/conversations';
|
||||
import { getIntl, getTheme } from '../selectors/user';
|
||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||
import { SmartDeleteMessagesModal } from './DeleteMessagesModal';
|
||||
|
||||
function renderEditHistoryMessagesModal(): JSX.Element {
|
||||
return <SmartEditHistoryMessagesModal />;
|
||||
|
@ -34,6 +35,10 @@ function renderContactModal(): JSX.Element {
|
|||
return <SmartContactModal />;
|
||||
}
|
||||
|
||||
function renderDeleteMessagesModal(): JSX.Element {
|
||||
return <SmartDeleteMessagesModal />;
|
||||
}
|
||||
|
||||
function renderForwardMessagesModal(): JSX.Element {
|
||||
return <SmartForwardMessagesModal />;
|
||||
}
|
||||
|
@ -62,6 +67,7 @@ export function SmartGlobalModalContainer(): JSX.Element {
|
|||
contactModalState,
|
||||
editHistoryMessages,
|
||||
errorModalProps,
|
||||
deleteMessagesProps,
|
||||
forwardMessagesProps,
|
||||
isProfileEditorVisible,
|
||||
isShortcutGuideModalVisible,
|
||||
|
@ -128,6 +134,7 @@ export function SmartGlobalModalContainer(): JSX.Element {
|
|||
contactModalState={contactModalState}
|
||||
editHistoryMessages={editHistoryMessages}
|
||||
errorModalProps={errorModalProps}
|
||||
deleteMessagesProps={deleteMessagesProps}
|
||||
forwardMessagesProps={forwardMessagesProps}
|
||||
hasSafetyNumberChangeModal={hasSafetyNumberChangeModal}
|
||||
hideUserNotFoundModal={hideUserNotFoundModal}
|
||||
|
@ -142,6 +149,7 @@ export function SmartGlobalModalContainer(): JSX.Element {
|
|||
renderContactModal={renderContactModal}
|
||||
renderEditHistoryMessagesModal={renderEditHistoryMessagesModal}
|
||||
renderErrorModal={renderErrorModal}
|
||||
renderDeleteMessagesModal={renderDeleteMessagesModal}
|
||||
renderForwardMessagesModal={renderForwardMessagesModal}
|
||||
renderProfileEditor={renderProfileEditor}
|
||||
renderSafetyNumber={renderSafetyNumber}
|
||||
|
|
|
@ -139,7 +139,9 @@ export function SmartStoryViewer(): JSX.Element | null {
|
|||
);
|
||||
}}
|
||||
onSetSkinTone={onSetSkinTone}
|
||||
onTextTooLong={() => showToast(ToastType.MessageBodyTooLong)}
|
||||
onTextTooLong={() => {
|
||||
showToast({ toastType: ToastType.MessageBodyTooLong });
|
||||
}}
|
||||
onUseEmoji={onUseEmoji}
|
||||
onMediaPlaybackStart={pauseVoiceNotePlayer}
|
||||
preferredReactionEmoji={preferredReactionEmoji}
|
||||
|
|
|
@ -112,8 +112,6 @@ export function SmartTimelineItem(props: ExternalProps): JSX.Element {
|
|||
const {
|
||||
blockGroupLinkRequests,
|
||||
clearTargetedMessage: clearSelectedMessage,
|
||||
deleteMessages,
|
||||
deleteMessageForEveryone,
|
||||
doubleCheckMissingQuoteReference,
|
||||
kickOffAttachmentDownload,
|
||||
markAttachmentAsCorrupted,
|
||||
|
@ -138,6 +136,7 @@ export function SmartTimelineItem(props: ExternalProps): JSX.Element {
|
|||
const {
|
||||
showContactModal,
|
||||
showEditHistoryModal,
|
||||
toggleDeleteMessagesModal,
|
||||
toggleForwardMessagesModal,
|
||||
toggleSafetyNumberModal,
|
||||
} = useGlobalModalActions();
|
||||
|
@ -177,8 +176,6 @@ export function SmartTimelineItem(props: ExternalProps): JSX.Element {
|
|||
blockGroupLinkRequests={blockGroupLinkRequests}
|
||||
checkForAccount={checkForAccount}
|
||||
clearTargetedMessage={clearSelectedMessage}
|
||||
deleteMessages={deleteMessages}
|
||||
deleteMessageForEveryone={deleteMessageForEveryone}
|
||||
doubleCheckMissingQuoteReference={doubleCheckMissingQuoteReference}
|
||||
kickOffAttachmentDownload={kickOffAttachmentDownload}
|
||||
markAttachmentAsCorrupted={markAttachmentAsCorrupted}
|
||||
|
@ -202,6 +199,7 @@ export function SmartTimelineItem(props: ExternalProps): JSX.Element {
|
|||
showSpoiler={showSpoiler}
|
||||
startCallingLobby={startCallingLobby}
|
||||
startConversation={startConversation}
|
||||
toggleDeleteMessagesModal={toggleDeleteMessagesModal}
|
||||
toggleForwardMessagesModal={toggleForwardMessagesModal}
|
||||
toggleSafetyNumberModal={toggleSafetyNumberModal}
|
||||
viewStory={viewStory}
|
||||
|
|
|
@ -441,7 +441,6 @@ describe('electron/state/ducks/username', () => {
|
|||
type: 'toast/SHOW_TOAST',
|
||||
payload: {
|
||||
toastType: ToastType.FailedToDeleteUsername,
|
||||
parameters: undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -40,9 +40,68 @@ export enum ToastType {
|
|||
StoryVideoUnsupported = 'StoryVideoUnsupported',
|
||||
TapToViewExpiredIncoming = 'TapToViewExpiredIncoming',
|
||||
TapToViewExpiredOutgoing = 'TapToViewExpiredOutgoing',
|
||||
TooManyMessagesToDeleteForEveryone = 'TooManyMessagesToDeleteForEveryone',
|
||||
TooManyMessagesToForward = 'TooManyMessagesToForward',
|
||||
UnableToLoadAttachment = 'UnableToLoadAttachment',
|
||||
UnsupportedMultiAttachment = 'UnsupportedMultiAttachment',
|
||||
UnsupportedOS = 'UnsupportedOS',
|
||||
UserAddedToGroup = 'UserAddedToGroup',
|
||||
}
|
||||
|
||||
export type AnyToast =
|
||||
| { toastType: ToastType.AddingUserToGroup; parameters: { contact: string } }
|
||||
| { toastType: ToastType.AlreadyGroupMember }
|
||||
| { toastType: ToastType.AlreadyRequestedToJoin }
|
||||
| { toastType: ToastType.Blocked }
|
||||
| { toastType: ToastType.BlockedGroup }
|
||||
| { toastType: ToastType.CannotForwardEmptyMessage }
|
||||
| { toastType: ToastType.CannotMixMultiAndNonMultiAttachments }
|
||||
| { toastType: ToastType.CannotOpenGiftBadgeIncoming }
|
||||
| { toastType: ToastType.CannotOpenGiftBadgeOutgoing }
|
||||
| { toastType: ToastType.CannotStartGroupCall }
|
||||
| {
|
||||
toastType: ToastType.ConversationArchived;
|
||||
parameters: { conversationId: string };
|
||||
}
|
||||
| { toastType: ToastType.ConversationMarkedUnread }
|
||||
| { toastType: ToastType.ConversationRemoved; parameters: { title: string } }
|
||||
| { toastType: ToastType.ConversationUnarchived }
|
||||
| { toastType: ToastType.CopiedUsername }
|
||||
| { toastType: ToastType.CopiedUsernameLink }
|
||||
| { toastType: ToastType.DangerousFileType }
|
||||
| { toastType: ToastType.DeleteForEveryoneFailed }
|
||||
| { toastType: ToastType.Error }
|
||||
| { toastType: ToastType.Expired }
|
||||
| { toastType: ToastType.FailedToDeleteUsername }
|
||||
| { toastType: ToastType.FileSaved; parameters: { fullPath: string } }
|
||||
| {
|
||||
toastType: ToastType.FileSize;
|
||||
parameters: { limit: number; units: string };
|
||||
}
|
||||
| { toastType: ToastType.InvalidConversation }
|
||||
| { toastType: ToastType.LeftGroup }
|
||||
| { toastType: ToastType.MaxAttachments }
|
||||
| { toastType: ToastType.MessageBodyTooLong }
|
||||
| { toastType: ToastType.OriginalMessageNotFound }
|
||||
| { toastType: ToastType.PinnedConversationsFull }
|
||||
| { toastType: ToastType.ReactionFailed }
|
||||
| { toastType: ToastType.ReportedSpamAndBlocked }
|
||||
| { toastType: ToastType.StoryMuted }
|
||||
| { toastType: ToastType.StoryReact }
|
||||
| { toastType: ToastType.StoryReply }
|
||||
| { toastType: ToastType.StoryVideoError }
|
||||
| { toastType: ToastType.StoryVideoUnsupported }
|
||||
| { toastType: ToastType.TapToViewExpiredIncoming }
|
||||
| { toastType: ToastType.TapToViewExpiredOutgoing }
|
||||
| {
|
||||
toastType: ToastType.TooManyMessagesToDeleteForEveryone;
|
||||
parameters: { count: number };
|
||||
}
|
||||
| { toastType: ToastType.TooManyMessagesToForward }
|
||||
| { toastType: ToastType.UnableToLoadAttachment }
|
||||
| { toastType: ToastType.UnsupportedMultiAttachment }
|
||||
| { toastType: ToastType.UnsupportedOS }
|
||||
| {
|
||||
toastType: ToastType.UserAddedToGroup;
|
||||
parameters: { contact: string; group: string };
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@ import type { ConversationAttributesType } from '../model-types';
|
|||
import { hasExpired } from '../state/selectors/expiration';
|
||||
import { isOSUnsupported } from '../state/selectors/updates';
|
||||
|
||||
import type { AnyToast } from '../types/Toast';
|
||||
import { ToastType } from '../types/Toast';
|
||||
import {
|
||||
isDirectConversation,
|
||||
|
@ -17,13 +18,13 @@ const MAX_MESSAGE_BODY_LENGTH = 64 * 1024;
|
|||
export function shouldShowInvalidMessageToast(
|
||||
conversationAttributes: ConversationAttributesType,
|
||||
messageText?: string
|
||||
): ToastType | undefined {
|
||||
): AnyToast | undefined {
|
||||
const state = window.reduxStore.getState();
|
||||
if (hasExpired(state)) {
|
||||
if (isOSUnsupported(state)) {
|
||||
return ToastType.UnsupportedOS;
|
||||
return { toastType: ToastType.UnsupportedOS };
|
||||
}
|
||||
return ToastType.Expired;
|
||||
return { toastType: ToastType.Expired };
|
||||
}
|
||||
|
||||
const isValid =
|
||||
|
@ -32,7 +33,7 @@ export function shouldShowInvalidMessageToast(
|
|||
isGroupV2(conversationAttributes);
|
||||
|
||||
if (!isValid) {
|
||||
return ToastType.InvalidConversation;
|
||||
return { toastType: ToastType.InvalidConversation };
|
||||
}
|
||||
|
||||
const { e164, uuid } = conversationAttributes;
|
||||
|
@ -41,7 +42,7 @@ export function shouldShowInvalidMessageToast(
|
|||
((e164 && window.storage.blocked.isBlocked(e164)) ||
|
||||
(uuid && window.storage.blocked.isUuidBlocked(uuid)))
|
||||
) {
|
||||
return ToastType.Blocked;
|
||||
return { toastType: ToastType.Blocked };
|
||||
}
|
||||
|
||||
const { groupId } = conversationAttributes;
|
||||
|
@ -50,18 +51,18 @@ export function shouldShowInvalidMessageToast(
|
|||
groupId &&
|
||||
window.storage.blocked.isGroupBlocked(groupId)
|
||||
) {
|
||||
return ToastType.BlockedGroup;
|
||||
return { toastType: ToastType.BlockedGroup };
|
||||
}
|
||||
|
||||
if (
|
||||
!isDirectConversation(conversationAttributes) &&
|
||||
conversationAttributes.left
|
||||
) {
|
||||
return ToastType.LeftGroup;
|
||||
return { toastType: ToastType.LeftGroup };
|
||||
}
|
||||
|
||||
if (messageText && messageText.length > MAX_MESSAGE_BODY_LENGTH) {
|
||||
return ToastType.MessageBodyTooLong;
|
||||
return { toastType: ToastType.MessageBodyTooLong };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
|
Loading…
Reference in a new issue