Move the safety number viewer into modal

This commit is contained in:
Josh Perez 2021-10-06 16:27:14 -04:00 committed by GitHub
parent 8c34d6ebc2
commit 048e1e4cd7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 132 additions and 167 deletions

View file

@ -1123,7 +1123,7 @@
"description": "This is a menu item for viewing all media (images + video) in a conversation, using the imperative case, as in a command." "description": "This is a menu item for viewing all media (images + video) in a conversation, using the imperative case, as in a command."
}, },
"verifyHelp": { "verifyHelp": {
"message": "If you wish to verify the security of your end-to-end encryption with $name$, compare the numbers above with the numbers on their device.", "message": "To verify the security of your end-to-end encryption with $name$, compare the numbers above with their device.",
"placeholders": { "placeholders": {
"name": { "name": {
"content": "$1", "content": "$1",
@ -2148,6 +2148,10 @@
} }
} }
}, },
"SafetyNumberModal__title": {
"message": "Verify Safety Number",
"description": "Title for the modal for safety number verification"
},
"safetyNumberChanged": { "safetyNumberChanged": {
"message": "Safety Number has changed", "message": "Safety Number has changed",
"description": "A notification shown in the conversation when a contact reinstalls" "description": "A notification shown in the conversation when a contact reinstalls"

View file

@ -106,12 +106,6 @@
</div> </div>
</script> </script>
<script type="text/x-tmpl-mustache" id="key-verification">
<div class="container" tabindex="0">
<div class="key-verification-wrapper"></div>
</div>
</script>
<script type="text/x-tmpl-mustache" id="link-flow-template"> <script type="text/x-tmpl-mustache" id="link-flow-template">
<div class='module-title-bar-drag-area'></div> <div class='module-title-bar-drag-area'></div>
@ -267,10 +261,6 @@
type="text/javascript" type="text/javascript"
src="js/views/react_wrapper_view.js" src="js/views/react_wrapper_view.js"
></script> ></script>
<script
type="text/javascript"
src="js/views/key_verification_view.js"
></script>
<script <script
type="text/javascript" type="text/javascript"
src="ts/shims/showConfirmationDialog.js" src="ts/shims/showConfirmationDialog.js"

View file

@ -1,37 +0,0 @@
// Copyright 2015-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* global Backbone, Signal, Whisper, $ */
// eslint-disable-next-line func-names
(function () {
window.Whisper = window.Whisper || {};
Whisper.KeyVerificationPanelView = Whisper.View.extend({
className: 'panel',
template: () => $('#key-verification').html(),
initialize(options) {
this.render();
this.view = new Whisper.ReactWrapperView({
JSX: Signal.State.Roots.createSafetyNumberViewer(window.reduxStore, {
contactID: options.model.get('id'),
}),
onInitialRender: () => {
if (options.onLoad) {
options.onLoad(this);
}
},
});
this.$('.key-verification-wrapper').append(this.view.el);
},
remove() {
if (this.view) {
this.view.remove();
}
Backbone.View.prototype.remove.call(this);
},
});
})();

View file

@ -2,6 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
.module-SafetyNumberViewer { .module-SafetyNumberViewer {
text-align: center;
&__icon { &__icon {
height: 1.25em; height: 1.25em;
width: 1.25em; width: 1.25em;
@ -9,10 +11,6 @@
display: inline-block; display: inline-block;
} }
&__verification-label {
margin: 10px 0;
}
&__icon--verified { &__icon--verified {
display: inline-block; display: inline-block;
height: 1.25em; height: 1.25em;
@ -55,33 +53,14 @@
} }
} }
&__verify-container {
text-align: center;
}
&__button--verify {
border-radius: 5px;
font-weight: bold;
margin: 0;
outline: none;
padding: 10px;
}
&__number { &__number {
background: #f6f6f6; background: $color-gray-02;
border-radius: 5px; border-radius: 8px;
border: solid 1px #dedede;
font-family: monospace; font-family: monospace;
margin: 20px auto 20px auto; height: 100px;
padding: 10px; margin: 24px auto;
text-align: center; padding: 24px;
width: 16em; width: 240px;
@include dark-theme {
background: #1b1b1b;
border: solid 1px #848484;
color: #f6f6f6;
}
} }
&__verification-status { &__verification-status {
@ -119,4 +98,12 @@
} }
} }
} }
&__button {
margin-top: 30px;
}
&__modal.module-Modal {
max-width: 500px;
}
} }

View file

@ -79,12 +79,6 @@
</div> </div>
</script> </script>
<script type="text/x-tmpl-mustache" id="key-verification">
<div class="container" tabindex="0">
<div class="key-verification-wrapper"></div>
</div>
</script>
<script type="text/x-tmpl-mustache" id="link-flow-template"> <script type="text/x-tmpl-mustache" id="link-flow-template">
<div class='module-title-bar-drag-area'></div> <div class='module-title-bar-drag-area'></div>
@ -263,11 +257,6 @@
src="../js/views/contact_list_view.js" src="../js/views/contact_list_view.js"
data-cover data-cover
></script> ></script>
<script
type="text/javascript"
src="../js/views/key_verification_view.js"
data-cover
></script>
<script <script
type="text/javascript" type="text/javascript"
src="../js/views/group_member_list_view.js" src="../js/views/group_member_list_view.js"

View file

@ -10,6 +10,9 @@ type PropsType = {
// ProfileEditor // ProfileEditor
isProfileEditorVisible: boolean; isProfileEditorVisible: boolean;
renderProfileEditor: () => JSX.Element; renderProfileEditor: () => JSX.Element;
// SafetyNumberModal
safetyNumberModalContactId?: string;
renderSafetyNumber: () => JSX.Element;
}; };
export const GlobalModalContainer = ({ export const GlobalModalContainer = ({
@ -19,7 +22,14 @@ export const GlobalModalContainer = ({
// ProfileEditor // ProfileEditor
isProfileEditorVisible, isProfileEditorVisible,
renderProfileEditor, renderProfileEditor,
// SafetyNumberModal
safetyNumberModalContactId,
renderSafetyNumber,
}: PropsType): JSX.Element | null => { }: PropsType): JSX.Element | null => {
if (safetyNumberModalContactId) {
return renderSafetyNumber();
}
if (contactModalState) { if (contactModalState) {
return renderContactModal(); return renderContactModal();
} }

View file

@ -0,0 +1,31 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { Modal } from './Modal';
import {
SafetyNumberViewer,
PropsType as SafetyNumberViewerPropsType,
} from './SafetyNumberViewer';
type PropsType = {
toggleSafetyNumberModal: () => unknown;
} & SafetyNumberViewerPropsType;
export const SafetyNumberModal = ({
i18n,
toggleSafetyNumberModal,
...safetyNumberViewerProps
}: PropsType): JSX.Element | null => {
return (
<Modal
hasXButton
i18n={i18n}
moduleClassName="module-SafetyNumberViewer__modal"
onClose={() => toggleSafetyNumberModal()}
title={i18n('SafetyNumberModal__title')}
>
<SafetyNumberViewer i18n={i18n} {...safetyNumberViewerProps} />
</Modal>
);
};

View file

@ -50,12 +50,6 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
generateSafetyNumber: action('generate-safety-number'), generateSafetyNumber: action('generate-safety-number'),
i18n, i18n,
safetyNumber: text('safetyNumber', overrideProps.safetyNumber || 'XXX'), safetyNumber: text('safetyNumber', overrideProps.safetyNumber || 'XXX'),
safetyNumberChanged: boolean(
'safetyNumberChanged',
overrideProps.safetyNumberChanged !== undefined
? overrideProps.safetyNumberChanged
: false
),
toggleVerified: action('toggle-verified'), toggleVerified: action('toggle-verified'),
verificationDisabled: boolean( verificationDisabled: boolean(
'verificationDisabled', 'verificationDisabled',
@ -95,16 +89,6 @@ story.add('Verification Disabled', () => {
); );
}); });
story.add('Safety Number Changed', () => {
return (
<SafetyNumberViewer
{...createProps({
safetyNumberChanged: true,
})}
/>
);
});
story.add('Safety Number (dialog close)', () => { story.add('Safety Number (dialog close)', () => {
return ( return (
<SafetyNumberViewer <SafetyNumberViewer

View file

@ -2,9 +2,10 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import React from 'react'; import React from 'react';
import { Button, ButtonVariant } from './Button';
import { ConversationType } from '../state/ducks/conversations'; import { ConversationType } from '../state/ducks/conversations';
import { LocalizerType } from '../types/Util';
import { Intl } from './Intl'; import { Intl } from './Intl';
import { LocalizerType } from '../types/Util';
export type PropsType = { export type PropsType = {
contact?: ConversationType; contact?: ConversationType;
@ -12,7 +13,6 @@ export type PropsType = {
i18n: LocalizerType; i18n: LocalizerType;
onClose?: () => void; onClose?: () => void;
safetyNumber: string; safetyNumber: string;
safetyNumberChanged?: boolean;
toggleVerified: (contact: ConversationType) => void; toggleVerified: (contact: ConversationType) => void;
verificationDisabled: boolean; verificationDisabled: boolean;
}; };
@ -23,7 +23,6 @@ export const SafetyNumberViewer = ({
i18n, i18n,
onClose, onClose,
safetyNumber, safetyNumber,
safetyNumberChanged,
toggleVerified, toggleVerified,
verificationDisabled, verificationDisabled,
}: PropsType): JSX.Element | null => { }: PropsType): JSX.Element | null => {
@ -42,10 +41,8 @@ export const SafetyNumberViewer = ({
if (!contact.phoneNumber) { if (!contact.phoneNumber) {
return ( return (
<div className="module-SafetyNumberViewer"> <div className="module-SafetyNumberViewer">
<div className="module-SafetyNumberViewer__verify-container">
{i18n('cannotGenerateSafetyNumber')} {i18n('cannotGenerateSafetyNumber')}
</div> </div>
</div>
); );
} }
@ -59,9 +56,6 @@ export const SafetyNumberViewer = ({
const { isVerified } = contact; const { isVerified } = contact;
const verifiedStatusKey = isVerified ? 'isVerified' : 'isNotVerified'; const verifiedStatusKey = isVerified ? 'isVerified' : 'isNotVerified';
const safetyNumberChangedKey = safetyNumberChanged
? 'changedRightAfterVerify'
: 'yourSafetyNumberWith';
const verifyButtonText = isVerified ? i18n('unverify') : i18n('verify'); const verifyButtonText = isVerified ? i18n('unverify') : i18n('verify');
return ( return (
@ -73,16 +67,6 @@ export const SafetyNumberViewer = ({
</button> </button>
</div> </div>
)} )}
<div className="module-SafetyNumberViewer__verification-label">
<Intl
i18n={i18n}
id={safetyNumberChangedKey}
components={{
name1: boldName,
name2: boldName,
}}
/>
</div>
<div className="module-SafetyNumberViewer__number"> <div className="module-SafetyNumberViewer__number">
{safetyNumber || getPlaceholder()} {safetyNumber || getPlaceholder()}
</div> </div>
@ -95,18 +79,16 @@ export const SafetyNumberViewer = ({
)} )}
<Intl i18n={i18n} id={verifiedStatusKey} components={[boldName]} /> <Intl i18n={i18n} id={verifiedStatusKey} components={[boldName]} />
</div> </div>
<div className="module-SafetyNumberViewer__verify-container"> <div className="module-SafetyNumberViewer__button">
<button <Button
className="module-SafetyNumberViewer__button--verify"
disabled={verificationDisabled} disabled={verificationDisabled}
onClick={() => { onClick={() => {
toggleVerified(contact); toggleVerified(contact);
}} }}
tabIndex={0} variant={ButtonVariant.Secondary}
type="button"
> >
{verifyButtonText} {verifyButtonText}
</button> </Button>
</div> </div>
</div> </div>
); );

View file

@ -7,6 +7,7 @@ export type GlobalModalsStateType = {
readonly contactModalState?: ContactModalStateType; readonly contactModalState?: ContactModalStateType;
readonly isProfileEditorVisible: boolean; readonly isProfileEditorVisible: boolean;
readonly profileEditorHasError: boolean; readonly profileEditorHasError: boolean;
readonly safetyNumberModalContactId?: string;
}; };
// Actions // Actions
@ -16,6 +17,7 @@ const SHOW_CONTACT_MODAL = 'globalModals/SHOW_CONTACT_MODAL';
const TOGGLE_PROFILE_EDITOR = 'globalModals/TOGGLE_PROFILE_EDITOR'; const TOGGLE_PROFILE_EDITOR = 'globalModals/TOGGLE_PROFILE_EDITOR';
export const TOGGLE_PROFILE_EDITOR_ERROR = export const TOGGLE_PROFILE_EDITOR_ERROR =
'globalModals/TOGGLE_PROFILE_EDITOR_ERROR'; 'globalModals/TOGGLE_PROFILE_EDITOR_ERROR';
const TOGGLE_SAFETY_NUMBER_MODAL = 'globalModals/TOGGLE_SAFETY_NUMBER_MODAL';
export type ContactModalStateType = { export type ContactModalStateType = {
contactId: string; contactId: string;
@ -39,11 +41,17 @@ export type ToggleProfileEditorErrorActionType = {
type: typeof TOGGLE_PROFILE_EDITOR_ERROR; type: typeof TOGGLE_PROFILE_EDITOR_ERROR;
}; };
type ToggleSafetyNumberModalActionType = {
type: typeof TOGGLE_SAFETY_NUMBER_MODAL;
payload: string | undefined;
};
export type GlobalModalsActionType = export type GlobalModalsActionType =
| HideContactModalActionType | HideContactModalActionType
| ShowContactModalActionType | ShowContactModalActionType
| ToggleProfileEditorActionType | ToggleProfileEditorActionType
| ToggleProfileEditorErrorActionType; | ToggleProfileEditorErrorActionType
| ToggleSafetyNumberModalActionType;
// Action Creators // Action Creators
@ -52,6 +60,7 @@ export const actions = {
showContactModal, showContactModal,
toggleProfileEditor, toggleProfileEditor,
toggleProfileEditorHasError, toggleProfileEditorHasError,
toggleSafetyNumberModal,
}; };
function hideContactModal(): HideContactModalActionType { function hideContactModal(): HideContactModalActionType {
@ -81,6 +90,15 @@ function toggleProfileEditorHasError(): ToggleProfileEditorErrorActionType {
return { type: TOGGLE_PROFILE_EDITOR_ERROR }; return { type: TOGGLE_PROFILE_EDITOR_ERROR };
} }
function toggleSafetyNumberModal(
safetyNumberModalContactId?: string
): ToggleSafetyNumberModalActionType {
return {
type: TOGGLE_SAFETY_NUMBER_MODAL,
payload: safetyNumberModalContactId,
};
}
// Reducer // Reducer
export function getEmptyState(): GlobalModalsStateType { export function getEmptyState(): GlobalModalsStateType {
@ -122,5 +140,12 @@ export function reducer(
}; };
} }
if (action.type === TOGGLE_SAFETY_NUMBER_MODAL) {
return {
...state,
safetyNumberModalContactId: action.payload,
};
}
return state; return state;
} }

View file

@ -8,6 +8,7 @@ import { GlobalModalContainer } from '../../components/GlobalModalContainer';
import { StateType } from '../reducer'; import { StateType } from '../reducer';
import { SmartProfileEditorModal } from './ProfileEditorModal'; import { SmartProfileEditorModal } from './ProfileEditorModal';
import { SmartContactModal } from './ContactModal'; import { SmartContactModal } from './ContactModal';
import { SmartSafetyNumberModal } from './SafetyNumberModal';
const FilteredSmartProfileEditorModal = SmartProfileEditorModal; const FilteredSmartProfileEditorModal = SmartProfileEditorModal;
@ -24,6 +25,11 @@ const mapStateToProps = (state: StateType) => {
...state.globalModals, ...state.globalModals,
renderContactModal, renderContactModal,
renderProfileEditor, renderProfileEditor,
renderSafetyNumber: () => (
<SmartSafetyNumberModal
contactID={String(state.globalModals.safetyNumberModalContactId)}
/>
),
}; };
}; };

View file

@ -0,0 +1,27 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { connect } from 'react-redux';
import { mapDispatchToProps } from '../actions';
import { SafetyNumberModal } from '../../components/SafetyNumberModal';
import { StateType } from '../reducer';
import { getContactSafetyNumber } from '../selectors/safetyNumber';
import { getConversationSelector } from '../selectors/conversations';
import { getIntl } from '../selectors/user';
export type Props = {
contactID: string;
};
const mapStateToProps = (state: StateType, props: Props) => {
return {
...props,
...getContactSafetyNumber(state, props),
contact: getConversationSelector(state)(props.contactID),
i18n: getIntl(state),
};
};
const smart = connect(mapStateToProps, mapDispatchToProps);
export const SmartSafetyNumberModal = smart(SafetyNumberModal);

View file

@ -106,38 +106,6 @@
"updated": "2018-09-18T19:19:27.699Z", "updated": "2018-09-18T19:19:27.699Z",
"reasonDetail": "Very limited in what HTML can be injected - dark/light options specify colors for the light/dark parts of QRCode" "reasonDetail": "Very limited in what HTML can be injected - dark/light options specify colors for the light/dark parts of QRCode"
}, },
{
"rule": "jQuery-$(",
"path": "js/views/key_verification_view.js",
"line": " this.$('.key-verification-wrapper').append(this.view.el);",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Adding sub-view to DOM"
},
{
"rule": "jQuery-$(",
"path": "js/views/key_verification_view.js",
"line": " template: () => $('#key-verification').html(),",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access"
},
{
"rule": "jQuery-append(",
"path": "js/views/key_verification_view.js",
"line": " this.$('.key-verification-wrapper').append(this.view.el);",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Adding sub-view to DOM"
},
{
"rule": "jQuery-html(",
"path": "js/views/key_verification_view.js",
"line": " template: () => $('#key-verification').html(),",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access"
},
{ {
"rule": "jQuery-wrap(", "rule": "jQuery-wrap(",
"path": "node_modules/@chanzuckerberg/axe-storybook-testing/node_modules/ansi-styles/index.js", "path": "node_modules/@chanzuckerberg/axe-storybook-testing/node_modules/ansi-styles/index.js",

View file

@ -2125,10 +2125,9 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
conversation = window.ConversationController.get(id); conversation = window.ConversationController.get(id);
} }
if (conversation) { if (conversation) {
const view = new Whisper.KeyVerificationPanelView({ window.reduxActions.globalModals.toggleSafetyNumberModal(
model: conversation, conversation.get('id')
}); );
this.listenBack(view);
} }
} }