diff --git a/js/modules/signal.js b/js/modules/signal.js index 3331da9c3..a9624a5cb 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -29,7 +29,9 @@ const { AttachmentList, } = require('../../ts/components/conversation/AttachmentList'); const { CaptionEditor } = require('../../ts/components/CaptionEditor'); -const { ConfirmationModal } = require('../../ts/components/ConfirmationModal'); +const { + ConfirmationDialog, +} = require('../../ts/components/ConfirmationDialog'); const { ContactDetail, } = require('../../ts/components/conversation/ContactDetail'); @@ -317,7 +319,7 @@ exports.setup = (options = {}) => { const Components = { AttachmentList, CaptionEditor, - ConfirmationModal, + ConfirmationDialog, ContactDetail, ContactListItem, ContactModal, diff --git a/permissions_popup_preload.js b/permissions_popup_preload.js index ee3ccd02b..317bc2fa1 100644 --- a/permissions_popup_preload.js +++ b/permissions_popup_preload.js @@ -9,7 +9,7 @@ window.ReactDOM = require('react-dom'); const { ipcRenderer, remote } = require('electron'); const url = require('url'); const i18n = require('./js/modules/i18n'); -const { ConfirmationModal } = require('./ts/components/ConfirmationModal'); +const { ConfirmationDialog } = require('./ts/components/ConfirmationDialog'); const { makeGetter, makeSetter } = require('./preload_utils'); const { getEnvironment, @@ -32,7 +32,7 @@ window.forCalling = config.forCalling === 'true'; window.forCamera = config.forCamera === 'true'; window.Signal = { Components: { - ConfirmationModal, + ConfirmationDialog, }, }; diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index a1db4661c..3e7f55008 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -7673,278 +7673,6 @@ button.module-image__border-overlay:focus { } } -// Module: SafetyNumberChangeDialog - -.module-sfn-dialog__title { - @include font-body-1-bold; - text-align: center; - - @include dark-theme { - color: $color-white; - } -} - -.module-sfn-dialog__message { - @include font-body-2; - text-align: center; - - @include light-theme { - color: $color-gray-60; - } - - @include dark-theme { - color: $color-gray-25; - } -} - -.module-sfn-dialog__contacts { - list-style-type: none; - max-height: 300px; - overflow-y: scroll; - padding: 0; -} - -.module-sfn-dialog__contact { - align-items: center; - display: flex; - flex-direction: row; - margin-bottom: 16px; - - &--wrapper { - flex-grow: 1; - margin-left: 12px; - } - - &--name { - @include font-body-1-bold; - - @include dark-theme { - color: $color-white; - } - } - - &--number { - @include light-theme { - color: $color-gray-60; - } - - @include dark-theme { - color: $color-gray-25; - } - } - - &--view { - @include font-body-1-bold; - background: inherit; - border: none; - cursor: pointer; - margin-right: 2px; - outline: none; - padding: 8px 14px; - - @include keyboard-mode { - &:focus { - box-shadow: 0px 0px 0px 2px $ultramarine-ui-light; - } - } - - @include light-theme { - color: $ultramarine-ui-light; - } - - @include dark-theme { - color: $ultramarine-ui-dark; - } - } -} - -.module-sfn-dialog__actions { - border-top: 1px solid $color-gray-05; - display: flex; - justify-content: flex-end; - margin-left: -16px; - margin-right: -16px; - margin-top: -14px; - padding-left: 16px; - padding-right: 16px; - padding-top: 16px; - - &--cancel { - @include font-body-1-bold; - border: none; - border-radius: 4px; - outline: none; - padding: 7px 14px; - - @include mouse-mode { - &:hover { - background: $color-gray-15; - } - } - - @include keyboard-mode { - &:focus { - box-shadow: 0px 0px 0px 2px $ultramarine-ui-light; - } - } - - @include light-theme { - background-color: $color-gray-05; - color: $ultramarine-ui-light; - } - - @include dark-theme { - background-color: $color-gray-75; - color: $ultramarine-ui-dark; - } - } - - &--confirm { - @include font-body-1-bold; - background: $ultramarine-ui-light; - border: none; - border-radius: 4px; - color: $color-white; - margin-left: 12px; - outline: none; - padding: 7px 14px; - - @include mouse-mode { - &:hover { - background: $ultramarine-brand-dark; - } - } - - @include keyboard-mode { - &:focus { - box-shadow: 0px 0px 0px 2px $ultramarine-brand-dark; - } - } - } -} - -/* Safety Number verification */ - -.module-safety-number { - &__icon { - height: 1.25em; - width: 1.25em; - vertical-align: text-bottom; - display: inline-block; - } - - &__verification-label { - margin: 10px 0; - } - - &__icon--verified { - display: inline-block; - height: 1.25em; - margin-right: 4px; - vertical-align: text-bottom; - width: 1.25em; - - @include light-theme { - -webkit-mask: url('../images/icons/v2/check-24.svg') no-repeat center; - -webkit-mask-size: 100%; - background-color: #121212; - } - - @include dark-theme { - -webkit-mask: url('../images/icons/v2/check-24.svg') no-repeat center; - -webkit-mask-size: 100%; - background-color: #f6f6f6; - } - } - - &__icon--shield { - display: inline-block; - height: 1.25em; - margin-right: 4px; - vertical-align: text-bottom; - width: 1.25em; - - @include light-theme { - -webkit-mask: url('../images/icons/v2/safety-number-outline-24.svg') - no-repeat center; - -webkit-mask-size: 100%; - background-color: #121212; - } - - @include dark-theme { - -webkit-mask: url('../images/icons/v2/safety-number-solid-24.svg') - no-repeat center; - -webkit-mask-size: 100%; - background-color: #f6f6f6; - } - } - - &__verify-container { - text-align: center; - } - - &__button--verify { - border-radius: 5px; - font-weight: bold; - margin: 0; - outline: none; - padding: 10px; - } - - &__number { - background: #f6f6f6; - border-radius: 5px; - border: solid 1px #dedede; - font-family: monospace; - margin: 20px auto 20px auto; - padding: 10px; - text-align: center; - width: 16em; - - @include dark-theme { - background: #1b1b1b; - border: solid 1px #848484; - color: #f6f6f6; - } - } - - &__verification-status { - margin: 30px 0 10px; - text-align: center; - } - - &__close-button { - display: flex; - justify-content: flex-end; - - button { - background: inherit; - border: none; - cursor: pointer; - padding: 0; - - @include keyboard-mode { - &:focus { - border: 1px solid $ultramarine-ui-light; - } - } - - span { - display: inline-block; - height: 24px; - width: 24px; - - @include light-theme { - @include color-svg('../images/icons/v2/x-24.svg', $color-gray-60); - } - @include dark-theme { - @include color-svg('../images/icons/v2/x-24.svg', $color-gray-05); - } - } - } - } -} - // Module: StickerPicker .module-sticker-picker { @@ -8873,112 +8601,6 @@ button.module-image__border-overlay:focus { } } -// Module: confirmation dialog -.module-confirmation-dialog { - &__overlay { - background: $color-black-alpha-40; - position: fixed; - left: 0; - top: 0; - width: 100vw; - height: 100vh; - display: flex; - justify-content: center; - align-items: center; - // THIS Z-INDEX IS OVER NINE THOUSAND. OVER NINE THOUSAND?! THAT CAN'T BE! - z-index: 9001; - } - - &__container { - width: 360px; - padding: 12px 16px; - border-radius: 8px; - @include popper-shadow(); - - @include light-theme() { - background: $color-white; - color: $color-gray-90; - } - - @include dark-theme() { - background: $color-gray-80; - color: $color-gray-05; - } - - &__title { - @include font-body-1-bold; - } - - &__content { - @include font-body-1; - } - - &__buttons { - margin-top: 22px; - - display: flex; - flex-direction: row; - justify-content: flex-end; - - &__button { - margin-left: 4px; - border-radius: 17px; - height: 34px; - padding: 5px 12px; - display: flex; - justify-content: center; - align-items: center; - - @include font-body-1-bold; - - @include mouse-mode { - outline: none; - } - - @include light-theme() { - background: $color-white; - color: $color-gray-60; - border: 1px solid $color-gray-60; - } - - @include dark-theme() { - background: $color-gray-75; - color: $color-gray-25; - border: 1px solid $color-gray-25; - } - - &--negative { - @include light-theme() { - border: none; - background: $color-accent-red; - color: $color-white; - } - - @include dark-theme() { - border: none; - background: $color-accent-red; - color: $color-white; - } - } - - &--affirmative { - @include light-theme() { - border: none; - background: $color-accent-green; - color: $color-white; - } - - @include dark-theme() { - border: none; - background: $color-accent-green; - color: $color-white; - } - } - } - } - } -} - .module-left-pane-dialog { background: $color-accent-green; color: $color-white; diff --git a/stylesheets/components/Modal.scss b/stylesheets/components/Modal.scss index c1e7f3c5c..51c28fe0c 100644 --- a/stylesheets/components/Modal.scss +++ b/stylesheets/components/Modal.scss @@ -88,5 +88,9 @@ display: flex; justify-content: flex-end; margin-top: 16px; + + .module-Button { + margin-left: 8px; + } } } diff --git a/stylesheets/components/SafetyNumberChangeDialog.scss b/stylesheets/components/SafetyNumberChangeDialog.scss new file mode 100644 index 000000000..92ef066e2 --- /dev/null +++ b/stylesheets/components/SafetyNumberChangeDialog.scss @@ -0,0 +1,78 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +.module-SafetyNumberChangeDialog { + &__message { + @include font-body-2; + text-align: center; + + @include light-theme { + color: $color-gray-60; + } + + @include dark-theme { + color: $color-gray-25; + } + } + + &__contacts { + list-style-type: none; + max-height: 300px; + overflow-y: scroll; + padding: 0; + } + + &__contact { + align-items: center; + display: flex; + flex-direction: row; + margin-bottom: 16px; + + &--wrapper { + flex-grow: 1; + margin-left: 12px; + } + + &--name { + @include font-body-1-bold; + + @include dark-theme { + color: $color-white; + } + } + + &--number { + @include light-theme { + color: $color-gray-60; + } + + @include dark-theme { + color: $color-gray-25; + } + } + + &--view { + @include font-body-1-bold; + background: inherit; + border: none; + cursor: pointer; + margin-right: 2px; + outline: none; + padding: 8px 14px; + + @include keyboard-mode { + &:focus { + box-shadow: 0px 0px 0px 2px $ultramarine-ui-light; + } + } + + @include light-theme { + color: $ultramarine-ui-light; + } + + @include dark-theme { + color: $ultramarine-ui-dark; + } + } + } +} diff --git a/stylesheets/components/SafetyNumberViewer.scss b/stylesheets/components/SafetyNumberViewer.scss new file mode 100644 index 000000000..68386b930 --- /dev/null +++ b/stylesheets/components/SafetyNumberViewer.scss @@ -0,0 +1,122 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +.module-SafetyNumberViewer { + &__icon { + height: 1.25em; + width: 1.25em; + vertical-align: text-bottom; + display: inline-block; + } + + &__verification-label { + margin: 10px 0; + } + + &__icon--verified { + display: inline-block; + height: 1.25em; + margin-right: 4px; + vertical-align: text-bottom; + width: 1.25em; + + @include light-theme { + -webkit-mask: url('../images/icons/v2/check-24.svg') no-repeat center; + -webkit-mask-size: 100%; + background-color: #121212; + } + + @include dark-theme { + -webkit-mask: url('../images/icons/v2/check-24.svg') no-repeat center; + -webkit-mask-size: 100%; + background-color: #f6f6f6; + } + } + + &__icon--shield { + display: inline-block; + height: 1.25em; + margin-right: 4px; + vertical-align: text-bottom; + width: 1.25em; + + @include light-theme { + -webkit-mask: url('../images/icons/v2/safety-number-outline-24.svg') + no-repeat center; + -webkit-mask-size: 100%; + background-color: #121212; + } + + @include dark-theme { + -webkit-mask: url('../images/icons/v2/safety-number-solid-24.svg') + no-repeat center; + -webkit-mask-size: 100%; + background-color: #f6f6f6; + } + } + + &__verify-container { + text-align: center; + } + + &__button--verify { + border-radius: 5px; + font-weight: bold; + margin: 0; + outline: none; + padding: 10px; + } + + &__number { + background: #f6f6f6; + border-radius: 5px; + border: solid 1px #dedede; + font-family: monospace; + margin: 20px auto 20px auto; + padding: 10px; + text-align: center; + width: 16em; + + @include dark-theme { + background: #1b1b1b; + border: solid 1px #848484; + color: #f6f6f6; + } + } + + &__verification-status { + margin: 30px 0 10px; + text-align: center; + } + + &__close-button { + display: flex; + justify-content: flex-end; + + button { + background: inherit; + border: none; + cursor: pointer; + padding: 0; + + @include keyboard-mode { + &:focus { + border: 1px solid $ultramarine-ui-light; + } + } + + span { + display: inline-block; + height: 24px; + width: 24px; + + @include light-theme { + @include color-svg('../images/icons/v2/x-24.svg', $color-gray-60); + } + @include dark-theme { + @include color-svg('../images/icons/v2/x-24.svg', $color-gray-05); + } + } + } + } +} diff --git a/stylesheets/manifest.scss b/stylesheets/manifest.scss index d3b60002a..61b6bf2a2 100644 --- a/stylesheets/manifest.scss +++ b/stylesheets/manifest.scss @@ -38,5 +38,7 @@ @import './components/GroupTitleInput.scss'; @import './components/MessageAudio.scss'; @import './components/Modal.scss'; +@import './components/SafetyNumberChangeDialog.scss'; +@import './components/SafetyNumberViewer.scss'; @import './components/SearchResultsLoadingFakeHeader.scss'; @import './components/SearchResultsLoadingFakeRow.scss'; diff --git a/ts/components/CallingDeviceSelection.tsx b/ts/components/CallingDeviceSelection.tsx index 8ce3416a0..d2442d706 100644 --- a/ts/components/CallingDeviceSelection.tsx +++ b/ts/components/CallingDeviceSelection.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; -import { ConfirmationModal } from './ConfirmationModal'; +import { Modal } from './Modal'; import { LocalizerType } from '../types/Util'; import { AudioDevice, @@ -135,12 +135,7 @@ export const CallingDeviceSelection = ({ : undefined; return ( - +
-
+ ); }; diff --git a/ts/components/ConfirmationDialog.tsx b/ts/components/ConfirmationDialog.tsx index e5b2e35d3..417c56f65 100644 --- a/ts/components/ConfirmationDialog.tsx +++ b/ts/components/ConfirmationDialog.tsx @@ -2,8 +2,10 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import classNames from 'classnames'; +import { Button, ButtonVariant } from './Button'; import { LocalizerType } from '../types/Util'; +import { Modal } from './Modal'; +import { Theme } from '../util/theme'; export type ActionSpec = { text: string; @@ -12,12 +14,14 @@ export type ActionSpec = { }; export type OwnProps = { - readonly actions: Array; + readonly actions?: Array; readonly cancelText?: string; readonly children?: React.ReactNode; readonly i18n: LocalizerType; + readonly onCancel?: () => unknown; readonly onClose: () => unknown; readonly title?: string | React.ReactNode; + readonly theme?: Theme; }; export type Props = OwnProps; @@ -28,85 +32,77 @@ function focusRef(el: HTMLElement | null) { } } -// TODO: This should use . See DESKTOP-1038. -export const ConfirmationDialog = React.memo( - ({ i18n, onClose, cancelText, children, title, actions }: Props) => { - React.useEffect(() => { - const handler = ({ key }: KeyboardEvent) => { - if (key === 'Escape') { - onClose(); - } - }; - document.addEventListener('keydown', handler); +function getButtonVariant( + buttonStyle?: 'affirmative' | 'negative' +): ButtonVariant { + if (buttonStyle === 'affirmative') { + return ButtonVariant.Primary; + } - return () => { - document.removeEventListener('keydown', handler); - }; - }, [onClose]); + if (buttonStyle === 'negative') { + return ButtonVariant.Destructive; + } + + return ButtonVariant.Secondary; +} + +export const ConfirmationDialog = React.memo( + ({ + actions = [], + cancelText, + children, + i18n, + onCancel, + onClose, + theme, + title, + }: Props) => { + const cancelAndClose = React.useCallback(() => { + if (onCancel) { + onCancel(); + } + onClose(); + }, [onCancel, onClose]); const handleCancel = React.useCallback( (e: React.MouseEvent) => { if (e.target === e.currentTarget) { - onClose(); + cancelAndClose(); } }, - [onClose] + [cancelAndClose] ); - const handleAction = React.useCallback( - (e: React.MouseEvent) => { - if (e.currentTarget.dataset.action) { - const actionIndex = parseInt(e.currentTarget.dataset.action, 10); - const { action } = actions[actionIndex]; - action(); - } - onClose(); - }, - [onClose, actions] - ); + const hasActions = Boolean(actions.length); return ( -
- {title ? ( -

- {title} -

- ) : null} -
- {children} -
- {actions.length > 0 && ( -
- + {actions.map((action, i) => ( + - {actions.map((action, i) => ( - - ))} -
- )} -
+ {action.text} + + ))} + +
); } ); diff --git a/ts/components/ConfirmationModal.tsx b/ts/components/ConfirmationModal.tsx deleted file mode 100644 index 814d0bc42..000000000 --- a/ts/components/ConfirmationModal.tsx +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2019-2020 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -import * as React from 'react'; -import classNames from 'classnames'; -import { createPortal } from 'react-dom'; -import { - ConfirmationDialog, - Props as ConfirmationDialogProps, -} from './ConfirmationDialog'; -import { LocalizerType } from '../types/Util'; -import { Theme, themeClassName } from '../util/theme'; - -export type OwnProps = { - readonly i18n: LocalizerType; - readonly onClose: () => unknown; - readonly theme?: Theme; -}; - -export type Props = OwnProps & ConfirmationDialogProps; - -export const ConfirmationModal = React.memo( - ({ i18n, onClose, theme, children, ...rest }: Props) => { - const [root, setRoot] = React.useState(null); - - React.useEffect(() => { - const div = document.createElement('div'); - document.body.appendChild(div); - setRoot(div); - - return () => { - document.body.removeChild(div); - setRoot(null); - }; - }, []); - - React.useEffect(() => { - const handler = (event: KeyboardEvent) => { - if (event.key === 'Escape') { - onClose(); - - event.preventDefault(); - event.stopPropagation(); - } - }; - document.addEventListener('keydown', handler); - - return () => { - document.removeEventListener('keydown', handler); - }; - }, [onClose]); - - const handleCancel = React.useCallback( - (e: React.MouseEvent) => { - if (e.target === e.currentTarget) { - onClose(); - } - }, - [onClose] - ); - - const handleKeyCancel = React.useCallback( - (e: React.KeyboardEvent) => { - if (e.target === e.currentTarget && e.keyCode === 27) { - onClose(); - } - }, - [onClose] - ); - - return root - ? createPortal( -
- - {children} - -
, - root - ) - : null; - } -); diff --git a/ts/components/ErrorModal.tsx b/ts/components/ErrorModal.tsx index eb6b84572..1a6630a5b 100644 --- a/ts/components/ErrorModal.tsx +++ b/ts/components/ErrorModal.tsx @@ -4,7 +4,8 @@ import * as React from 'react'; import { LocalizerType } from '../types/Util'; -import { ConfirmationModal } from './ConfirmationModal'; +import { Modal } from './Modal'; +import { Button, ButtonVariant } from './Button'; export type PropsType = { buttonText?: string; @@ -21,30 +22,29 @@ function focusRef(el: HTMLElement | null) { } } -// TODO: This should use . See DESKTOP-1038. export const ErrorModal = (props: PropsType): JSX.Element => { const { buttonText, description, i18n, onClose, title } = props; return ( - -
- {description || i18n('ErrorModal--description')} -
-
- -
-
+ <> +
+ {description || i18n('ErrorModal--description')} +
+ + + + +
); }; diff --git a/ts/components/GroupCallRemoteParticipant.tsx b/ts/components/GroupCallRemoteParticipant.tsx index 0e15a5055..f338637e5 100644 --- a/ts/components/GroupCallRemoteParticipant.tsx +++ b/ts/components/GroupCallRemoteParticipant.tsx @@ -18,7 +18,7 @@ import { import { LocalizerType } from '../types/Util'; import { CallBackgroundBlur } from './CallBackgroundBlur'; import { Avatar, AvatarSize } from './Avatar'; -import { ConfirmationModal } from './ConfirmationModal'; +import { ConfirmationDialog } from './ConfirmationDialog'; import { Intl } from './Intl'; import { ContactName } from './conversation/ContactName'; import { useIntersectionObserver } from '../util/hooks'; @@ -200,7 +200,8 @@ export const GroupCallRemoteParticipant: React.FC = React.memo( return ( <> {showBlockInfo && ( - { setShowBlockInfo(false); @@ -221,18 +222,9 @@ export const GroupCallRemoteParticipant: React.FC = React.memo( /> } - actions={[ - { - text: i18n('ok'), - action: () => { - setShowBlockInfo(false); - }, - style: 'affirmative', - }, - ]} > {i18n('calling__block-info')} - + )}
void; title?: ReactNode; + theme?: Theme; }; export function Modal({ @@ -22,13 +24,14 @@ export function Modal({ i18n, onClose = noop, title, + theme, }: Readonly): ReactElement { const [scrolled, setScrolled] = useState(false); const hasHeader = Boolean(hasXButton || title); return ( - +
unknown; readonly children: React.ReactElement; + readonly theme?: Theme; }; -export const ModalHost = React.memo(({ onClose, children }: PropsType) => { - const [root, setRoot] = React.useState(null); +export const ModalHost = React.memo( + ({ onClose, children, theme }: PropsType) => { + const [root, setRoot] = React.useState(null); - React.useEffect(() => { - const div = document.createElement('div'); - document.body.appendChild(div); - setRoot(div); + useEffect(() => { + const div = document.createElement('div'); + document.body.appendChild(div); + setRoot(div); - return () => { - document.body.removeChild(div); - setRoot(null); - }; - }, []); + return () => { + document.body.removeChild(div); + setRoot(null); + }; + }, []); - React.useEffect(() => { - const handler = (event: KeyboardEvent) => { - if (event.key === 'Escape') { - onClose(); + useEffect(() => { + const handler = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + onClose(); - event.preventDefault(); - event.stopPropagation(); - } - }; - document.addEventListener('keydown', handler); + event.preventDefault(); + event.stopPropagation(); + } + }; + document.addEventListener('keydown', handler); - return () => { - document.removeEventListener('keydown', handler); - }; - }, [onClose]); + return () => { + document.removeEventListener('keydown', handler); + }; + }, [onClose]); - // This makes it easier to write dialogs to be hosted here; they won't have to worry - // as much about preventing propagation of mouse events. - const handleCancel = React.useCallback( - (e: React.MouseEvent) => { - if (e.target === e.currentTarget) { - onClose(); - } - }, - [onClose] - ); + // This makes it easier to write dialogs to be hosted here; they won't have to worry + // as much about preventing propagation of mouse events. + const handleCancel = React.useCallback( + (e: React.MouseEvent) => { + if (e.target === e.currentTarget) { + onClose(); + } + }, + [onClose] + ); - return root - ? createPortal( -
- {children} -
, - root - ) - : null; -}); + return root + ? createPortal( +
+ {children} +
, + root + ) + : null; + } +); diff --git a/ts/components/SafetyNumberChangeDialog.stories.tsx b/ts/components/SafetyNumberChangeDialog.stories.tsx index 1074267e1..50b688682 100644 --- a/ts/components/SafetyNumberChangeDialog.stories.tsx +++ b/ts/components/SafetyNumberChangeDialog.stories.tsx @@ -13,6 +13,7 @@ import enMessages from '../../_locales/en/messages.json'; const i18n = setupI18n('en', enMessages); const contactWithAllData = { + id: 'abc', avatarPath: undefined, color: 'signal-blue', profileName: '-*Smartest Dude*-', @@ -22,6 +23,7 @@ const contactWithAllData = { } as ConversationType; const contactWithJustProfile = { + id: 'def', avatarPath: undefined, color: 'signal-blue', title: '-*Smartest Dude*-', @@ -31,6 +33,7 @@ const contactWithJustProfile = { } as ConversationType; const contactWithJustNumber = { + id: 'xyz', avatarPath: undefined, color: 'signal-blue', profileName: undefined, diff --git a/ts/components/SafetyNumberChangeDialog.tsx b/ts/components/SafetyNumberChangeDialog.tsx index 4f5fc6613..3e8f591b9 100644 --- a/ts/components/SafetyNumberChangeDialog.tsx +++ b/ts/components/SafetyNumberChangeDialog.tsx @@ -2,10 +2,12 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; +import { noop } from 'lodash'; import { Avatar } from './Avatar'; -import { ConfirmationModal } from './ConfirmationModal'; +import { ConfirmationDialog } from './ConfirmationDialog'; import { InContactsIcon } from './InContactsIcon'; +import { Modal } from './Modal'; import { ConversationType } from '../state/ducks/conversations'; import { LocalizerType } from '../types/Util'; @@ -24,18 +26,17 @@ export type Props = { readonly renderSafetyNumber: (props: SafetyNumberProps) => JSX.Element; }; -type SafetyDialogContentProps = Props & { - readonly onView: (contact: ConversationType) => void; -}; - -const SafetyDialogContents = ({ +export const SafetyNumberChangeDialog = ({ confirmText, contacts, i18n, onCancel, onConfirm, - onView, -}: SafetyDialogContentProps): JSX.Element => { + renderSafetyNumber, +}: Props): JSX.Element => { + const [selectedContact, setSelectedContact] = React.useState< + ConversationType | undefined + >(undefined); const cancelButtonRef = React.createRef(); React.useEffect(() => { @@ -44,20 +45,46 @@ const SafetyDialogContents = ({ } }, [cancelButtonRef, contacts]); + const onClose = selectedContact + ? () => { + setSelectedContact(undefined); + } + : onCancel; + + if (selectedContact) { + return ( + + {renderSafetyNumber({ contactID: selectedContact.id, onClose })} + + ); + } + return ( - <> -

- {i18n('safetyNumberChanges')} -

-
+ +
{i18n('changedVerificationWarning')}
-
    +
      {contacts.map((contact: ConversationType) => { const shouldShowNumber = Boolean(contact.name || contact.profileName); return ( -
    • +
    • -
      -
      +
      +
      {contact.title} {contact.name ? ( @@ -80,15 +107,15 @@ const SafetyDialogContents = ({ ) : null}
      {shouldShowNumber ? ( -
      +
      {contact.phoneNumber}
      ) : null}
    -
    - - -
    - - ); -}; - -export const SafetyNumberChangeDialog = (props: Props): JSX.Element => { - const { i18n, onCancel, renderSafetyNumber } = props; - const [contact, setViewSafetyNumber] = React.useState< - ConversationType | undefined - >(undefined); - - const onClose = contact - ? () => { - setViewSafetyNumber(undefined); - } - : onCancel; - - return ( - - {contact && renderSafetyNumber({ contactID: contact.id, onClose })} - {!contact && ( - { - setViewSafetyNumber(selectedContact); - }} - /> - )} - + ); }; diff --git a/ts/components/SafetyNumberViewer.tsx b/ts/components/SafetyNumberViewer.tsx index 7c804f2fb..4b339bd85 100644 --- a/ts/components/SafetyNumberViewer.tsx +++ b/ts/components/SafetyNumberViewer.tsx @@ -41,8 +41,8 @@ export const SafetyNumberViewer = ({ if (!contact.phoneNumber) { return ( -
    -
    +
    +
    {i18n('cannotGenerateSafetyNumber')}
    @@ -54,7 +54,7 @@ export const SafetyNumberViewer = ({ showNumber && contact.phoneNumber ? ` ยท ${contact.phoneNumber}` : ''; const name = `${contact.title}${numberFragment}`; const boldName = ( - {name} + {name} ); const { isVerified } = contact; @@ -65,15 +65,15 @@ export const SafetyNumberViewer = ({ const verifyButtonText = isVerified ? i18n('unverify') : i18n('verify'); return ( -
    +
    {onClose && ( -
    +
    )} -
    +
    -
    +
    {safetyNumber || getPlaceholder()}
    -
    +
    {isVerified ? ( - + ) : ( - + )}
    -
    +