diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index 33b1c9ca4f..7eb729bbda 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -2337,6 +2337,9 @@
"autoUpdateLaterButtonLabel": {
"message": "Later"
},
+ "autoUpdateIgnoreButtonLabel": {
+ "message": "Ignore update"
+ },
"leftTheGroup": {
"message": "$name$ left the group.",
"description": "Shown in the conversation history when a single person leaves the group",
diff --git a/stylesheets/components/LeftPaneDialog.scss b/stylesheets/components/LeftPaneDialog.scss
index 19e875f18f..ec780a6b70 100644
--- a/stylesheets/components/LeftPaneDialog.scss
+++ b/stylesheets/components/LeftPaneDialog.scss
@@ -8,12 +8,17 @@
}
.LeftPaneDialog {
+ @include button-reset;
+
align-items: center;
background: $color-ultramarine;
color: $color-white;
display: flex;
+ width: 100%;
min-height: 64px;
padding: 12px 18px;
+ user-select: none;
+ cursor: inherit;
&__container {
display: flex;
@@ -46,10 +51,11 @@
}
&__icon {
- width: 20px;
- height: 20px;
+ width: 24px;
+ height: 24px;
margin-right: 18px;
background-color: $color-white;
+ -webkit-mask-size: contain;
&--relink {
-webkit-mask: url('../images/icons/v2/link-broken-16.svg') no-repeat
diff --git a/ts/components/DialogExpiredBuild.tsx b/ts/components/DialogExpiredBuild.tsx
index 500a000b49..ac2dfd3ab9 100644
--- a/ts/components/DialogExpiredBuild.tsx
+++ b/ts/components/DialogExpiredBuild.tsx
@@ -5,6 +5,8 @@ import React from 'react';
import { LocalizerType } from '../types/Util';
+import { LeftPaneDialog } from './LeftPaneDialog';
+
type PropsType = {
hasExpired: boolean;
i18n: LocalizerType;
@@ -19,19 +21,17 @@ export const DialogExpiredBuild = ({
}
return (
-
+
+ {i18n('expiredWarning')}{' '}
+
+ {i18n('upgrade')}
+
+
);
};
diff --git a/ts/components/DialogNetworkStatus.tsx b/ts/components/DialogNetworkStatus.tsx
index d8ec8a9be4..4e7f11d978 100644
--- a/ts/components/DialogNetworkStatus.tsx
+++ b/ts/components/DialogNetworkStatus.tsx
@@ -1,8 +1,9 @@
// Copyright 2020-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
-import React from 'react';
+import React, { useEffect } from 'react';
+import { LeftPaneDialog } from './LeftPaneDialog';
import { Spinner } from './Spinner';
import { LocalizerType } from '../types/Util';
import { SocketStatus } from '../types/SocketStatus';
@@ -16,42 +17,6 @@ export type PropsType = NetworkStateType & {
manualReconnect: () => void;
};
-type RenderDialogTypes = {
- isConnecting?: boolean;
- title: string;
- subtext: string;
- renderActionableButton?: () => JSX.Element;
-};
-
-function renderDialog({
- isConnecting,
- title,
- subtext,
- renderActionableButton,
-}: RenderDialogTypes): JSX.Element {
- return (
-
- {isConnecting ? (
-
-
-
- ) : (
-
- )}
-
-
{title}
-
{subtext}
-
{renderActionableButton && renderActionableButton()}
-
-
- );
-}
-
export const DialogNetworkStatus = ({
hasNetworkDialog,
i18n,
@@ -59,8 +24,10 @@ export const DialogNetworkStatus = ({
socketStatus,
manualReconnect,
}: PropsType): JSX.Element | null => {
- const [isConnecting, setIsConnecting] = React.useState(false);
- React.useEffect(() => {
+ const [isConnecting, setIsConnecting] = React.useState(
+ socketStatus === SocketStatus.CONNECTING
+ );
+ useEffect(() => {
if (!hasNetworkDialog) {
return () => null;
}
@@ -80,62 +47,46 @@ export const DialogNetworkStatus = ({
};
}, [hasNetworkDialog, isConnecting, setIsConnecting]);
- if (!hasNetworkDialog) {
- return null;
- }
-
const reconnect = () => {
setIsConnecting(true);
manualReconnect();
};
- const manualReconnectButton = (): JSX.Element => (
-
- );
+ if (!hasNetworkDialog) {
+ return null;
+ }
if (isConnecting) {
- return renderDialog({
- isConnecting: true,
- subtext: i18n('connectingHangOn'),
- title: i18n('connecting'),
- });
+ const spinner = (
+
+
+
+ );
+
+ return (
+
+ );
}
- if (!isOnline) {
- return renderDialog({
- renderActionableButton: manualReconnectButton,
- subtext: i18n('checkNetworkConnection'),
- title: i18n('offline'),
- });
- }
-
- let subtext = '';
- let title = '';
- let renderActionableButton;
-
- switch (socketStatus) {
- case SocketStatus.CONNECTING:
- subtext = i18n('connectingHangOn');
- title = i18n('connecting');
- break;
- case SocketStatus.CLOSED:
- case SocketStatus.CLOSING:
- default:
- renderActionableButton = manualReconnectButton;
- title = i18n('disconnected');
- subtext = i18n('checkNetworkConnection');
- }
-
- return renderDialog({
- isConnecting: socketStatus === SocketStatus.CONNECTING,
- renderActionableButton,
- subtext,
- title,
- });
+ return (
+
+ );
};
diff --git a/ts/components/DialogRelink.tsx b/ts/components/DialogRelink.tsx
index 41fa641c46..193602fb8b 100644
--- a/ts/components/DialogRelink.tsx
+++ b/ts/components/DialogRelink.tsx
@@ -5,6 +5,8 @@ import React from 'react';
import { LocalizerType } from '../types/Util';
+import { LeftPaneDialog } from './LeftPaneDialog';
+
export type PropsType = {
i18n: LocalizerType;
isRegistrationDone: boolean;
@@ -21,20 +23,13 @@ export const DialogRelink = ({
}
return (
-
-
-
-
{i18n('unlinked')}
-
-
-
-
-
+
);
};
diff --git a/ts/components/DialogUpdate.tsx b/ts/components/DialogUpdate.tsx
index 689c60a32c..924e57498f 100644
--- a/ts/components/DialogUpdate.tsx
+++ b/ts/components/DialogUpdate.tsx
@@ -5,8 +5,9 @@ import React from 'react';
import formatFileSize from 'filesize';
import { DialogType } from '../types/Dialogs';
-import { Intl } from './Intl';
import { LocalizerType } from '../types/Util';
+import { Intl } from './Intl';
+import { LeftPaneDialog } from './LeftPaneDialog';
export type PropsType = {
dialogType: DialogType;
@@ -48,132 +49,99 @@ export const DialogUpdate = ({
if (dialogType === DialogType.Cannot_Update) {
return (
-
-
-
{i18n('cannotUpdate')}
-
-
- https://signal.org/download/
- ,
- ]}
- i18n={i18n}
- id="cannotUpdateDetail"
- />
-
-
-
+
+
+
+ https://signal.org/download/
+ ,
+ ]}
+ i18n={i18n}
+ id="cannotUpdateDetail"
+ />
+
+
);
}
if (dialogType === DialogType.MacOS_Read_Only) {
return (
-
-
-
-
{i18n('cannotUpdate')}
-
- Signal.app,
- folder: /Applications,
- }}
- i18n={i18n}
- id="readOnlyVolume"
- />
-
-
-
-
-
-
+
+
);
}
- let size: string | undefined;
+ let title = i18n('autoUpdateNewVersionTitle');
+
if (
downloadSize &&
(dialogType === DialogType.DownloadReady ||
dialogType === DialogType.Downloading)
) {
- size = `(${formatFileSize(downloadSize, { round: 0 })})`;
- }
-
- let updateSubText: JSX.Element;
- if (dialogType === DialogType.DownloadReady) {
- updateSubText = (
-
- );
- } else if (dialogType === DialogType.Downloading) {
- const width = Math.ceil(
- ((downloadedSize || 1) / (downloadSize || 1)) * 100
- );
-
- updateSubText = (
-
- );
- } else {
- updateSubText = (
-
- );
+ title += ` (${formatFileSize(downloadSize, { round: 0 })})`;
}
const versionTitle = version
? i18n('DialogUpdate--version-available', [version])
: undefined;
- return (
-
-
-
-
-
- {i18n('autoUpdateNewVersionTitle')} {size}
-
- {updateSubText}
-
-
-
- {dialogType !== DialogType.Downloading && (
-
+
+
+ );
+ }
+
+ let clickLabel: string;
+ if (dialogType === DialogType.DownloadReady) {
+ clickLabel = i18n('downloadNewVersionMessage');
+ } else {
+ clickLabel = i18n('autoUpdateNewVersionMessage');
+ }
+
+ return (
+
);
};
diff --git a/ts/components/LeftPaneDialog.tsx b/ts/components/LeftPaneDialog.tsx
new file mode 100644
index 0000000000..15458b8a28
--- /dev/null
+++ b/ts/components/LeftPaneDialog.tsx
@@ -0,0 +1,149 @@
+// Copyright 2021 Signal Messenger, LLC
+// SPDX-License-Identifier: AGPL-3.0-only
+import React, { ReactNode } from 'react';
+import classNames from 'classnames';
+
+const BASE_CLASS_NAME = 'LeftPaneDialog';
+
+export type PropsType = {
+ type?: 'warning' | 'error';
+ icon?: 'update' | 'relink' | 'network' | ReactNode;
+ title?: string;
+ subtitle?: string;
+ children?: ReactNode;
+ hoverText?: string;
+} & (
+ | {
+ onClick?: undefined;
+ clickLabel?: undefined;
+ hasAction?: false;
+ }
+ | {
+ onClick: () => void;
+ clickLabel: string;
+ hasAction: true;
+ }
+) &
+ (
+ | {
+ onClose?: undefined;
+ closeLabel?: undefined;
+ hasXButton?: false;
+ }
+ | {
+ onClose: () => void;
+ closeLabel: string;
+ hasXButton: true;
+ }
+ );
+
+export const LeftPaneDialog: React.FC = ({
+ icon,
+ type,
+ onClick,
+ clickLabel,
+ title,
+ subtitle,
+ children,
+ hoverText,
+ hasAction,
+
+ hasXButton,
+ onClose,
+ closeLabel,
+}) => {
+ const onClickWrap = (e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ onClick?.();
+ };
+
+ const onCloseWrap = (e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ onClose?.();
+ };
+
+ const iconClassName =
+ typeof icon === 'string'
+ ? classNames([
+ `${BASE_CLASS_NAME}__icon`,
+ `${BASE_CLASS_NAME}__icon--${icon}`,
+ ])
+ : undefined;
+
+ let action: ReactNode;
+ if (hasAction) {
+ action = (
+
+ );
+ }
+
+ let xButton: ReactNode;
+ if (hasXButton) {
+ xButton = (
+
+
+
+ );
+ }
+
+ const className = classNames([
+ BASE_CLASS_NAME,
+ type === undefined ? undefined : `${BASE_CLASS_NAME}--${type}`,
+ ]);
+
+ const content = (
+ <>
+
+ {typeof icon === 'string' ?
: icon}
+
+ {title === undefined ? undefined :
{title}
}
+ {subtitle === undefined ? undefined :
{subtitle}
}
+ {children}
+ {action}
+
+
+ {xButton}
+ >
+ );
+
+ if (onClick) {
+ return (
+
+ );
+ }
+
+ return (
+
+ {content}
+
+ );
+};