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