Removes ReactWrapperView
This commit is contained in:
parent
dec23725e5
commit
0b83ab497d
25 changed files with 444 additions and 396 deletions
|
@ -140,7 +140,6 @@ import { isValidUuid, UUIDKind, UUID } from './types/UUID';
|
|||
import * as log from './logging/log';
|
||||
import { loadRecentEmojis } from './util/loadRecentEmojis';
|
||||
import { deleteAllLogs } from './util/deleteAllLogs';
|
||||
import { ReactWrapperView } from './views/ReactWrapperView';
|
||||
import { ToastCaptchaFailed } from './components/ToastCaptchaFailed';
|
||||
import { ToastCaptchaSolved } from './components/ToastCaptchaSolved';
|
||||
import { showToast } from './util/showToast';
|
||||
|
@ -1267,30 +1266,6 @@ export async function startApp(): Promise<void> {
|
|||
window.reduxActions.user.userChanged({ menuOptions: options });
|
||||
});
|
||||
|
||||
let shortcutGuideView: ReactWrapperView | null = null;
|
||||
|
||||
window.showKeyboardShortcuts = () => {
|
||||
if (!shortcutGuideView) {
|
||||
shortcutGuideView = new ReactWrapperView({
|
||||
className: 'shortcut-guide-wrapper',
|
||||
JSX: window.Signal.State.Roots.createShortcutGuideModal(
|
||||
window.reduxStore,
|
||||
{
|
||||
close: () => {
|
||||
if (shortcutGuideView) {
|
||||
shortcutGuideView.remove();
|
||||
shortcutGuideView = null;
|
||||
}
|
||||
},
|
||||
}
|
||||
),
|
||||
onClose: () => {
|
||||
shortcutGuideView = null;
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', event => {
|
||||
const { ctrlKey, metaKey, shiftKey } = event;
|
||||
|
||||
|
@ -1309,7 +1284,7 @@ export async function startApp(): Promise<void> {
|
|||
// Show keyboard shortcuts - handled by Electron-managed keyboard shortcuts
|
||||
// However, on linux Ctrl+/ selects all text, so we prevent that
|
||||
if (commandOrCtrl && key === '/') {
|
||||
window.showKeyboardShortcuts();
|
||||
window.Events.showKeyboardShortcuts();
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
@ -1374,9 +1349,11 @@ export async function startApp(): Promise<void> {
|
|||
}
|
||||
|
||||
// Cancel out of keyboard shortcut screen - has first precedence
|
||||
if (shortcutGuideView && key === 'Escape') {
|
||||
shortcutGuideView.remove();
|
||||
shortcutGuideView = null;
|
||||
const isShortcutGuideModalVisible = window.reduxStore
|
||||
? window.reduxStore.getState().globalModals.isShortcutGuideModalVisible
|
||||
: false;
|
||||
if (isShortcutGuideModalVisible && key === 'Escape') {
|
||||
window.reduxActions.globalModals.closeShortcutGuideModal();
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return;
|
||||
|
|
|
@ -16,7 +16,6 @@ const i18n = setupI18n('en', enMessages);
|
|||
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||
title: text('title', overrideProps.title || ''),
|
||||
description: text('description', overrideProps.description || ''),
|
||||
buttonText: text('buttonText', overrideProps.buttonText || ''),
|
||||
i18n,
|
||||
onClose: action('onClick'),
|
||||
});
|
||||
|
@ -35,7 +34,6 @@ export function CustomStrings(): JSX.Element {
|
|||
{...createProps({
|
||||
title: 'Real bad!',
|
||||
description: 'Just avoid that next time, kay?',
|
||||
buttonText: 'Fine',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -8,7 +8,6 @@ import { Modal } from './Modal';
|
|||
import { Button, ButtonVariant } from './Button';
|
||||
|
||||
export type PropsType = {
|
||||
buttonText?: string;
|
||||
description?: string;
|
||||
title?: string;
|
||||
|
||||
|
@ -23,11 +22,11 @@ function focusRef(el: HTMLElement | null) {
|
|||
}
|
||||
|
||||
export function ErrorModal(props: PropsType): JSX.Element {
|
||||
const { buttonText, description, i18n, onClose, title } = props;
|
||||
const { description, i18n, onClose, title } = props;
|
||||
|
||||
const footer = (
|
||||
<Button onClick={onClose} ref={focusRef} variant={ButtonVariant.Secondary}>
|
||||
{buttonText || i18n('Confirmation--confirm')}
|
||||
{i18n('Confirmation--confirm')}
|
||||
</Button>
|
||||
);
|
||||
|
||||
|
|
|
@ -19,9 +19,18 @@ import { WhatsNewModal } from './WhatsNewModal';
|
|||
export type PropsType = {
|
||||
i18n: LocalizerType;
|
||||
theme: ThemeType;
|
||||
// AddUserToAnotherGroupModal
|
||||
addUserToAnotherGroupModalContactId?: string;
|
||||
renderAddUserToAnotherGroup: () => JSX.Element;
|
||||
// ContactModal
|
||||
contactModalState?: ContactModalStateType;
|
||||
renderContactModal: () => JSX.Element;
|
||||
// ErrorModal
|
||||
errorModalProps?: { description?: string; title?: string };
|
||||
renderErrorModal: (opts: {
|
||||
description?: string;
|
||||
title?: string;
|
||||
}) => JSX.Element;
|
||||
// ForwardMessageModal
|
||||
forwardMessageProps?: ForwardMessagePropsType;
|
||||
renderForwardMessageModal: () => JSX.Element;
|
||||
|
@ -31,9 +40,9 @@ export type PropsType = {
|
|||
// SafetyNumberModal
|
||||
safetyNumberModalContactId?: string;
|
||||
renderSafetyNumber: () => JSX.Element;
|
||||
// AddUserToAnotherGroupModal
|
||||
addUserToAnotherGroupModalContactId?: string;
|
||||
renderAddUserToAnotherGroup: () => JSX.Element;
|
||||
// ShortcutGuideModal
|
||||
isShortcutGuideModalVisible: boolean;
|
||||
renderShortcutGuideModal: () => JSX.Element;
|
||||
// SignalConnectionsModal
|
||||
isSignalConnectionsVisible: boolean;
|
||||
toggleSignalConnectionsModal: () => unknown;
|
||||
|
@ -57,9 +66,15 @@ export type PropsType = {
|
|||
|
||||
export function GlobalModalContainer({
|
||||
i18n,
|
||||
// AddUserToAnotherGroupModal
|
||||
addUserToAnotherGroupModalContactId,
|
||||
renderAddUserToAnotherGroup,
|
||||
// ContactModal
|
||||
contactModalState,
|
||||
renderContactModal,
|
||||
// ErrorModal
|
||||
errorModalProps,
|
||||
renderErrorModal,
|
||||
// ForwardMessageModal
|
||||
forwardMessageProps,
|
||||
renderForwardMessageModal,
|
||||
|
@ -69,9 +84,9 @@ export function GlobalModalContainer({
|
|||
// SafetyNumberModal
|
||||
safetyNumberModalContactId,
|
||||
renderSafetyNumber,
|
||||
// AddUserToAnotherGroupModal
|
||||
addUserToAnotherGroupModalContactId,
|
||||
renderAddUserToAnotherGroup,
|
||||
// ShortcutGuideModal
|
||||
isShortcutGuideModalVisible,
|
||||
renderShortcutGuideModal,
|
||||
// SignalConnectionsModal
|
||||
isSignalConnectionsVisible,
|
||||
toggleSignalConnectionsModal,
|
||||
|
@ -92,18 +107,66 @@ export function GlobalModalContainer({
|
|||
hideWhatsNewModal,
|
||||
isWhatsNewVisible,
|
||||
}: PropsType): JSX.Element | null {
|
||||
// We want the send anyway dialog to supersede most modals since this is an
|
||||
// immediate action the user needs to take.
|
||||
// We want the following dialogs to show in this order:
|
||||
// 1. Errors
|
||||
// 2. Safety Number Changes
|
||||
// 3. The Rest (in no particular order, but they're ordered alphabetically)
|
||||
|
||||
// Errors
|
||||
if (errorModalProps) {
|
||||
return renderErrorModal(errorModalProps);
|
||||
}
|
||||
|
||||
// Safety Number
|
||||
if (hasSafetyNumberChangeModal || safetyNumberChangedBlockingData) {
|
||||
return renderSendAnywayDialog();
|
||||
}
|
||||
|
||||
// The Rest
|
||||
|
||||
if (addUserToAnotherGroupModalContactId) {
|
||||
return renderAddUserToAnotherGroup();
|
||||
}
|
||||
|
||||
if (contactModalState) {
|
||||
return renderContactModal();
|
||||
}
|
||||
|
||||
if (forwardMessageProps) {
|
||||
return renderForwardMessageModal();
|
||||
}
|
||||
|
||||
if (isProfileEditorVisible) {
|
||||
return renderProfileEditor();
|
||||
}
|
||||
|
||||
if (isShortcutGuideModalVisible) {
|
||||
return renderShortcutGuideModal();
|
||||
}
|
||||
|
||||
if (isSignalConnectionsVisible) {
|
||||
return (
|
||||
<SignalConnectionsModal
|
||||
i18n={i18n}
|
||||
onClose={toggleSignalConnectionsModal}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isStoriesSettingsVisible) {
|
||||
return renderStoriesSettings();
|
||||
}
|
||||
|
||||
if (isWhatsNewVisible) {
|
||||
return <WhatsNewModal hideWhatsNewModal={hideWhatsNewModal} i18n={i18n} />;
|
||||
}
|
||||
|
||||
if (safetyNumberModalContactId) {
|
||||
return renderSafetyNumber();
|
||||
}
|
||||
|
||||
if (addUserToAnotherGroupModalContactId) {
|
||||
return renderAddUserToAnotherGroup();
|
||||
if (stickerPackPreviewId) {
|
||||
return renderStickerPreviewModal();
|
||||
}
|
||||
|
||||
if (userNotFoundModalState) {
|
||||
|
@ -135,38 +198,5 @@ export function GlobalModalContainer({
|
|||
);
|
||||
}
|
||||
|
||||
if (contactModalState) {
|
||||
return renderContactModal();
|
||||
}
|
||||
|
||||
if (isProfileEditorVisible) {
|
||||
return renderProfileEditor();
|
||||
}
|
||||
|
||||
if (isWhatsNewVisible) {
|
||||
return <WhatsNewModal hideWhatsNewModal={hideWhatsNewModal} i18n={i18n} />;
|
||||
}
|
||||
|
||||
if (forwardMessageProps) {
|
||||
return renderForwardMessageModal();
|
||||
}
|
||||
|
||||
if (isSignalConnectionsVisible) {
|
||||
return (
|
||||
<SignalConnectionsModal
|
||||
i18n={i18n}
|
||||
onClose={toggleSignalConnectionsModal}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isStoriesSettingsVisible) {
|
||||
return renderStoriesSettings();
|
||||
}
|
||||
|
||||
if (stickerPackPreviewId) {
|
||||
return renderStickerPreviewModal();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2019-2020 Signal Messenger, LLC
|
||||
// Copyright 2019-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
|
@ -9,14 +9,15 @@ import { ShortcutGuide } from './ShortcutGuide';
|
|||
export type PropsType = {
|
||||
hasInstalledStickers: boolean;
|
||||
platform: string;
|
||||
readonly close: () => unknown;
|
||||
readonly closeShortcutGuideModal: () => unknown;
|
||||
readonly i18n: LocalizerType;
|
||||
};
|
||||
|
||||
export const ShortcutGuideModal = React.memo(function ShortcutGuideModalInner(
|
||||
props: PropsType
|
||||
) {
|
||||
const { i18n, close, hasInstalledStickers, platform } = props;
|
||||
const { i18n, closeShortcutGuideModal, hasInstalledStickers, platform } =
|
||||
props;
|
||||
const [root, setRoot] = React.useState<HTMLElement | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
@ -34,10 +35,10 @@ export const ShortcutGuideModal = React.memo(function ShortcutGuideModalInner(
|
|||
<div className="module-shortcut-guide-modal">
|
||||
<div className="module-shortcut-guide-container">
|
||||
<ShortcutGuide
|
||||
close={closeShortcutGuideModal}
|
||||
hasInstalledStickers={hasInstalledStickers}
|
||||
platform={platform}
|
||||
close={close}
|
||||
i18n={i18n}
|
||||
platform={platform}
|
||||
/>
|
||||
</div>
|
||||
</div>,
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from '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'),
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'Components/ToastAlreadyGroupMember',
|
||||
};
|
||||
|
||||
export const _ToastAlreadyGroupMember = (): JSX.Element => (
|
||||
<ToastAlreadyGroupMember {...defaultProps} />
|
||||
);
|
||||
|
||||
_ToastAlreadyGroupMember.story = {
|
||||
name: 'ToastAlreadyGroupMember',
|
||||
};
|
|
@ -1,20 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { Toast } from './Toast';
|
||||
|
||||
type PropsType = {
|
||||
i18n: LocalizerType;
|
||||
onClose: () => unknown;
|
||||
};
|
||||
|
||||
export function ToastAlreadyGroupMember({
|
||||
i18n,
|
||||
onClose,
|
||||
}: PropsType): JSX.Element {
|
||||
return (
|
||||
<Toast onClose={onClose}>{i18n('GroupV2--join--already-in-group')}</Toast>
|
||||
);
|
||||
}
|
|
@ -51,6 +51,20 @@ AddingUserToGroup.args = {
|
|||
},
|
||||
};
|
||||
|
||||
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: {
|
||||
|
|
|
@ -45,6 +45,22 @@ export function ToastManager({
|
|||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.AlreadyGroupMember) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('GroupV2--join--already-in-group')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.AlreadyRequestedToJoin) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('GroupV2--join--already-awaiting-approval')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.Blocked) {
|
||||
return <Toast onClose={hideToast}>{i18n('unblockToSend')}</Toast>;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,19 @@
|
|||
// Copyright 2021-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
|
||||
import type { ConversationAttributesType } from '../model-types.d';
|
||||
import type { ConversationModel } from '../models/conversations';
|
||||
import type { PreJoinConversationType } from '../state/ducks/conversations';
|
||||
|
||||
import * as Bytes from '../Bytes';
|
||||
import * as Errors from '../types/errors';
|
||||
import * as log from '../logging/log';
|
||||
import { HTTPError } from '../textsecure/Errors';
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
import { ToastType } from '../types/Toast';
|
||||
import { UUIDKind } from '../types/UUID';
|
||||
import {
|
||||
applyNewAvatar,
|
||||
decryptGroupDescription,
|
||||
|
@ -12,25 +24,11 @@ import {
|
|||
LINK_VERSION_ERROR,
|
||||
parseGroupLink,
|
||||
} from '../groups';
|
||||
import * as Errors from '../types/errors';
|
||||
import { UUIDKind } from '../types/UUID';
|
||||
import * as Bytes from '../Bytes';
|
||||
import { longRunningTaskWrapper } from '../util/longRunningTaskWrapper';
|
||||
import { isGroupV1 } from '../util/whatTypeOfConversation';
|
||||
import { createGroupV2JoinModal } from '../state/roots/createGroupV2JoinModal';
|
||||
import { explodePromise } from '../util/explodePromise';
|
||||
|
||||
import type { ConversationAttributesType } from '../model-types.d';
|
||||
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 { ReactWrapperView } from '../views/ReactWrapperView';
|
||||
import { ErrorModal } from '../components/ErrorModal';
|
||||
import { ToastAlreadyGroupMember } from '../components/ToastAlreadyGroupMember';
|
||||
import { ToastAlreadyRequestedToJoin } from '../components/ToastAlreadyRequestedToJoin';
|
||||
import { HTTPError } from '../textsecure/Errors';
|
||||
import { isAccessControlEnabled } from './util';
|
||||
import { isGroupV1 } from '../util/whatTypeOfConversation';
|
||||
import { longRunningTaskWrapper } from '../util/longRunningTaskWrapper';
|
||||
import { sleep } from '../util/sleep';
|
||||
|
||||
export async function joinViaLink(hash: string): Promise<void> {
|
||||
|
@ -43,15 +41,15 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
log.error(`joinViaLink: Failed to parse group link ${errorString}`);
|
||||
|
||||
if (error instanceof Error && error.name === LINK_VERSION_ERROR) {
|
||||
showErrorDialog(
|
||||
window.i18n('GroupV2--join--unknown-link-version'),
|
||||
window.i18n('GroupV2--join--unknown-link-version--title')
|
||||
);
|
||||
window.reduxActions.globalModals.showErrorModal({
|
||||
description: window.i18n('GroupV2--join--unknown-link-version'),
|
||||
title: window.i18n('GroupV2--join--unknown-link-version--title'),
|
||||
});
|
||||
} else {
|
||||
showErrorDialog(
|
||||
window.i18n('GroupV2--join--invalid-link'),
|
||||
window.i18n('GroupV2--join--invalid-link--title')
|
||||
);
|
||||
window.reduxActions.globalModals.showErrorModal({
|
||||
description: window.i18n('GroupV2--join--invalid-link'),
|
||||
title: window.i18n('GroupV2--join--invalid-link--title'),
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -74,7 +72,7 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
window.reduxActions.conversations.showConversation({
|
||||
conversationId: existingConversation.id,
|
||||
});
|
||||
showToast(ToastAlreadyGroupMember);
|
||||
window.reduxActions.toast.showToast(ToastType.AlreadyGroupMember);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -99,20 +97,20 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
error instanceof HTTPError &&
|
||||
error.responseHeaders['x-signal-forbidden-reason']
|
||||
) {
|
||||
showErrorDialog(
|
||||
window.i18n('GroupV2--join--link-forbidden'),
|
||||
window.i18n('GroupV2--join--link-forbidden--title')
|
||||
);
|
||||
window.reduxActions.globalModals.showErrorModal({
|
||||
description: window.i18n('GroupV2--join--link-forbidden'),
|
||||
title: window.i18n('GroupV2--join--link-forbidden--title'),
|
||||
});
|
||||
} else if (error instanceof HTTPError && error.code === 403) {
|
||||
showErrorDialog(
|
||||
window.i18n('GroupV2--join--link-revoked'),
|
||||
window.i18n('GroupV2--join--link-revoked--title')
|
||||
);
|
||||
window.reduxActions.globalModals.showErrorModal({
|
||||
description: window.i18n('GroupV2--join--link-revoked'),
|
||||
title: window.i18n('GroupV2--join--link-revoked--title'),
|
||||
});
|
||||
} else {
|
||||
showErrorDialog(
|
||||
window.i18n('GroupV2--join--general-join-failure'),
|
||||
window.i18n('GroupV2--join--general-join-failure--title')
|
||||
);
|
||||
window.reduxActions.globalModals.showErrorModal({
|
||||
description: window.i18n('GroupV2--join--general-join-failure'),
|
||||
title: window.i18n('GroupV2--join--general-join-failure--title'),
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -121,10 +119,10 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
log.error(
|
||||
`joinViaLink/${logId}: addFromInviteLink value of ${result.addFromInviteLink} is invalid`
|
||||
);
|
||||
showErrorDialog(
|
||||
window.i18n('GroupV2--join--link-revoked'),
|
||||
window.i18n('GroupV2--join--link-revoked--title')
|
||||
);
|
||||
window.reduxActions.globalModals.showErrorModal({
|
||||
description: window.i18n('GroupV2--join--link-revoked'),
|
||||
title: window.i18n('GroupV2--join--link-revoked--title'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -168,7 +166,7 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
conversationId: existingConversation.id,
|
||||
});
|
||||
|
||||
showToast(ToastAlreadyRequestedToJoin);
|
||||
window.reduxActions.toast.showToast(ToastType.AlreadyRequestedToJoin);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -202,9 +200,9 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
|
||||
const closeDialog = async () => {
|
||||
try {
|
||||
if (groupV2InfoDialog) {
|
||||
groupV2InfoDialog.remove();
|
||||
groupV2InfoDialog = undefined;
|
||||
if (groupV2InfoNode) {
|
||||
unmountComponentAtNode(groupV2InfoNode);
|
||||
groupV2InfoNode = undefined;
|
||||
}
|
||||
|
||||
window.reduxActions.conversations.setPreJoinConversation(undefined);
|
||||
|
@ -220,9 +218,9 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
|
||||
const join = async () => {
|
||||
try {
|
||||
if (groupV2InfoDialog) {
|
||||
groupV2InfoDialog.remove();
|
||||
groupV2InfoDialog = undefined;
|
||||
if (groupV2InfoNode) {
|
||||
unmountComponentAtNode(groupV2InfoNode);
|
||||
groupV2InfoNode = undefined;
|
||||
}
|
||||
|
||||
window.reduxActions.conversations.setPreJoinConversation(undefined);
|
||||
|
@ -375,13 +373,13 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
|
||||
log.info(`joinViaLink/${logId}: Showing modal`);
|
||||
|
||||
let groupV2InfoDialog: Backbone.View | undefined = new ReactWrapperView({
|
||||
className: 'group-v2-join-dialog-wrapper',
|
||||
JSX: window.Signal.State.Roots.createGroupV2JoinModal(window.reduxStore, {
|
||||
join,
|
||||
onClose: closeDialog,
|
||||
}),
|
||||
});
|
||||
let groupV2InfoNode: HTMLDivElement | undefined =
|
||||
document.createElement('div');
|
||||
|
||||
render(
|
||||
createGroupV2JoinModal(window.reduxStore, { join, onClose: closeDialog }),
|
||||
groupV2InfoNode
|
||||
);
|
||||
|
||||
// We declare a new function here so we can await but not block
|
||||
const fetchAvatar = async () => {
|
||||
|
@ -405,7 +403,7 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
};
|
||||
|
||||
// Dialog has been dismissed; we'll delete the unneeeded avatar
|
||||
if (!groupV2InfoDialog) {
|
||||
if (!groupV2InfoNode) {
|
||||
await window.Signal.Migrations.deleteAttachmentData(
|
||||
attributes.avatar.path
|
||||
);
|
||||
|
@ -426,19 +424,3 @@ export async function joinViaLink(hash: string): Promise<void> {
|
|||
|
||||
await promise;
|
||||
}
|
||||
|
||||
function showErrorDialog(description: string, title: string) {
|
||||
const errorView = new ReactWrapperView({
|
||||
className: 'error-modal-wrapper',
|
||||
JSX: (
|
||||
<ErrorModal
|
||||
i18n={window.i18n}
|
||||
title={title}
|
||||
description={description}
|
||||
onClose={() => {
|
||||
errorView.remove();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
|
@ -25,9 +25,7 @@ import { DisappearingTimeDialog } from './components/DisappearingTimeDialog';
|
|||
|
||||
// State
|
||||
import { createApp } from './state/roots/createApp';
|
||||
import { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal';
|
||||
import { createSafetyNumberViewer } from './state/roots/createSafetyNumberViewer';
|
||||
import { createShortcutGuideModal } from './state/roots/createShortcutGuideModal';
|
||||
|
||||
import { createStore } from './state/createStore';
|
||||
import * as appDuck from './state/ducks/app';
|
||||
|
@ -393,9 +391,7 @@ export const setup = (options: {
|
|||
|
||||
const Roots = {
|
||||
createApp,
|
||||
createGroupV2JoinModal,
|
||||
createSafetyNumberViewer,
|
||||
createShortcutGuideModal,
|
||||
};
|
||||
|
||||
const Ducks = {
|
||||
|
|
|
@ -41,10 +41,15 @@ type MigrateToGV2PropsType = {
|
|||
export type GlobalModalsStateType = Readonly<{
|
||||
addUserToAnotherGroupModalContactId?: string;
|
||||
contactModalState?: ContactModalStateType;
|
||||
errorModalProps?: {
|
||||
description?: string;
|
||||
title?: string;
|
||||
};
|
||||
forwardMessageProps?: ForwardMessagePropsType;
|
||||
gv2MigrationProps?: MigrateToGV2PropsType;
|
||||
isProfileEditorVisible: boolean;
|
||||
isSignalConnectionsVisible: boolean;
|
||||
isShortcutGuideModalVisible: boolean;
|
||||
isStoriesSettingsVisible: boolean;
|
||||
isWhatsNewVisible: boolean;
|
||||
profileEditorHasError: boolean;
|
||||
|
@ -80,6 +85,10 @@ const SHOW_GV2_MIGRATION_DIALOG = 'globalModals/SHOW_GV2_MIGRATION_DIALOG';
|
|||
const CLOSE_GV2_MIGRATION_DIALOG = 'globalModals/CLOSE_GV2_MIGRATION_DIALOG';
|
||||
const SHOW_STICKER_PACK_PREVIEW = 'globalModals/SHOW_STICKER_PACK_PREVIEW';
|
||||
const CLOSE_STICKER_PACK_PREVIEW = 'globalModals/CLOSE_STICKER_PACK_PREVIEW';
|
||||
const CLOSE_ERROR_MODAL = 'globalModals/CLOSE_ERROR_MODAL';
|
||||
const SHOW_ERROR_MODAL = 'globalModals/SHOW_ERROR_MODAL';
|
||||
const CLOSE_SHORTCUT_GUIDE_MODAL = 'globalModals/CLOSE_SHORTCUT_GUIDE_MODAL';
|
||||
const SHOW_SHORTCUT_GUIDE_MODAL = 'globalModals/SHOW_SHORTCUT_GUIDE_MODAL';
|
||||
|
||||
export type ContactModalStateType = {
|
||||
contactId: string;
|
||||
|
@ -186,6 +195,26 @@ type CloseStickerPackPreviewActionType = {
|
|||
type: typeof CLOSE_STICKER_PACK_PREVIEW;
|
||||
};
|
||||
|
||||
type CloseErrorModalActionType = {
|
||||
type: typeof CLOSE_ERROR_MODAL;
|
||||
};
|
||||
|
||||
type ShowErrorModalActionType = {
|
||||
type: typeof SHOW_ERROR_MODAL;
|
||||
payload: {
|
||||
description?: string;
|
||||
title?: string;
|
||||
};
|
||||
};
|
||||
|
||||
type CloseShortcutGuideModalActionType = {
|
||||
type: typeof CLOSE_SHORTCUT_GUIDE_MODAL;
|
||||
};
|
||||
|
||||
type ShowShortcutGuideModalActionType = {
|
||||
type: typeof SHOW_SHORTCUT_GUIDE_MODAL;
|
||||
};
|
||||
|
||||
export type GlobalModalsActionType =
|
||||
| StartMigrationToGV2ActionType
|
||||
| CloseGV2MigrationDialogActionType
|
||||
|
@ -201,6 +230,10 @@ export type GlobalModalsActionType =
|
|||
| ShowSendAnywayDialogActionType
|
||||
| CloseStickerPackPreviewActionType
|
||||
| ShowStickerPackPreviewActionType
|
||||
| CloseErrorModalActionType
|
||||
| ShowErrorModalActionType
|
||||
| CloseShortcutGuideModalActionType
|
||||
| ShowShortcutGuideModalActionType
|
||||
| ToggleForwardMessageModalActionType
|
||||
| ToggleProfileEditorActionType
|
||||
| ToggleProfileEditorErrorActionType
|
||||
|
@ -231,6 +264,10 @@ export const actions = {
|
|||
closeGV2MigrationDialog,
|
||||
showStickerPackPreview,
|
||||
closeStickerPackPreview,
|
||||
closeErrorModal,
|
||||
showErrorModal,
|
||||
closeShortcutGuideModal,
|
||||
showShortcutGuideModal,
|
||||
};
|
||||
|
||||
export const useGlobalModalActions = (): BoundActionCreatorsMapObject<
|
||||
|
@ -467,11 +504,46 @@ export function showStickerPackPreview(
|
|||
};
|
||||
}
|
||||
|
||||
function closeErrorModal(): CloseErrorModalActionType {
|
||||
return {
|
||||
type: CLOSE_ERROR_MODAL,
|
||||
};
|
||||
}
|
||||
|
||||
function showErrorModal({
|
||||
description,
|
||||
title,
|
||||
}: {
|
||||
title?: string;
|
||||
description?: string;
|
||||
}): ShowErrorModalActionType {
|
||||
return {
|
||||
type: SHOW_ERROR_MODAL,
|
||||
payload: {
|
||||
description,
|
||||
title,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function closeShortcutGuideModal(): CloseShortcutGuideModalActionType {
|
||||
return {
|
||||
type: CLOSE_SHORTCUT_GUIDE_MODAL,
|
||||
};
|
||||
}
|
||||
|
||||
function showShortcutGuideModal(): ShowShortcutGuideModalActionType {
|
||||
return {
|
||||
type: SHOW_SHORTCUT_GUIDE_MODAL,
|
||||
};
|
||||
}
|
||||
|
||||
// Reducer
|
||||
|
||||
export function getEmptyState(): GlobalModalsStateType {
|
||||
return {
|
||||
isProfileEditorVisible: false,
|
||||
isShortcutGuideModalVisible: false,
|
||||
isSignalConnectionsVisible: false,
|
||||
isStoriesSettingsVisible: false,
|
||||
isWhatsNewVisible: false,
|
||||
|
@ -616,5 +688,33 @@ export function reducer(
|
|||
};
|
||||
}
|
||||
|
||||
if (action.type === CLOSE_ERROR_MODAL) {
|
||||
return {
|
||||
...state,
|
||||
errorModalProps: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === SHOW_ERROR_MODAL) {
|
||||
return {
|
||||
...state,
|
||||
errorModalProps: action.payload,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === CLOSE_SHORTCUT_GUIDE_MODAL) {
|
||||
return {
|
||||
...state,
|
||||
isShortcutGuideModalVisible: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === SHOW_SHORTCUT_GUIDE_MODAL) {
|
||||
return {
|
||||
...state,
|
||||
isShortcutGuideModalVisible: true,
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// Copyright 2021-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// TODO DESKTOP-4761
|
||||
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright 2019-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import type { Store } from 'redux';
|
||||
|
||||
import type { ExternalProps } from '../smart/ShortcutGuideModal';
|
||||
import { SmartShortcutGuideModal } from '../smart/ShortcutGuideModal';
|
||||
|
||||
export const createShortcutGuideModal = (
|
||||
store: Store,
|
||||
props: ExternalProps
|
||||
): React.ReactElement => (
|
||||
<Provider store={store}>
|
||||
<SmartShortcutGuideModal {...props} />
|
||||
</Provider>
|
||||
);
|
|
@ -1,22 +1,25 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import type { GlobalModalsStateType } from '../ducks/globalModals';
|
||||
import type { StateType } from '../reducer';
|
||||
import { ErrorModal } from '../../components/ErrorModal';
|
||||
import { GlobalModalContainer } from '../../components/GlobalModalContainer';
|
||||
import { SmartAddUserToAnotherGroupModal } from './AddUserToAnotherGroupModal';
|
||||
import { SmartContactModal } from './ContactModal';
|
||||
import { SmartForwardMessageModal } from './ForwardMessageModal';
|
||||
import { SmartProfileEditorModal } from './ProfileEditorModal';
|
||||
import { SmartSafetyNumberModal } from './SafetyNumberModal';
|
||||
import { SmartSendAnywayDialog } from './SendAnywayDialog';
|
||||
import { SmartShortcutGuideModal } from './ShortcutGuideModal';
|
||||
import { SmartStickerPreviewModal } from './StickerPreviewModal';
|
||||
import { SmartStoriesSettingsModal } from './StoriesSettingsModal';
|
||||
import { getConversationsStoppingSend } from '../selectors/conversations';
|
||||
import { mapDispatchToProps } from '../actions';
|
||||
|
||||
import { getIntl, getTheme } from '../selectors/user';
|
||||
import { SmartAddUserToAnotherGroupModal } from './AddUserToAnotherGroupModal';
|
||||
import { SmartStickerPreviewModal } from './StickerPreviewModal';
|
||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||
|
||||
function renderProfileEditor(): JSX.Element {
|
||||
return <SmartProfileEditorModal />;
|
||||
|
@ -38,42 +41,95 @@ function renderSendAnywayDialog(): JSX.Element {
|
|||
return <SmartSendAnywayDialog />;
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: StateType) => {
|
||||
const i18n = getIntl(state);
|
||||
function renderShortcutGuideModal(): JSX.Element {
|
||||
return <SmartShortcutGuideModal />;
|
||||
}
|
||||
|
||||
const { stickerPackPreviewId } = state.globalModals;
|
||||
export function SmartGlobalModalContainer(): JSX.Element {
|
||||
const conversationsStoppingSend = useSelector(getConversationsStoppingSend);
|
||||
const i18n = useSelector(getIntl);
|
||||
const theme = useSelector(getTheme);
|
||||
|
||||
return {
|
||||
...state.globalModals,
|
||||
hasSafetyNumberChangeModal: getConversationsStoppingSend(state).length > 0,
|
||||
i18n,
|
||||
theme: getTheme(state),
|
||||
renderContactModal,
|
||||
renderForwardMessageModal,
|
||||
renderProfileEditor,
|
||||
renderStoriesSettings,
|
||||
renderStickerPreviewModal: () =>
|
||||
const hasSafetyNumberChangeModal = conversationsStoppingSend.length > 0;
|
||||
|
||||
const {
|
||||
addUserToAnotherGroupModalContactId,
|
||||
isProfileEditorVisible,
|
||||
isSignalConnectionsVisible,
|
||||
isShortcutGuideModalVisible,
|
||||
isStoriesSettingsVisible,
|
||||
isWhatsNewVisible,
|
||||
safetyNumberModalContactId,
|
||||
stickerPackPreviewId,
|
||||
} = useSelector<StateType, GlobalModalsStateType>(
|
||||
state => state.globalModals
|
||||
);
|
||||
|
||||
const {
|
||||
closeErrorModal,
|
||||
hideWhatsNewModal,
|
||||
hideUserNotFoundModal,
|
||||
toggleSignalConnectionsModal,
|
||||
} = useGlobalModalActions();
|
||||
|
||||
const renderAddUserToAnotherGroup = useCallback(() => {
|
||||
return (
|
||||
<SmartAddUserToAnotherGroupModal
|
||||
contactID={String(addUserToAnotherGroupModalContactId)}
|
||||
/>
|
||||
);
|
||||
}, [addUserToAnotherGroupModalContactId]);
|
||||
|
||||
const renderSafetyNumber = useCallback(
|
||||
() => (
|
||||
<SmartSafetyNumberModal contactID={String(safetyNumberModalContactId)} />
|
||||
),
|
||||
[safetyNumberModalContactId]
|
||||
);
|
||||
|
||||
const renderStickerPreviewModal = useCallback(
|
||||
() =>
|
||||
stickerPackPreviewId ? (
|
||||
<SmartStickerPreviewModal packId={stickerPackPreviewId} />
|
||||
) : null,
|
||||
renderSafetyNumber: () => (
|
||||
<SmartSafetyNumberModal
|
||||
contactID={String(state.globalModals.safetyNumberModalContactId)}
|
||||
[stickerPackPreviewId]
|
||||
);
|
||||
|
||||
const renderErrorModal = useCallback(
|
||||
({ description, title }: { description?: string; title?: string }) => (
|
||||
<ErrorModal
|
||||
title={title}
|
||||
description={description}
|
||||
i18n={i18n}
|
||||
onClose={closeErrorModal}
|
||||
/>
|
||||
),
|
||||
renderAddUserToAnotherGroup: () => {
|
||||
return (
|
||||
<SmartAddUserToAnotherGroupModal
|
||||
contactID={String(
|
||||
state.globalModals.addUserToAnotherGroupModalContactId
|
||||
)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
renderSendAnywayDialog,
|
||||
};
|
||||
};
|
||||
[closeErrorModal, i18n]
|
||||
);
|
||||
|
||||
const smart = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
||||
export const SmartGlobalModalContainer = smart(GlobalModalContainer);
|
||||
return (
|
||||
<GlobalModalContainer
|
||||
hasSafetyNumberChangeModal={hasSafetyNumberChangeModal}
|
||||
hideUserNotFoundModal={hideUserNotFoundModal}
|
||||
hideWhatsNewModal={hideWhatsNewModal}
|
||||
i18n={i18n}
|
||||
isProfileEditorVisible={isProfileEditorVisible}
|
||||
isShortcutGuideModalVisible={isShortcutGuideModalVisible}
|
||||
isSignalConnectionsVisible={isSignalConnectionsVisible}
|
||||
isStoriesSettingsVisible={isStoriesSettingsVisible}
|
||||
isWhatsNewVisible={isWhatsNewVisible}
|
||||
renderAddUserToAnotherGroup={renderAddUserToAnotherGroup}
|
||||
renderContactModal={renderContactModal}
|
||||
renderErrorModal={renderErrorModal}
|
||||
renderForwardMessageModal={renderForwardMessageModal}
|
||||
renderProfileEditor={renderProfileEditor}
|
||||
renderSafetyNumber={renderSafetyNumber}
|
||||
renderSendAnywayDialog={renderSendAnywayDialog}
|
||||
renderShortcutGuideModal={renderShortcutGuideModal}
|
||||
renderStickerPreviewModal={renderStickerPreviewModal}
|
||||
renderStoriesSettings={renderStoriesSettings}
|
||||
theme={theme}
|
||||
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,13 +15,7 @@ import {
|
|||
getReceivedStickerPacks,
|
||||
} from '../selectors/stickers';
|
||||
|
||||
export type ExternalProps = {
|
||||
close: () => unknown;
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
||||
const { close } = props;
|
||||
|
||||
const mapStateToProps = (state: StateType) => {
|
||||
const blessedPacks = getBlessedStickerPacks(state);
|
||||
const installedPacks = getInstalledStickerPacks(state);
|
||||
const knownPacks = getKnownStickerPacks(state);
|
||||
|
@ -38,7 +32,6 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
|||
const platform = getPlatform(state);
|
||||
|
||||
return {
|
||||
close,
|
||||
hasInstalledStickers,
|
||||
platform,
|
||||
i18n: getIntl(state),
|
||||
|
|
|
@ -18,7 +18,7 @@ import type { LoggerType } from '../../types/Logging';
|
|||
|
||||
import { JobQueue } from '../../jobs/JobQueue';
|
||||
import type { ParsedJob, StoredJob, JobQueueStore } from '../../jobs/types';
|
||||
import { sleep } from '../../util';
|
||||
import { sleep } from '../../util/sleep';
|
||||
|
||||
describe('JobQueue', () => {
|
||||
describe('end-to-end tests', () => {
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
export enum ToastType {
|
||||
AddingUserToGroup = 'AddingUserToGroup',
|
||||
AlreadyGroupMember = 'AlreadyGroupMember',
|
||||
AlreadyRequestedToJoin = 'AlreadyRequestedToJoin',
|
||||
Blocked = 'Blocked',
|
||||
BlockedGroup = 'BlockedGroup',
|
||||
CannotMixMultiAndNonMultiAttachments = 'CannotMixMultiAndNonMultiAttachments',
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
import { webFrame } from 'electron';
|
||||
import type { AudioDevice } from 'ringrtc';
|
||||
import * as React from 'react';
|
||||
import { noop } from 'lodash';
|
||||
import { getStoriesAvailable } from './stories';
|
||||
|
||||
|
@ -19,9 +18,6 @@ import * as Stickers from '../types/Stickers';
|
|||
import type { SystemTraySetting } from '../types/SystemTraySetting';
|
||||
import { parseSystemTraySetting } from '../types/SystemTraySetting';
|
||||
|
||||
import { ReactWrapperView } from '../views/ReactWrapperView';
|
||||
import { ErrorModal } from '../components/ErrorModal';
|
||||
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import { calling } from '../services/calling';
|
||||
import { getConversationsWithCustomColorSelector } from '../state/selectors/conversations';
|
||||
|
@ -422,7 +418,8 @@ export function createIPCEvents(
|
|||
elem.remove();
|
||||
}
|
||||
},
|
||||
showKeyboardShortcuts: () => window.showKeyboardShortcuts(),
|
||||
showKeyboardShortcuts: () =>
|
||||
window.reduxActions.globalModals.showShortcutGuideModal(),
|
||||
|
||||
deleteAllData: async () => {
|
||||
await window.Signal.Data.goBackToMainProcess();
|
||||
|
@ -455,18 +452,9 @@ export function createIPCEvents(
|
|||
'showGroupViaLink: Ran into an error!',
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
const errorView = new ReactWrapperView({
|
||||
className: 'error-modal-wrapper',
|
||||
JSX: (
|
||||
<ErrorModal
|
||||
i18n={window.i18n}
|
||||
title={window.i18n('GroupV2--join--general-join-failure--title')}
|
||||
description={window.i18n('GroupV2--join--general-join-failure')}
|
||||
onClose={() => {
|
||||
errorView.remove();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
window.reduxActions.globalModals.showErrorModal({
|
||||
title: window.i18n('GroupV2--join--general-join-failure--title'),
|
||||
description: window.i18n('GroupV2--join--general-join-failure'),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -549,16 +537,7 @@ export function createIPCEvents(
|
|||
}
|
||||
|
||||
function showUnknownSgnlLinkModal(): void {
|
||||
const errorView = new ReactWrapperView({
|
||||
className: 'error-modal-wrapper',
|
||||
JSX: (
|
||||
<ErrorModal
|
||||
i18n={window.i18n}
|
||||
description={window.i18n('unknown-sgnl-link')}
|
||||
onClose={() => {
|
||||
errorView.remove();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
window.reduxActions.globalModals.showErrorModal({
|
||||
description: window.i18n('unknown-sgnl-link'),
|
||||
});
|
||||
}
|
|
@ -9397,7 +9397,7 @@
|
|||
},
|
||||
{
|
||||
"rule": "jQuery-prepend(",
|
||||
"path": "ts/util/createIPCEvents.tsx",
|
||||
"path": "ts/util/createIPCEvents.ts",
|
||||
"line": " document.body.prepend(newOverlay);",
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2022-11-30T00:14:31.394Z"
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright 2021-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
import { ReactWrapperView } from '../views/ReactWrapperView';
|
||||
import { ErrorModal } from '../components/ErrorModal';
|
||||
import { ProgressModal } from '../components/ProgressModal';
|
||||
import * as log from '../logging/log';
|
||||
import React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
|
||||
import * as Errors from '../types/errors';
|
||||
import * as log from '../logging/log';
|
||||
import { ProgressModal } from '../components/ProgressModal';
|
||||
import { clearTimeoutIfNecessary } from './clearTimeoutIfNecessary';
|
||||
|
||||
export async function longRunningTaskWrapper<T>({
|
||||
|
@ -24,17 +24,13 @@ export async function longRunningTaskWrapper<T>({
|
|||
const ONE_SECOND = 1000;
|
||||
const TWO_SECONDS = 2000;
|
||||
|
||||
let progressView: Backbone.View | undefined;
|
||||
let progressNode: HTMLDivElement | undefined;
|
||||
let spinnerStart;
|
||||
let progressTimeout: NodeJS.Timeout | undefined = setTimeout(() => {
|
||||
log.info(`longRunningTaskWrapper/${idLog}: Creating spinner`);
|
||||
progressNode = document.createElement('div');
|
||||
|
||||
// Note: this component uses a portal to render itself into the top-level DOM. No
|
||||
// need to attach it to the DOM here.
|
||||
progressView = new ReactWrapperView({
|
||||
className: 'progress-modal-wrapper',
|
||||
JSX: <ProgressModal i18n={window.i18n} />,
|
||||
});
|
||||
log.info(`longRunningTaskWrapper/${idLog}: Creating spinner`);
|
||||
render(<ProgressModal i18n={window.i18n} />, progressNode);
|
||||
spinnerStart = Date.now();
|
||||
}, TWO_SECONDS);
|
||||
|
||||
|
@ -47,7 +43,7 @@ export async function longRunningTaskWrapper<T>({
|
|||
|
||||
clearTimeoutIfNecessary(progressTimeout);
|
||||
progressTimeout = undefined;
|
||||
if (progressView) {
|
||||
if (progressNode) {
|
||||
const now = Date.now();
|
||||
if (spinnerStart && now - spinnerStart < ONE_SECOND) {
|
||||
log.info(
|
||||
|
@ -55,8 +51,8 @@ export async function longRunningTaskWrapper<T>({
|
|||
);
|
||||
await window.Signal.Util.sleep(ONE_SECOND);
|
||||
}
|
||||
progressView.remove();
|
||||
progressView = undefined;
|
||||
unmountComponentAtNode(progressNode);
|
||||
progressNode = undefined;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -68,27 +64,14 @@ export async function longRunningTaskWrapper<T>({
|
|||
|
||||
clearTimeoutIfNecessary(progressTimeout);
|
||||
progressTimeout = undefined;
|
||||
if (progressView) {
|
||||
progressView.remove();
|
||||
progressView = undefined;
|
||||
if (progressNode) {
|
||||
unmountComponentAtNode(progressNode);
|
||||
progressNode = undefined;
|
||||
}
|
||||
|
||||
if (!suppressErrorDialog) {
|
||||
log.info(`longRunningTaskWrapper/${idLog}: Showing error dialog`);
|
||||
|
||||
// Note: this component uses a portal to render itself into the top-level DOM. No
|
||||
// need to attach it to the DOM here.
|
||||
const errorView: Backbone.View = new ReactWrapperView({
|
||||
className: 'error-modal-wrapper',
|
||||
JSX: (
|
||||
<ErrorModal
|
||||
i18n={window.i18n}
|
||||
onClose={() => {
|
||||
errorView.remove();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
});
|
||||
window.reduxActions.globalModals.showErrorModal({});
|
||||
}
|
||||
|
||||
throw error;
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
import React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
|
||||
import type { ToastAlreadyGroupMember } from '../components/ToastAlreadyGroupMember';
|
||||
import type { ToastAlreadyRequestedToJoin } from '../components/ToastAlreadyRequestedToJoin';
|
||||
import type { ToastCaptchaFailed } from '../components/ToastCaptchaFailed';
|
||||
import type { ToastCaptchaSolved } from '../components/ToastCaptchaSolved';
|
||||
import type {
|
||||
|
@ -24,8 +22,6 @@ import type { ToastStickerPackInstallFailed } from '../components/ToastStickerPa
|
|||
import type { ToastVoiceNoteLimit } from '../components/ToastVoiceNoteLimit';
|
||||
import type { ToastVoiceNoteMustBeOnlyAttachment } from '../components/ToastVoiceNoteMustBeOnlyAttachment';
|
||||
|
||||
export function showToast(Toast: typeof ToastAlreadyGroupMember): void;
|
||||
export function showToast(Toast: typeof ToastAlreadyRequestedToJoin): void;
|
||||
export function showToast(Toast: typeof ToastCaptchaFailed): void;
|
||||
export function showToast(Toast: typeof ToastCaptchaSolved): void;
|
||||
export function showToast(
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
// Copyright 2018-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ReactElement } from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import * as Backbone from 'backbone';
|
||||
|
||||
export class ReactWrapperView extends Backbone.View {
|
||||
private readonly onClose?: () => unknown;
|
||||
private JSX: ReactElement;
|
||||
|
||||
constructor({
|
||||
className,
|
||||
onClose,
|
||||
JSX,
|
||||
}: Readonly<{
|
||||
className?: string;
|
||||
onClose?: () => unknown;
|
||||
JSX: ReactElement;
|
||||
}>) {
|
||||
super();
|
||||
|
||||
this.className = className ?? 'react-wrapper';
|
||||
this.JSX = JSX;
|
||||
this.onClose = onClose;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
update(JSX: ReactElement): void {
|
||||
this.JSX = JSX;
|
||||
this.render();
|
||||
}
|
||||
|
||||
override render(): this {
|
||||
this.el.className = this.className;
|
||||
ReactDOM.render(this.JSX, this.el);
|
||||
return this;
|
||||
}
|
||||
|
||||
override remove(): this {
|
||||
if (this.onClose) {
|
||||
this.onClose();
|
||||
}
|
||||
ReactDOM.unmountComponentAtNode(this.el);
|
||||
Backbone.View.prototype.remove.call(this);
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,16 +1,17 @@
|
|||
// Copyright 2020-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
/* eslint-disable camelcase, max-classes-per-file */
|
||||
|
||||
import type * as Backbone from 'backbone';
|
||||
import type { ReactElement } from 'react';
|
||||
import * as Backbone from 'backbone';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { render } from 'mustache';
|
||||
|
||||
import type { ConversationModel } from '../models/conversations';
|
||||
import { getMessageById } from '../messages/getMessageById';
|
||||
import { strictAssert } from '../util/assert';
|
||||
import { isGroup } from '../util/whatTypeOfConversation';
|
||||
import { ReactWrapperView } from './ReactWrapperView';
|
||||
import * as log from '../logging/log';
|
||||
import { createConversationView } from '../state/roots/createConversationView';
|
||||
import {
|
||||
|
@ -197,4 +198,47 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
}
|
||||
}
|
||||
|
||||
class ReactWrapperView extends Backbone.View {
|
||||
private readonly onClose?: () => unknown;
|
||||
private JSX: ReactElement;
|
||||
|
||||
constructor({
|
||||
className,
|
||||
onClose,
|
||||
JSX,
|
||||
}: Readonly<{
|
||||
className?: string;
|
||||
onClose?: () => unknown;
|
||||
JSX: ReactElement;
|
||||
}>) {
|
||||
super();
|
||||
|
||||
this.className = className ?? 'react-wrapper';
|
||||
this.JSX = JSX;
|
||||
this.onClose = onClose;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
update(JSX: ReactElement): void {
|
||||
this.JSX = JSX;
|
||||
this.render();
|
||||
}
|
||||
|
||||
override render(): this {
|
||||
this.el.className = this.className;
|
||||
ReactDOM.render(this.JSX, this.el);
|
||||
return this;
|
||||
}
|
||||
|
||||
override remove(): this {
|
||||
if (this.onClose) {
|
||||
this.onClose();
|
||||
}
|
||||
ReactDOM.unmountComponentAtNode(this.el);
|
||||
Backbone.View.prototype.remove.call(this);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
window.Whisper.ConversationView = ConversationView;
|
||||
|
|
4
ts/window.d.ts
vendored
4
ts/window.d.ts
vendored
|
@ -37,9 +37,7 @@ import type { ConversationController } from './ConversationController';
|
|||
import type { ReduxActions } from './state/types';
|
||||
import type { createStore } from './state/createStore';
|
||||
import type { createApp } from './state/roots/createApp';
|
||||
import type { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal';
|
||||
import type { createSafetyNumberViewer } from './state/roots/createSafetyNumberViewer';
|
||||
import type { createShortcutGuideModal } from './state/roots/createShortcutGuideModal';
|
||||
import type * as appDuck from './state/ducks/app';
|
||||
import type * as callingDuck from './state/ducks/calling';
|
||||
import type * as conversationsDuck from './state/ducks/conversations';
|
||||
|
@ -159,9 +157,7 @@ export type SignalCoreType = {
|
|||
createStore: typeof createStore;
|
||||
Roots: {
|
||||
createApp: typeof createApp;
|
||||
createGroupV2JoinModal: typeof createGroupV2JoinModal;
|
||||
createSafetyNumberViewer: typeof createSafetyNumberViewer;
|
||||
createShortcutGuideModal: typeof createShortcutGuideModal;
|
||||
};
|
||||
Ducks: {
|
||||
app: typeof appDuck;
|
||||
|
|
Loading…
Reference in a new issue