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')} - -
-
+ + {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} +
+ ); +};