Removes ToastView, new React toast

This commit is contained in:
Josh Perez 2021-09-22 16:59:54 -04:00 committed by GitHub
parent 024a3521e1
commit e6d952d105
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
89 changed files with 1854 additions and 676 deletions

View File

@ -76,15 +76,12 @@
</div>
</div>
</div>
<div id="toast"></div>
</div>
<div class='lightbox-container'></div>
</div>
</script>
<script type="text/x-tmpl-mustache" id="toast">
{{ toastMessage }}
</script>
<script type="text/x-tmpl-mustache" id="conversation">
<div class='conversation-header'></div>
<div class='main panel'>
@ -114,11 +111,6 @@
</div>
</script>
<script type="text/x-tmpl-mustache" id="file-size-modal">
{{ file-size-warning }}
({{ limit }}{{ units }})
</script>
<script type="text/x-tmpl-mustache" id="group-member-list">
<div class='container' tabindex='0'>
{{ #summary }} <div class='summary'>{{ summary }}</div>{{ /summary }}

View File

@ -33,7 +33,6 @@ window.getEnvironment = getEnvironment;
window.Backbone = require('backbone');
require('./ts/backbone/views/whisper_view');
require('./ts/backbone/views/toast_view');
require('./ts/logging/set_up_renderer_logging').initialize();
require('./ts/views/debug_log_view');

View File

@ -464,7 +464,6 @@ try {
require('./ts/models/conversations');
require('./ts/backbone/views/whisper_view');
require('./ts/backbone/views/toast_view');
require('./ts/views/conversation_view');
require('./ts/views/inbox_view');
require('./ts/views/install_view');

View File

@ -209,41 +209,6 @@
}
}
.toast {
position: absolute;
left: 50%;
transform: translate(-50%, 0);
bottom: 62px;
text-align: center;
padding-left: 16px;
padding-right: 16px;
padding-top: 8px;
padding-bottom: 8px;
border-radius: 4px;
z-index: 100;
font-size: 13px;
line-height: 18px;
letter-spacing: 0;
@include light-theme {
background-color: $color-gray-75;
color: $color-white;
box-shadow: 0 4px 16px 0 $color-black-alpha-20,
0 0 0 0.5px $color-black-alpha-05;
}
@include dark-theme {
background-color: $color-gray-45;
color: $color-white;
box-shadow: 0 4px 16px 0 $color-white-alpha-20;
}
}
.toast-clickable {
cursor: pointer;
}
.permissions-popup,
.debug-log-window {
.modal {

View File

@ -0,0 +1,31 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
.Toast {
@include font-body-2;
border-radius: 4px;
bottom: 62px;
left: 50%;
padding: 8px 16px;
position: absolute;
text-align: center;
transform: translate(-50%, 0);
z-index: 100;
@include light-theme {
background-color: $color-gray-75;
color: $color-white;
box-shadow: 0 4px 16px 0 $color-black-alpha-20,
0 0 0 0.5px $color-black-alpha-05;
}
@include dark-theme {
background-color: $color-gray-45;
color: $color-white;
box-shadow: 0 4px 16px 0 $color-white-alpha-20;
}
&--clickable {
cursor: pointer;
}
}

View File

@ -83,6 +83,7 @@
@import './components/Slider.scss';
@import './components/SystemMessage.scss';
@import './components/Tabs.scss';
@import './components/Toast.scss';
@import './components/TimelineWarning.scss';
@import './components/TimelineWarnings.scss';
@import './components/WhatsNew.scss';

View File

@ -50,10 +50,6 @@
</div>
</script>
<script type="text/x-tmpl-mustache" id="toast">
{{ toastMessage }}
</script>
<script type="text/x-tmpl-mustache" id="conversation">
<div class='conversation-header'></div>
<div class='main panel'>

View File

@ -1,35 +0,0 @@
// Copyright 2015-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
window.Whisper = window.Whisper || {};
window.Whisper.ToastView = window.Whisper.View.extend({
className: 'toast',
template: () => $('#toast').html(),
initialize() {
this.$el.hide();
this.timeout = 2000;
},
close() {
this.$el.fadeOut(this.remove.bind(this));
},
render() {
this.$el.html(
window.Mustache.render(
window._.result(this, 'template', ''),
window._.result(this, 'render_attributes', '')
)
);
this.$el.attr('tabIndex', 0);
this.$el.show();
setTimeout(this.close.bind(this), this.timeout);
},
});
window.Whisper.ToastView.show = (View, el) => {
const toast = new View();
toast.$el.appendTo(el);
toast.render();
};

View File

@ -104,6 +104,11 @@ import {
getEmojiReducerState,
} from './util/loadRecentEmojis';
import { deleteAllLogs } from './util/deleteAllLogs';
import { ToastCaptchaFailed } from './components/ToastCaptchaFailed';
import { ToastCaptchaSolved } from './components/ToastCaptchaSolved';
import { ToastConversationArchived } from './components/ToastConversationArchived';
import { ToastConversationUnarchived } from './components/ToastConversationUnarchived';
import { showToast } from './util/showToast';
const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;
@ -1405,10 +1410,7 @@ export async function startApp(): Promise<void> {
) {
conversation.setArchived(true);
conversation.trigger('unload', 'keyboard shortcut archive');
window.Whisper.ToastView.show(
window.Whisper.ConversationArchivedToast,
document.body
);
showToast(ToastConversationArchived);
// It's very likely that the act of archiving a conversation will set focus to
// 'none,' or the top-level body element. This resets it to the left pane.
@ -1433,10 +1435,7 @@ export async function startApp(): Promise<void> {
(key === 'u' || key === 'U')
) {
conversation.setArchived(false);
window.Whisper.ToastView.show(
window.Whisper.ConversationUnarchivedToast,
document.body
);
showToast(ToastConversationUnarchived);
event.preventDefault();
event.stopPropagation();
@ -1660,19 +1659,11 @@ export async function startApp(): Promise<void> {
onChallengeFailed() {
// TODO: DESKTOP-1530
// Display humanized `retryAfter`
window.Whisper.ToastView.show(
window.Whisper.CaptchaFailedToast,
document.getElementsByClassName('conversation-stack')[0] ||
document.body
);
showToast(ToastCaptchaFailed);
},
onChallengeSolved() {
window.Whisper.ToastView.show(
window.Whisper.CaptchaSolvedToast,
document.getElementsByClassName('conversation-stack')[0] ||
document.body
);
showToast(ToastCaptchaSolved);
},
setChallengeStatus(challengeStatus) {

83
ts/components/Toast.tsx Normal file
View File

@ -0,0 +1,83 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { KeyboardEvent, ReactNode, useEffect } from 'react';
import classNames from 'classnames';
import { createPortal } from 'react-dom';
import { onTimeout, removeTimeout } from '../services/timers';
export type PropsType = {
autoDismissDisabled?: boolean;
children: ReactNode;
className?: string;
onClick?: () => unknown;
onClose: () => unknown;
timeout?: number;
};
export const Toast = ({
autoDismissDisabled = false,
children,
className,
onClick,
onClose,
timeout = 2000,
}: PropsType): JSX.Element | null => {
const [root, setRoot] = React.useState<HTMLElement | null>(null);
useEffect(() => {
const div = document.createElement('div');
document.body.appendChild(div);
setRoot(div);
return () => {
document.body.removeChild(div);
setRoot(null);
};
}, []);
useEffect(() => {
if (!root || autoDismissDisabled) {
return;
}
const timeoutId = onTimeout(Date.now() + timeout, onClose);
return () => {
if (timeoutId) {
removeTimeout(timeoutId);
}
};
}, [autoDismissDisabled, onClose, root, timeout]);
let interactivityProps = {};
if (onClick) {
interactivityProps = {
role: 'button',
onClick() {
onClick();
},
onKeyDown(ev: KeyboardEvent<HTMLDivElement>) {
if (ev.key === 'Enter' || ev.key === ' ') {
onClick();
}
},
};
}
return root
? createPortal(
<div
className={classNames(
'Toast',
onClick ? 'Toast--clickable' : null,
className
)}
{...interactivityProps}
>
{children}
</div>,
root
)
: null;
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastAlreadyGroupMember } from './ToastAlreadyGroupMember';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastAlreadyGroupMember', module);
story.add('ToastAlreadyGroupMember', () => (
<ToastAlreadyGroupMember {...defaultProps} />
));

View File

@ -0,0 +1,20 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastAlreadyGroupMember = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return (
<Toast onClose={onClose}>{i18n('GroupV2--join--already-in-group')}</Toast>
);
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastAlreadyRequestedToJoin } from './ToastAlreadyRequestedToJoin';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastAlreadyRequestedToJoin', module);
story.add('ToastAlreadyRequestedToJoin', () => (
<ToastAlreadyRequestedToJoin {...defaultProps} />
));

View File

@ -0,0 +1,22 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastAlreadyRequestedToJoin = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return (
<Toast onClose={onClose}>
{i18n('GroupV2--join--already-awaiting-approval')}
</Toast>
);
};

View File

@ -0,0 +1,21 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastBlocked } from './ToastBlocked';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastBlocked', module);
story.add('ToastBlocked', () => <ToastBlocked {...defaultProps} />);

View File

@ -0,0 +1,15 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastBlocked = ({ i18n, onClose }: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('unblockToSend')}</Toast>;
};

View File

@ -0,0 +1,21 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastBlockedGroup } from './ToastBlockedGroup';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastBlockedGroup', module);
story.add('ToastBlockedGroup', () => <ToastBlockedGroup {...defaultProps} />);

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastBlockedGroup = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('unblockGroupToSend')}</Toast>;
};

View File

@ -0,0 +1,26 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastCannotMixImageAndNonImageAttachments } from './ToastCannotMixImageAndNonImageAttachments';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf(
'Components/ToastCannotMixImageAndNonImageAttachments',
module
);
story.add('ToastCannotMixImageAndNonImageAttachments', () => (
<ToastCannotMixImageAndNonImageAttachments {...defaultProps} />
));

View File

@ -0,0 +1,22 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastCannotMixImageAndNonImageAttachments = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return (
<Toast onClose={onClose}>
{i18n('cannotMixImageAndNonImageAttachments')}
</Toast>
);
};

View File

@ -0,0 +1,20 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastCannotStartGroupCall = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return (
<Toast onClose={onClose}>{i18n('GroupV2--cannot-start-group-call')}</Toast>
);
};

View File

@ -0,0 +1,21 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastCaptchaFailed } from './ToastCaptchaFailed';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastCaptchaFailed', module);
story.add('ToastCaptchaFailed', () => <ToastCaptchaFailed {...defaultProps} />);

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastCaptchaFailed = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('verificationFailed')}</Toast>;
};

View File

@ -0,0 +1,21 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastCaptchaSolved } from './ToastCaptchaSolved';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastCaptchaSolved', module);
story.add('ToastCaptchaSolved', () => <ToastCaptchaSolved {...defaultProps} />);

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastCaptchaSolved = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('verificationComplete')}</Toast>;
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastConversationArchived } from './ToastConversationArchived';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastConversationArchived', module);
story.add('ToastConversationArchived', () => (
<ToastConversationArchived {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastConversationArchived = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('conversationArchived')}</Toast>;
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastConversationMarkedUnread } from './ToastConversationMarkedUnread';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastConversationMarkedUnread', module);
story.add('ToastConversationMarkedUnread', () => (
<ToastConversationMarkedUnread {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastConversationMarkedUnread = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('conversationMarkedUnread')}</Toast>;
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastConversationUnarchived } from './ToastConversationUnarchived';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastConversationUnarchived', module);
story.add('ToastConversationUnarchived', () => (
<ToastConversationUnarchived {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastConversationUnarchived = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('conversationReturnedToInbox')}</Toast>;
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastDangerousFileType } from './ToastDangerousFileType';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastDangerousFileType', module);
story.add('ToastDangerousFileType', () => (
<ToastDangerousFileType {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastDangerousFileType = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('dangerousFileType')}</Toast>;
};

View File

@ -0,0 +1,24 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastDecryptionError } from './ToastDecryptionError';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
onShowDebugLog: action('onShowDebugLog'),
};
const story = storiesOf('Components/ToastDecryptionError', module);
story.add('ToastDecryptionError', () => (
<ToastDecryptionError {...defaultProps} />
));

View File

@ -0,0 +1,32 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
export type ToastPropsType = {
onShowDebugLog: () => unknown;
};
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
} & ToastPropsType;
export const ToastDecryptionError = ({
i18n,
onClose,
onShowDebugLog,
}: PropsType): JSX.Element => {
return (
<Toast
autoDismissDisabled
className="decryption-error"
onClick={onShowDebugLog}
onClose={onClose}
>
{i18n('decryptionErrorToast')}
</Toast>
);
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastDeleteForEveryoneFailed } from './ToastDeleteForEveryoneFailed';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastDeleteForEveryoneFailed', module);
story.add('ToastDeleteForEveryoneFailed', () => (
<ToastDeleteForEveryoneFailed {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastDeleteForEveryoneFailed = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('deleteForEveryoneFailed')}</Toast>;
};

View File

@ -0,0 +1,21 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastExpired } from './ToastExpired';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastExpired', module);
story.add('ToastExpired', () => <ToastExpired {...defaultProps} />);

View File

@ -0,0 +1,15 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastExpired = ({ i18n, onClose }: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('expiredWarning')}</Toast>;
};

View File

@ -0,0 +1,22 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastFileSaved } from './ToastFileSaved';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
onOpenFile: action('onOpenFile'),
};
const story = storiesOf('Components/ToastFileSaved', module);
story.add('ToastFileSaved', () => <ToastFileSaved {...defaultProps} />);

View File

@ -0,0 +1,27 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
export type ToastPropsType = {
onOpenFile: () => unknown;
};
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
} & ToastPropsType;
export const ToastFileSaved = ({
i18n,
onClose,
onOpenFile,
}: PropsType): JSX.Element => {
return (
<Toast onClick={onOpenFile} onClose={onClose}>
{i18n('attachmentSaved')}
</Toast>
);
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastFileSize } from './ToastFileSize';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastFileSize', module);
story.add('ToastFileSize', () => (
<ToastFileSize {...defaultProps} limit={100} units="MB" />
));

View File

@ -0,0 +1,31 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
export type ToastPropsType = {
limit: number;
units: string;
};
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
} & ToastPropsType;
export const ToastFileSize = ({
i18n,
limit,
onClose,
units,
}: PropsType): JSX.Element => {
return (
<Toast onClose={onClose}>
{i18n('fileSizeWarning')}
{limit}
{units}
</Toast>
);
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastGroupLinkCopied } from './ToastGroupLinkCopied';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastGroupLinkCopied', module);
story.add('ToastGroupLinkCopied', () => (
<ToastGroupLinkCopied {...defaultProps} />
));

View File

@ -0,0 +1,20 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastGroupLinkCopied = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return (
<Toast onClose={onClose}>{i18n('GroupLinkManagement--clipboard')}</Toast>
);
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastInvalidConversation } from './ToastInvalidConversation';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastInvalidConversation', module);
story.add('ToastInvalidConversation', () => (
<ToastInvalidConversation {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastInvalidConversation = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('invalidConversation')}</Toast>;
};

View File

@ -0,0 +1,21 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastLeftGroup } from './ToastLeftGroup';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastLeftGroup', module);
story.add('ToastLeftGroup', () => <ToastLeftGroup {...defaultProps} />);

View File

@ -0,0 +1,15 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastLeftGroup = ({ i18n, onClose }: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('youLeftTheGroup')}</Toast>;
};

View File

@ -0,0 +1,21 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastLinkCopied } from './ToastLinkCopied';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastLinkCopied', module);
story.add('ToastLinkCopied', () => <ToastLinkCopied {...defaultProps} />);

View File

@ -0,0 +1,15 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastLinkCopied = ({ i18n, onClose }: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('debugLogLinkCopied')}</Toast>;
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastLoadingFullLogs } from './ToastLoadingFullLogs';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastLoadingFullLogs', module);
story.add('ToastLoadingFullLogs', () => (
<ToastLoadingFullLogs {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastLoadingFullLogs = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('loading')}</Toast>;
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastMaxAttachments } from './ToastMaxAttachments';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastMaxAttachments', module);
story.add('ToastMaxAttachments', () => (
<ToastMaxAttachments {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastMaxAttachments = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('maximumAttachments')}</Toast>;
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastMessageBodyTooLong } from './ToastMessageBodyTooLong';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastMessageBodyTooLong', module);
story.add('ToastMessageBodyTooLong', () => (
<ToastMessageBodyTooLong {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastMessageBodyTooLong = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('messageBodyTooLong')}</Toast>;
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastOneNonImageAtATime } from './ToastOneNonImageAtATime';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastOneNonImageAtATime', module);
story.add('ToastOneNonImageAtATime', () => (
<ToastOneNonImageAtATime {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastOneNonImageAtATime = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('oneNonImageAtATimeToast')}</Toast>;
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastOriginalMessageNotFound } from './ToastOriginalMessageNotFound';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastOriginalMessageNotFound', module);
story.add('ToastOriginalMessageNotFound', () => (
<ToastOriginalMessageNotFound {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastOriginalMessageNotFound = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('originalMessageNotFound')}</Toast>;
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastPinnedConversationsFull } from './ToastPinnedConversationsFull';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastPinnedConversationsFull', module);
story.add('ToastPinnedConversationsFull', () => (
<ToastPinnedConversationsFull {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastPinnedConversationsFull = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('pinnedConversationsFull')}</Toast>;
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastReactionFailed } from './ToastReactionFailed';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastReactionFailed', module);
story.add('ToastReactionFailed', () => (
<ToastReactionFailed {...defaultProps} />
));

View File

@ -0,0 +1,22 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastReactionFailed = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return (
<Toast onClick={onClose} onClose={onClose}>
{i18n('Reactions--error')}
</Toast>
);
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastReportedSpamAndBlocked } from './ToastReportedSpamAndBlocked';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastReportedSpamAndBlocked', module);
story.add('ToastReportedSpamAndBlocked', () => (
<ToastReportedSpamAndBlocked {...defaultProps} />
));

View File

@ -0,0 +1,22 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastReportedSpamAndBlocked = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return (
<Toast onClose={onClose}>
{i18n('MessageRequests--block-and-report-spam-success-toast')}
</Toast>
);
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastStickerPackInstallFailed } from './ToastStickerPackInstallFailed';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastStickerPackInstallFailed', module);
story.add('ToastStickerPackInstallFailed', () => (
<ToastStickerPackInstallFailed {...defaultProps} />
));

View File

@ -0,0 +1,20 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastStickerPackInstallFailed = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return (
<Toast onClose={onClose}>{i18n('stickers--toast--InstallFailed')}</Toast>
);
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastTapToViewExpiredIncoming } from './ToastTapToViewExpiredIncoming';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastTapToViewExpiredIncoming', module);
story.add('ToastTapToViewExpiredIncoming', () => (
<ToastTapToViewExpiredIncoming {...defaultProps} />
));

View File

@ -0,0 +1,22 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastTapToViewExpiredIncoming = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return (
<Toast onClose={onClose}>
{i18n('Message--tap-to-view--incoming--expired-toast')}
</Toast>
);
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastTapToViewExpiredOutgoing } from './ToastTapToViewExpiredOutgoing';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastTapToViewExpiredOutgoing', module);
story.add('ToastTapToViewExpiredOutgoing', () => (
<ToastTapToViewExpiredOutgoing {...defaultProps} />
));

View File

@ -0,0 +1,22 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastTapToViewExpiredOutgoing = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return (
<Toast onClose={onClose}>
{i18n('Message--tap-to-view--outgoing--expired-toast')}
</Toast>
);
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastUnableToLoadAttachment } from './ToastUnableToLoadAttachment';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastUnableToLoadAttachment', module);
story.add('ToastUnableToLoadAttachment', () => (
<ToastUnableToLoadAttachment {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastUnableToLoadAttachment = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('unableToLoadAttachment')}</Toast>;
};

View File

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastVoiceNoteLimit } from './ToastVoiceNoteLimit';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf('Components/ToastVoiceNoteLimit', module);
story.add('ToastVoiceNoteLimit', () => (
<ToastVoiceNoteLimit {...defaultProps} />
));

View File

@ -0,0 +1,18 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastVoiceNoteLimit = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return <Toast onClose={onClose}>{i18n('voiceNoteLimit')}</Toast>;
};

View File

@ -0,0 +1,26 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { ToastVoiceNoteMustBeOnlyAttachment } from './ToastVoiceNoteMustBeOnlyAttachment';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
const story = storiesOf(
'Components/ToastVoiceNoteMustBeOnlyAttachment',
module
);
story.add('ToastVoiceNoteMustBeOnlyAttachment', () => (
<ToastVoiceNoteMustBeOnlyAttachment {...defaultProps} />
));

View File

@ -0,0 +1,20 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export const ToastVoiceNoteMustBeOnlyAttachment = ({
i18n,
onClose,
}: PropsType): JSX.Element => {
return (
<Toast onClose={onClose}>{i18n('voiceNoteMustBeOnlyAttachment')}</Toast>
);
};

View File

@ -21,6 +21,9 @@ import type { ConversationModel } from '../models/conversations';
import type { PreJoinConversationType } from '../state/ducks/conversations';
import { SignalService as Proto } from '../protobuf';
import * as log from '../logging/log';
import { showToast } from '../util/showToast';
import { ToastAlreadyGroupMember } from '../components/ToastAlreadyGroupMember';
import { ToastAlreadyRequestedToJoin } from '../components/ToastAlreadyRequestedToJoin';
export async function joinViaLink(hash: string): Promise<void> {
let inviteLinkPassword: string;
@ -65,10 +68,7 @@ export async function joinViaLink(hash: string): Promise<void> {
window.reduxActions.conversations.openConversationInternal({
conversationId: existingConversation.id,
});
window.Whisper.ToastView.show(
window.Whisper.AlreadyGroupMemberToast,
document.getElementsByClassName('conversation-stack')[0]
);
showToast(ToastAlreadyGroupMember);
return;
}
@ -144,10 +144,7 @@ export async function joinViaLink(hash: string): Promise<void> {
conversationId: existingConversation.id,
});
window.Whisper.ToastView.show(
window.Whisper.AlreadyRequestedToJoinToast,
document.getElementsByClassName('conversation-stack')[0]
);
showToast(ToastAlreadyRequestedToJoin);
return;
}

10
ts/util/copyGroupLink.ts Normal file
View File

@ -0,0 +1,10 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { showToast } from './showToast';
import { ToastGroupLinkCopied } from '../components/ToastGroupLinkCopied';
export async function copyGroupLink(groupLink: string): Promise<void> {
await window.navigator.clipboard.writeText(groupLink);
showToast(ToastGroupLinkCopied);
}

View File

@ -17,6 +17,8 @@ import { parseIntOrThrow } from './parseIntOrThrow';
import * as RemoteConfig from '../RemoteConfig';
import { Address } from '../types/Address';
import { QualifiedAddress } from '../types/QualifiedAddress';
import { ToastDecryptionError } from '../components/ToastDecryptionError';
import { showToast } from './showToast';
import { ConversationModel } from '../models/conversations';
import {
@ -131,10 +133,9 @@ function maybeShowDecryptionToast(logId: string) {
}
log.info(`maybeShowDecryptionToast/${logId}: Showing decryption error toast`);
window.Whisper.ToastView.show(
window.Whisper.DecryptionErrorToast,
document.getElementsByClassName('conversation-stack')[0]
);
showToast(ToastDecryptionError, {
onShowDebugLog: () => window.showDebugLog(),
});
}
export async function onDecryptionError(

View File

@ -0,0 +1,33 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { AttachmentType } from '../types/Attachment';
import { showToast } from './showToast';
import { ToastFileSize } from '../components/ToastFileSize';
export function isAttachmentSizeOkay(
attachment: Readonly<AttachmentType>
): boolean {
const limitKb = window.Signal.Types.Attachment.getUploadSizeLimitKb(
attachment.contentType
);
// this needs to be cast properly
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if ((attachment.data.byteLength / 1024).toFixed(4) >= limitKb) {
const units = ['kB', 'MB', 'GB'];
let u = -1;
let limit = limitKb * 1000;
do {
limit /= 1000;
u += 1;
} while (limit >= 1000 && u < units.length - 1);
showToast(ToastFileSize, {
limit,
units: units[u],
});
return false;
}
return true;
}

View File

@ -12076,69 +12076,6 @@
"updated": "2018-09-17T20:50:40.689Z",
"reasonDetail": "Hard-coded value"
},
{
"rule": "jQuery-$(",
"path": "ts/backbone/views/toast_view.js",
"line": " template: () => $('#toast').html(),",
"reasonCategory": "falseMatch",
"updated": "2021-02-26T19:21:25.912Z"
},
{
"rule": "jQuery-appendTo(",
"path": "ts/backbone/views/toast_view.js",
"line": " toast.$el.appendTo(el);",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Add sub-view to DOM"
},
{
"rule": "jQuery-html(",
"path": "ts/backbone/views/toast_view.js",
"line": " this.$el.html(window.Mustache.render(window._.result(this, 'template', ''), window._.result(this, 'render_attributes', '')));",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Adding Mustache render to DOM"
},
{
"rule": "jQuery-html(",
"path": "ts/backbone/views/toast_view.js",
"line": " template: () => $('#toast').html(),",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T19:21:25.912Z",
"reasonDetail": "Static selector, read-only access"
},
{
"rule": "jQuery-$(",
"path": "ts/backbone/views/toast_view.ts",
"line": " template: () => $('#toast').html(),",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access"
},
{
"rule": "jQuery-appendTo(",
"path": "ts/backbone/views/toast_view.ts",
"line": " toast.$el.appendTo(el);",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Adding sub-view to DOM"
},
{
"rule": "jQuery-html(",
"path": "ts/backbone/views/toast_view.ts",
"line": " this.$el.html(",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Rendering provided template"
},
{
"rule": "jQuery-html(",
"path": "ts/backbone/views/toast_view.ts",
"line": " template: () => $('#toast').html(),",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T19:23:51.217Z",
"reasonDetail": "Static selector, read-only access"
},
{
"rule": "jQuery-html(",
"path": "ts/backbone/views/whisper_view.js",
@ -13518,14 +13455,7 @@
"path": "ts/views/inbox_view.js",
"line": " view.$el.appendTo(this.el);",
"reasonCategory": "usageTrusted",
"updated": "2021-09-15T21:07:50.995Z"
},
{
"rule": "jQuery-appendTo(",
"path": "ts/views/inbox_view.js",
"line": " toast.$el.appendTo(this.$el);",
"reasonCategory": "usageTrusted",
"updated": "2021-09-15T21:07:50.995Z"
"updated": "2021-09-20T22:27:31.785Z"
},
{
"rule": "jQuery-html(",
@ -13646,13 +13576,6 @@
"reasonCategory": "usageTrusted",
"updated": "2021-09-15T21:07:50.995Z"
},
{
"rule": "jQuery-appendTo(",
"path": "ts/views/inbox_view.ts",
"line": " toast.$el.appendTo(this.$el);",
"reasonCategory": "usageTrusted",
"updated": "2021-09-15T21:07:50.995Z"
},
{
"rule": "jQuery-html(",
"path": "ts/views/inbox_view.ts",

115
ts/util/showToast.tsx Normal file
View File

@ -0,0 +1,115 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { ToastAlreadyGroupMember } from '../components/ToastAlreadyGroupMember';
import { ToastAlreadyRequestedToJoin } from '../components/ToastAlreadyRequestedToJoin';
import { ToastBlocked } from '../components/ToastBlocked';
import { ToastBlockedGroup } from '../components/ToastBlockedGroup';
import { ToastCannotMixImageAndNonImageAttachments } from '../components/ToastCannotMixImageAndNonImageAttachments';
import { ToastCannotStartGroupCall } from '../components/ToastCannotStartGroupCall';
import { ToastCaptchaFailed } from '../components/ToastCaptchaFailed';
import { ToastCaptchaSolved } from '../components/ToastCaptchaSolved';
import { ToastConversationArchived } from '../components/ToastConversationArchived';
import { ToastConversationMarkedUnread } from '../components/ToastConversationMarkedUnread';
import { ToastConversationUnarchived } from '../components/ToastConversationUnarchived';
import { ToastDangerousFileType } from '../components/ToastDangerousFileType';
import {
ToastDecryptionError,
ToastPropsType as ToastDecryptionErrorPropsType,
} from '../components/ToastDecryptionError';
import { ToastDeleteForEveryoneFailed } from '../components/ToastDeleteForEveryoneFailed';
import { ToastExpired } from '../components/ToastExpired';
import {
ToastFileSaved,
ToastPropsType as ToastFileSavedPropsType,
} from '../components/ToastFileSaved';
import {
ToastFileSize,
ToastPropsType as ToastFileSizePropsType,
} from '../components/ToastFileSize';
import { ToastGroupLinkCopied } from '../components/ToastGroupLinkCopied';
import { ToastInvalidConversation } from '../components/ToastInvalidConversation';
import { ToastLeftGroup } from '../components/ToastLeftGroup';
import { ToastLinkCopied } from '../components/ToastLinkCopied';
import { ToastLoadingFullLogs } from '../components/ToastLoadingFullLogs';
import { ToastMaxAttachments } from '../components/ToastMaxAttachments';
import { ToastMessageBodyTooLong } from '../components/ToastMessageBodyTooLong';
import { ToastOneNonImageAtATime } from '../components/ToastOneNonImageAtATime';
import { ToastOriginalMessageNotFound } from '../components/ToastOriginalMessageNotFound';
import { ToastPinnedConversationsFull } from '../components/ToastPinnedConversationsFull';
import { ToastReactionFailed } from '../components/ToastReactionFailed';
import { ToastReportedSpamAndBlocked } from '../components/ToastReportedSpamAndBlocked';
import { ToastStickerPackInstallFailed } from '../components/ToastStickerPackInstallFailed';
import { ToastTapToViewExpiredIncoming } from '../components/ToastTapToViewExpiredIncoming';
import { ToastTapToViewExpiredOutgoing } from '../components/ToastTapToViewExpiredOutgoing';
import { ToastUnableToLoadAttachment } from '../components/ToastUnableToLoadAttachment';
import { ToastVoiceNoteLimit } from '../components/ToastVoiceNoteLimit';
import { ToastVoiceNoteMustBeOnlyAttachment } from '../components/ToastVoiceNoteMustBeOnlyAttachment';
export function showToast(Toast: typeof ToastAlreadyGroupMember): void;
export function showToast(Toast: typeof ToastAlreadyRequestedToJoin): void;
export function showToast(Toast: typeof ToastBlocked): void;
export function showToast(Toast: typeof ToastBlockedGroup): void;
export function showToast(
Toast: typeof ToastCannotMixImageAndNonImageAttachments
): void;
export function showToast(Toast: typeof ToastCannotStartGroupCall): void;
export function showToast(Toast: typeof ToastCaptchaFailed): void;
export function showToast(Toast: typeof ToastCaptchaSolved): void;
export function showToast(Toast: typeof ToastConversationArchived): void;
export function showToast(Toast: typeof ToastConversationMarkedUnread): void;
export function showToast(Toast: typeof ToastConversationUnarchived): void;
export function showToast(Toast: typeof ToastDangerousFileType): void;
export function showToast(
Toast: typeof ToastDecryptionError,
props: ToastDecryptionErrorPropsType
): void;
export function showToast(Toast: typeof ToastDeleteForEveryoneFailed): void;
export function showToast(Toast: typeof ToastExpired): void;
export function showToast(
Toast: typeof ToastFileSaved,
props: ToastFileSavedPropsType
): void;
export function showToast(
Toast: typeof ToastFileSize,
props: ToastFileSizePropsType
): void;
export function showToast(Toast: typeof ToastGroupLinkCopied): void;
export function showToast(Toast: typeof ToastInvalidConversation): void;
export function showToast(Toast: typeof ToastLeftGroup): void;
export function showToast(Toast: typeof ToastLinkCopied): void;
export function showToast(Toast: typeof ToastLoadingFullLogs): void;
export function showToast(Toast: typeof ToastMaxAttachments): void;
export function showToast(Toast: typeof ToastMessageBodyTooLong): void;
export function showToast(Toast: typeof ToastOneNonImageAtATime): void;
export function showToast(Toast: typeof ToastOriginalMessageNotFound): void;
export function showToast(Toast: typeof ToastPinnedConversationsFull): void;
export function showToast(Toast: typeof ToastReactionFailed): void;
export function showToast(Toast: typeof ToastReportedSpamAndBlocked): void;
export function showToast(Toast: typeof ToastStickerPackInstallFailed): void;
export function showToast(Toast: typeof ToastTapToViewExpiredIncoming): void;
export function showToast(Toast: typeof ToastTapToViewExpiredOutgoing): void;
export function showToast(Toast: typeof ToastUnableToLoadAttachment): void;
export function showToast(Toast: typeof ToastVoiceNoteLimit): void;
export function showToast(
Toast: typeof ToastVoiceNoteMustBeOnlyAttachment
): void;
// eslint-disable-next-line max-len
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
export function showToast(Toast: any, props = {}): void {
const node = document.getElementById('toast');
function onClose() {
if (!node) {
return;
}
unmountComponentAtNode(node);
}
render(<Toast i18n={window.i18n} onClose={onClose} {...props} />, node);
}

View File

@ -85,6 +85,35 @@ import { dropNull } from '../util/dropNull';
import { CompositionAPIType } from '../components/CompositionArea';
import * as log from '../logging/log';
import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser';
import { ToastCannotStartGroupCall } from '../components/ToastCannotStartGroupCall';
import { showToast } from '../util/showToast';
import { ToastBlocked } from '../components/ToastBlocked';
import { ToastBlockedGroup } from '../components/ToastBlockedGroup';
import { ToastCannotMixImageAndNonImageAttachments } from '../components/ToastCannotMixImageAndNonImageAttachments';
import { ToastConversationArchived } from '../components/ToastConversationArchived';
import { ToastConversationMarkedUnread } from '../components/ToastConversationMarkedUnread';
import { ToastConversationUnarchived } from '../components/ToastConversationUnarchived';
import { ToastDangerousFileType } from '../components/ToastDangerousFileType';
import { ToastDeleteForEveryoneFailed } from '../components/ToastDeleteForEveryoneFailed';
import { ToastExpired } from '../components/ToastExpired';
import { ToastFileSaved } from '../components/ToastFileSaved';
import { ToastFileSize } from '../components/ToastFileSize';
import { ToastInvalidConversation } from '../components/ToastInvalidConversation';
import { ToastLeftGroup } from '../components/ToastLeftGroup';
import { ToastMaxAttachments } from '../components/ToastMaxAttachments';
import { ToastMessageBodyTooLong } from '../components/ToastMessageBodyTooLong';
import { ToastOneNonImageAtATime } from '../components/ToastOneNonImageAtATime';
import { ToastOriginalMessageNotFound } from '../components/ToastOriginalMessageNotFound';
import { ToastPinnedConversationsFull } from '../components/ToastPinnedConversationsFull';
import { ToastReactionFailed } from '../components/ToastReactionFailed';
import { ToastReportedSpamAndBlocked } from '../components/ToastReportedSpamAndBlocked';
import { ToastTapToViewExpiredIncoming } from '../components/ToastTapToViewExpiredIncoming';
import { ToastTapToViewExpiredOutgoing } from '../components/ToastTapToViewExpiredOutgoing';
import { ToastUnableToLoadAttachment } from '../components/ToastUnableToLoadAttachment';
import { ToastVoiceNoteLimit } from '../components/ToastVoiceNoteLimit';
import { ToastVoiceNoteMustBeOnlyAttachment } from '../components/ToastVoiceNoteMustBeOnlyAttachment';
import { copyGroupLink } from '../util/copyGroupLink';
import { isAttachmentSizeOkay } from '../util/isAttachmentSizeOkay';
type AttachmentOptions = {
messageId: string;
@ -186,314 +215,8 @@ type MediaType = {
};
};
Whisper.ExpiredToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('expiredWarning') };
},
});
Whisper.BlockedToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('unblockToSend') };
},
});
Whisper.BlockedGroupToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('unblockGroupToSend') };
},
});
Whisper.CaptchaSolvedToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('verificationComplete') };
},
});
Whisper.CaptchaFailedToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('verificationFailed') };
},
});
Whisper.LeftGroupToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('youLeftTheGroup') };
},
});
Whisper.InvalidConversationToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('invalidConversation') };
},
});
Whisper.OriginalNotFoundToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('originalMessageNotFound') };
},
});
Whisper.OriginalNoLongerAvailableToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('originalMessageNotAvailable') };
},
});
Whisper.FoundButNotLoadedToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('messageFoundButNotLoaded') };
},
});
Whisper.VoiceNoteLimit = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('voiceNoteLimit') };
},
});
Whisper.VoiceNoteMustBeOnlyAttachmentToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('voiceNoteMustBeOnlyAttachment') };
},
});
Whisper.ConversationArchivedToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('conversationArchived') };
},
});
Whisper.ConversationUnarchivedToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('conversationReturnedToInbox') };
},
});
Whisper.ConversationMarkedUnreadToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('conversationMarkedUnread') };
},
});
Whisper.TapToViewExpiredIncomingToast = Whisper.ToastView.extend({
render_attributes() {
return {
toastMessage: window.i18n(
'Message--tap-to-view--incoming--expired-toast'
),
};
},
});
Whisper.TapToViewExpiredOutgoingToast = Whisper.ToastView.extend({
render_attributes() {
return {
toastMessage: window.i18n(
'Message--tap-to-view--outgoing--expired-toast'
),
};
},
});
Whisper.DecryptionErrorToast = Whisper.ToastView.extend({
className: 'toast toast-clickable decryption-error',
events: {
click: 'onClick',
keydown: 'onKeyDown',
},
render_attributes() {
return {
toastMessage: window.i18n('decryptionErrorToast'),
};
},
// Note: this is the same thing as ToastView, except it's missing the setTimeout, so it
// will stick around until the user interacts with it.
render() {
const toasts = document.getElementsByClassName('decryption-error');
if (toasts.length > 1) {
log.info(
'DecryptionErrorToast: We are second decryption error toast. Closing.'
);
this.close();
return;
}
this.$el.html(
window.Mustache.render(
window._.result(this, 'template', ''),
window._.result(this, 'render_attributes', '')
)
);
this.$el.attr('tabIndex', 0);
this.$el.show();
if (window.getInteractionMode() === 'keyboard') {
setTimeout(() => {
this.$el.focus();
}, 1);
}
},
onClick() {
this.close();
window.showDebugLog();
},
onKeyDown(event: KeyboardEvent) {
if (event.key !== 'Enter' && event.key !== ' ') {
return;
}
this.onClick();
},
});
Whisper.FileSavedToast = Whisper.ToastView.extend({
className: 'toast toast-clickable',
initialize(options: Readonly<{ fullPath: string }>) {
if (!options.fullPath) {
throw new Error('FileSavedToast: name option was not provided!');
}
this.fullPath = options.fullPath;
this.timeout = 10000;
if (window.getInteractionMode() === 'keyboard') {
setTimeout(() => {
this.$el.focus();
}, 1);
}
},
events: {
click: 'onClick',
keydown: 'onKeydown',
},
onClick() {
openFileInFolder(this.fullPath);
this.close();
},
onKeydown(event: KeyboardEvent) {
if (event.key !== 'Enter' && event.key !== ' ') {
return;
}
event.preventDefault();
event.stopPropagation();
openFileInFolder(this.fullPath);
this.close();
},
render_attributes() {
return { toastMessage: window.i18n('attachmentSaved') };
},
});
Whisper.ReactionFailedToast = Whisper.ToastView.extend({
className: 'toast toast-clickable',
initialize() {
this.timeout = 4000;
if (window.getInteractionMode() === 'keyboard') {
setTimeout(() => {
this.$el.focus();
}, 1);
}
},
events: {
click: 'onClick',
keydown: 'onKeydown',
},
onClick() {
this.close();
},
onKeydown(event: KeyboardEvent) {
if (event.key !== 'Enter' && event.key !== ' ') {
return;
}
event.preventDefault();
event.stopPropagation();
this.close();
},
render_attributes() {
return { toastMessage: window.i18n('Reactions--error') };
},
});
Whisper.DeleteForEveryoneFailedToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('deleteForEveryoneFailed') };
},
});
Whisper.GroupLinkCopiedToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('GroupLinkManagement--clipboard') };
},
});
Whisper.PinnedConversationsFullToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('pinnedConversationsFull') };
},
});
const MAX_MESSAGE_BODY_LENGTH = 64 * 1024;
Whisper.MessageBodyTooLongToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('messageBodyTooLong') };
},
});
Whisper.FileSizeToast = Whisper.ToastView.extend({
template: () => $('#file-size-modal').html(),
render_attributes() {
return {
'file-size-warning': window.i18n('fileSizeWarning'),
limit: this.model.limit,
units: this.model.units,
};
},
});
Whisper.UnableToLoadToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('unableToLoadAttachment') };
},
});
Whisper.CannotStartGroupCallToast = Whisper.ToastView.extend({
template: () => window.i18n('GroupV2--cannot-start-group-call'),
});
Whisper.DangerousFileTypeToast = Whisper.ToastView.extend({
template: () => window.i18n('dangerousFileType'),
});
Whisper.OneNonImageAtATimeToast = Whisper.ToastView.extend({
template: () => window.i18n('oneNonImageAtATimeToast'),
});
Whisper.CannotMixImageAndNonImageAttachmentsToast = Whisper.ToastView.extend({
template: () => window.i18n('cannotMixImageAndNonImageAttachments'),
});
Whisper.MaxAttachmentsToast = Whisper.ToastView.extend({
template: () => window.i18n('maximumAttachments'),
});
Whisper.AlreadyGroupMemberToast = Whisper.ToastView.extend({
template: () => window.i18n('GroupV2--join--already-in-group'),
});
Whisper.AlreadyRequestedToJoinToast = Whisper.ToastView.extend({
template: () => window.i18n('GroupV2--join--already-awaiting-approval'),
});
const ReportedSpamAndBlockedToast = Whisper.ToastView.extend({
template: () =>
window.i18n('MessageRequests--block-and-report-spam-success-toast'),
});
export class ConversationView extends window.Backbone.View<ConversationModel> {
// Debounced functions
private debouncedMaybeGrabLinkPreview: (
@ -660,7 +383,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
);
if (pinnedConversationIds.length >= 4) {
this.showToast(Whisper.PinnedConversationsFullToast);
showToast(ToastPinnedConversationsFull);
return;
}
this.model.pin();
@ -726,7 +449,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
this.model.get('announcementsOnly') &&
!this.model.areWeAdmin()
) {
this.showToast(Whisper.CannotStartGroupCallToast);
showToast(ToastCannotStartGroupCall);
return;
}
@ -769,26 +492,17 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
this.model.setArchived(true);
this.model.trigger('unload', 'archive');
Whisper.ToastView.show(
Whisper.ConversationArchivedToast,
document.body
);
showToast(ToastConversationArchived);
},
onMarkUnread: () => {
this.model.setMarkedUnread(true);
Whisper.ToastView.show(
Whisper.ConversationMarkedUnreadToast,
document.body
);
showToast(ToastConversationMarkedUnread);
},
onMoveToInbox: () => {
this.model.setArchived(false);
Whisper.ToastView.show(
Whisper.ConversationUnarchivedToast,
document.body
);
showToast(ToastConversationUnarchived);
},
}
),
@ -824,7 +538,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
bodyRanges: Array<BodyRangeType>,
caretLocation?: number
) => this.onEditorStateChange(msg, bodyRanges, caretLocation),
onTextTooLong: () => this.showToast(Whisper.MessageBodyTooLongToast),
onTextTooLong: () => showToast(ToastMessageBodyTooLong),
onChooseAttachment: this.onChooseAttachment.bind(this),
getQuotedMessage: () => this.model.get('quotedMessageId'),
clearQuotedMessage: () => this.setQuoteMessage(null),
@ -1040,11 +754,11 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
};
const showExpiredIncomingTapToViewToast = () => {
log.info('Showing expired tap-to-view toast for an incoming message');
this.showToast(Whisper.TapToViewExpiredIncomingToast);
showToast(ToastTapToViewExpiredIncoming);
};
const showExpiredOutgoingTapToViewToast = () => {
log.info('Showing expired tap-to-view toast for an outgoing message');
this.showToast(Whisper.TapToViewExpiredOutgoingToast);
showToast(ToastTapToViewExpiredOutgoing);
};
const showForwardMessageModal = this.showForwardMessageModal.bind(this);
@ -1115,7 +829,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
);
if (!message) {
this.showToast(Whisper.OriginalNotFoundToast);
showToast(ToastOriginalMessageNotFound);
return;
}
@ -1319,28 +1033,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
this.$('.timeline-placeholder').append(this.timelineView.el);
}
private showToast(
ToastView: typeof window.Whisper.ToastView,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
options?: any,
element?: Element
): void {
const toast = new ToastView(options);
if (element) {
toast.$el.appendTo(element);
} else {
const lightboxEl = $('.Lightbox');
if (lightboxEl.length > 0) {
toast.$el.appendTo(lightboxEl);
} else {
toast.$el.appendTo(this.$el);
}
}
toast.render();
}
// eslint-disable-next-line class-methods-use-this
async cleanModels(
collection: MessageModelCollectionType | Array<MessageModel>
@ -1785,7 +1477,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
jobQueue: reportSpamJobQueue,
}),
]);
this.showToast(ReportedSpamAndBlockedToast);
showToast(ToastReportedSpamAndBlocked);
},
});
}
@ -2031,22 +1723,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
});
}
showFileSizeError({
limit,
units,
u,
}: Readonly<{
limit: number;
units: Array<string>;
u: number;
}>): void {
const toast = new Whisper.FileSizeToast({
model: { limit, units: units[u] },
});
toast.$el.insertAfter(this.$el);
toast.render();
}
updateAttachmentsView(): void {
const draftAttachments = this.model.get('draftAttachments') || [];
window.reduxActions.composer.replaceAttachments(
@ -2091,18 +1767,21 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
const MB = 1000 * 1024;
if (file.size > 100 * MB) {
this.showFileSizeError({ limit: 100, units: ['MB'], u: 0 });
showToast(ToastFileSize, {
limit: 100,
units: 'MB',
});
return;
}
if (window.Signal.Util.isFileDangerous(file.name)) {
this.showToast(Whisper.DangerousFileTypeToast);
showToast(ToastDangerousFileType);
return;
}
const draftAttachments = this.model.get('draftAttachments') || [];
if (draftAttachments.length >= 32) {
this.showToast(Whisper.MaxAttachmentsToast);
showToast(ToastMaxAttachments);
return;
}
@ -2112,7 +1791,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
);
// You can't add another attachment if you already have a non-image staged
if (haveNonImage) {
this.showToast(Whisper.OneNonImageAtATimeToast);
showToast(ToastOneNonImageAtATime);
return;
}
@ -2120,7 +1799,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
// You can't add a non-image attachment if you already have attachments staged
if (!MIME.isImage(fileType) && draftAttachments.length > 0) {
this.showToast(Whisper.CannotMixImageAndNonImageAttachmentsToast);
showToast(ToastCannotMixImageAndNonImageAttachments);
return;
}
@ -2191,7 +1870,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
}
try {
if (!this.isSizeOkay(attachment)) {
if (!isAttachmentSizeOkay(attachment)) {
this.removeDraftAttachment(attachment);
}
} catch (error) {
@ -2201,7 +1880,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
);
this.removeDraftAttachment(attachment);
this.showToast(Whisper.UnableToLoadToast);
showToast(ToastUnableToLoadAttachment);
return;
}
@ -2213,32 +1892,10 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
error && error.stack ? error.stack : error
);
this.showToast(Whisper.UnableToLoadToast);
showToast(ToastUnableToLoadAttachment);
}
}
isSizeOkay(attachment: Readonly<AttachmentType>): boolean {
const limitKb = window.Signal.Types.Attachment.getUploadSizeLimitKb(
attachment.contentType
);
// this needs to be cast properly
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if ((attachment.data.byteLength / 1024).toFixed(4) >= limitKb) {
const units = ['kB', 'MB', 'GB'];
let u = -1;
let limit = limitKb * 1000;
do {
limit /= 1000;
u += 1;
} while (limit >= 1000 && u < units.length - 1);
this.showFileSizeError({ limit, units, u });
return false;
}
return true;
}
async handleVideoAttachment(
file: Readonly<File>
): Promise<InMemoryAttachmentDraftType> {
@ -2312,11 +1969,11 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
}
if (this.hasFiles({ includePending: true })) {
this.showToast(Whisper.VoiceNoteMustBeOnlyAttachmentToast);
showToast(ToastVoiceNoteMustBeOnlyAttachment);
return;
}
this.showToast(Whisper.VoiceNoteLimit);
showToast(ToastVoiceNoteLimit);
// Note - clicking anywhere will close the audio capture panel, due to
// the onClick handler in InboxView, which calls its closeRecording method.
@ -2504,12 +2161,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
this.debouncedMaybeGrabLinkPreview(messageText, caretLocation);
}
},
onTextTooLong: () =>
this.showToast(
Whisper.MessageBodyTooLongToast,
{},
document.querySelector('.module-ForwardMessageModal') || undefined
),
onTextTooLong: () => showToast(ToastMessageBodyTooLong),
}
),
});
@ -2787,7 +2439,11 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
});
if (fullPath) {
this.showToast(Whisper.FileSavedToast, { fullPath });
showToast(ToastFileSaved, {
onOpenFile: () => {
openFileInFolder(fullPath);
},
});
}
};
@ -2956,6 +2612,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
this.downloadAttachment({ attachment, timestamp, isDangerous });
}
// eslint-disable-next-line class-methods-use-this
async downloadAttachment({
attachment,
timestamp,
@ -2966,7 +2623,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
isDangerous: boolean;
}): Promise<void> {
if (isDangerous) {
this.showToast(Whisper.DangerousFileTypeToast);
showToast(ToastDangerousFileType);
return;
}
@ -2978,7 +2635,11 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
});
if (fullPath) {
this.showToast(Whisper.FileSavedToast, { fullPath });
showToast(ToastFileSaved, {
onOpenFile: () => {
openFileInFolder(fullPath);
},
});
}
}
@ -3132,7 +2793,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
error && error.stack,
messageId
);
this.showToast(Whisper.DeleteForEveryoneFailedToast);
showToast(ToastDeleteForEveryoneFailed);
}
this.resetPanel();
},
@ -3184,7 +2845,11 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
});
if (fullPath) {
this.showToast(Whisper.FileSavedToast, { fullPath });
showToast(ToastFileSaved, {
onOpenFile: () => {
openFileInFolder(fullPath);
},
});
}
};
@ -3293,7 +2958,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
{
changeHasGroupLink: this.changeHasGroupLink.bind(this),
conversationId: this.model.id,
copyGroupLink: this.copyGroupLink.bind(this),
copyGroupLink,
generateNewGroupLink: this.generateNewGroupLink.bind(this),
setAccessControlAddFromInviteLinkSetting: this.setAccessControlAddFromInviteLinkSetting.bind(
this
@ -3673,11 +3338,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
});
}
async copyGroupLink(groupLink: string): Promise<void> {
await navigator.clipboard.writeText(groupLink);
this.showToast(Whisper.GroupLinkCopiedToast);
}
async generateNewGroupLink(): Promise<void> {
const { model }: { model: ConversationModel } = this;
@ -3820,7 +3480,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
});
} catch (error) {
log.error('Error sending reaction', error, messageId, reaction);
this.showToast(Whisper.ReactionFailedToast);
showToast(ToastReactionFailed);
}
}
@ -3960,13 +3620,20 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
showInvalidMessageToast(messageText?: string): boolean {
const { model }: { model: ConversationModel } = this;
let ToastView: undefined | typeof window.Whisper.ToastView;
let toastView:
| undefined
| typeof ToastBlocked
| typeof ToastBlockedGroup
| typeof ToastExpired
| typeof ToastInvalidConversation
| typeof ToastLeftGroup
| typeof ToastMessageBodyTooLong;
if (window.reduxStore.getState().expiration.hasExpired) {
ToastView = Whisper.ExpiredToast;
toastView = ToastExpired;
}
if (!model.isValid()) {
ToastView = Whisper.InvalidConversationToast;
toastView = ToastInvalidConversation;
}
const e164 = this.model.get('e164');
@ -3976,7 +3643,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
((e164 && window.storage.blocked.isBlocked(e164)) ||
(uuid && window.storage.blocked.isUuidBlocked(uuid)))
) {
ToastView = Whisper.BlockedToast;
toastView = ToastBlocked;
}
const groupId = this.model.get('groupId');
@ -3985,18 +3652,18 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
groupId &&
window.storage.blocked.isGroupBlocked(groupId)
) {
ToastView = Whisper.BlockedGroupToast;
toastView = ToastBlockedGroup;
}
if (!isDirectConversation(model.attributes) && model.get('left')) {
ToastView = Whisper.LeftGroupToast;
toastView = ToastLeftGroup;
}
if (messageText && messageText.length > MAX_MESSAGE_BODY_LENGTH) {
ToastView = Whisper.MessageBodyTooLongToast;
toastView = ToastMessageBodyTooLong;
}
if (ToastView) {
this.showToast(ToastView);
if (toastView) {
showToast(toastView);
return true;
}

View File

@ -4,6 +4,9 @@
import copyText from 'copy-text-to-clipboard';
import * as log from '../logging/log';
import * as debugLog from '../logging/debuglogs';
import { ToastLoadingFullLogs } from '../components/ToastLoadingFullLogs';
import { ToastLinkCopied } from '../components/ToastLinkCopied';
import { showToast } from '../util/showToast';
window.Whisper = window.Whisper || {};
const { Whisper } = window;
@ -18,18 +21,6 @@ const LoadState = {
LogsInTextarea: 4,
};
const LoadingFullLogsToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('loading') };
},
});
const LinkedCopiedToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('debugLogLinkCopied') };
},
});
const DebugLogLinkView = Whisper.View.extend({
template: () => $('#debug-log-link').html(),
initialize(options: { url: string }) {
@ -50,7 +41,7 @@ const DebugLogLinkView = Whisper.View.extend({
e.preventDefault();
const target = e.currentTarget as HTMLAnchorElement;
copyText(target.href);
Whisper.ToastView.show(LinkedCopiedToast, document.body);
showToast(ToastLinkCopied);
},
});
@ -137,7 +128,7 @@ export const DebugLogView = Whisper.View.extend({
throw new Error('Expected log text to be present');
}
this.loadState = LoadState.PuttingLogsInTextarea;
Whisper.ToastView.show(LoadingFullLogsToast, document.body);
showToast(ToastLoadingFullLogs);
setTimeout(() => {
this.textarea.value = this.logText;
this.textarea.removeAttribute('readonly');

View File

@ -3,16 +3,12 @@
import * as log from '../logging/log';
import { ConversationModel } from '../models/conversations';
import { showToast } from '../util/showToast';
import { ToastStickerPackInstallFailed } from '../components/ToastStickerPackInstallFailed';
window.Whisper = window.Whisper || {};
const { Whisper } = window;
const StickerPackInstallFailedToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: window.i18n('stickers--toast--InstallFailed') };
},
});
const ConversationStack = Whisper.View.extend({
className: 'conversation-stack',
lastConversation: null,
@ -146,9 +142,7 @@ Whisper.InboxView = Whisper.View.extend({
}
Whisper.events.on('pack-install-failed', () => {
const toast = new StickerPackInstallFailedToast();
toast.$el.appendTo(this.$el);
toast.render();
showToast(ToastStickerPackInstallFailed);
});
},
render_attributes: {

39
ts/window.d.ts vendored
View File

@ -8,7 +8,7 @@ import * as Backbone from 'backbone';
import * as Underscore from 'underscore';
import moment from 'moment';
import PQueue from 'p-queue/dist';
import { Attributes, ComponentClass, FunctionComponent, Ref } from 'react';
import { Ref } from 'react';
import { imageToBlurHash } from './util/imageToBlurHash';
import * as Util from './util';
import {
@ -623,42 +623,6 @@ export type WhisperType = {
// Note: we can no longer use 'View.extend' once we've moved to Typescript's preferred
// 'extend View' syntax. Thus, we'll need to typescriptify most of it at once.
// Toast
AlreadyGroupMemberToast: typeof AnyViewClass;
AlreadyRequestedToJoinToast: typeof AnyViewClass;
BlockedGroupToast: typeof AnyViewClass;
BlockedToast: typeof AnyViewClass;
CannotMixImageAndNonImageAttachmentsToast: typeof AnyViewClass;
CaptchaSolvedToast: typeof AnyViewClass;
CaptchaFailedToast: typeof AnyViewClass;
CannotStartGroupCallToast: typeof AnyViewClass;
ConversationArchivedToast: typeof AnyViewClass;
ConversationUnarchivedToast: typeof AnyViewClass;
ConversationMarkedUnreadToast: typeof AnyViewClass;
DangerousFileTypeToast: typeof AnyViewClass;
DecryptionErrorToast: typeof AnyViewClass;
ExpiredToast: typeof AnyViewClass;
FileSavedToast: typeof AnyViewClass;
FileSizeToast: typeof AnyViewClass;
FoundButNotLoadedToast: typeof AnyViewClass;
GroupLinkCopiedToast: typeof AnyViewClass;
InvalidConversationToast: typeof AnyViewClass;
LeftGroupToast: typeof AnyViewClass;
MaxAttachmentsToast: typeof AnyViewClass;
MessageBodyTooLongToast: typeof AnyViewClass;
OneNonImageAtATimeToast: typeof AnyViewClass;
OriginalNoLongerAvailableToast: typeof AnyViewClass;
OriginalNotFoundToast: typeof AnyViewClass;
PinnedConversationsFullToast: typeof AnyViewClass;
ReactionFailedToast: typeof AnyViewClass;
DeleteForEveryoneFailedToast: typeof AnyViewClass;
TapToViewExpiredIncomingToast: typeof AnyViewClass;
TapToViewExpiredOutgoingToast: typeof AnyViewClass;
TimerConflictToast: typeof AnyViewClass;
UnableToLoadToast: typeof AnyViewClass;
VoiceNoteLimit: typeof AnyViewClass;
VoiceNoteMustBeOnlyAttachmentToast: typeof AnyViewClass;
ClearDataView: typeof AnyViewClass;
ConversationLoadingScreen: typeof AnyViewClass;
GroupMemberList: typeof AnyViewClass;
@ -669,6 +633,5 @@ export type WhisperType = {
RecorderView: typeof AnyViewClass;
SafetyNumberChangeDialogView: typeof AnyViewClass;
StandaloneRegistrationView: typeof AnyViewClass;
ToastView: typeof AnyViewClass;
View: typeof AnyViewClass;
};