From f1ee7589a01172895c824e6d9063c35c25daf125 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Thu, 24 Jun 2021 10:50:35 +1000 Subject: [PATCH] fix session password dialog --- ts/components/session/ActionsPanel.tsx | 37 +--------- ts/components/session/ModalContainer.tsx | 68 +++++++++++++++++++ .../session/SessionPasswordModal.tsx | 67 +++++++----------- .../conversation/SessionCompositionBox.tsx | 27 +++----- .../conversation/SessionConversation.tsx | 1 - .../session/settings/SessionSettings.tsx | 46 ++++--------- ts/state/ducks/modalDialog.tsx | 9 +++ ts/state/selectors/modal.ts | 6 ++ 8 files changed, 135 insertions(+), 126 deletions(-) create mode 100644 ts/components/session/ModalContainer.tsx diff --git a/ts/components/session/ActionsPanel.tsx b/ts/components/session/ActionsPanel.tsx index 725cb8244..45910298b 100644 --- a/ts/components/session/ActionsPanel.tsx +++ b/ts/components/session/ActionsPanel.tsx @@ -73,6 +73,7 @@ import { editProfileModal, onionPathModal } from '../../state/ducks/modalDialog' import { SessionSeedModal } from './SessionSeedModal'; import { AdminLeaveClosedGroupDialog } from '../conversation/AdminLeaveClosedGroupDialog'; import { uploadOurAvatar } from '../../interactions/conversationInteractions'; +import { ModalContainer } from './ModalContainer'; // tslint:disable-next-line: no-import-side-effect no-submodule-imports @@ -279,42 +280,6 @@ const doAppStartUp = () => { SwarmPolling.getInstance().start(); }; -const ModalContainer = () => { - const confirmModalState = useSelector(getConfirmModal); - const inviteModalState = useSelector(getInviteContactModal); - const addModeratorsModalState = useSelector(getAddModeratorsModal); - const removeModeratorsModalState = useSelector(getRemoveModeratorsModal); - const updateGroupMembersModalState = useSelector(getUpdateGroupMembersModal); - const updateGroupNameModalState = useSelector(getUpdateGroupNameModal); - const userDetailsModalState = useSelector(getUserDetailsModal); - const changeNicknameModal = useSelector(getChangeNickNameDialog); - const editProfileModalState = useSelector(getEditProfileDialog); - const onionPathModalState = useSelector(getOnionPathDialog); - const recoveryPhraseModalState = useSelector(getRecoveryPhraseDialog); - const adminLeaveClosedGroupModalState = useSelector(getAdminLeaveClosedGroupDialog); - - return ( - <> - {confirmModalState && } - {inviteModalState && } - {addModeratorsModalState && } - {removeModeratorsModalState && } - {updateGroupMembersModalState && ( - - )} - {updateGroupNameModalState && } - {userDetailsModalState && } - {changeNicknameModal && } - {editProfileModalState && } - {onionPathModalState && } - {recoveryPhraseModalState && } - {adminLeaveClosedGroupModalState && ( - - )} - - ); -}; - /** * ActionsPanel is the far left banner (not the left pane). * The panel with buttons to switch between the message/contact/settings/theme views diff --git a/ts/components/session/ModalContainer.tsx b/ts/components/session/ModalContainer.tsx new file mode 100644 index 000000000..5560a7617 --- /dev/null +++ b/ts/components/session/ModalContainer.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import { + getAddModeratorsModal, + getAdminLeaveClosedGroupDialog, + getChangeNickNameDialog, + getConfirmModal, + getEditProfileDialog, + getInviteContactModal, + getOnionPathDialog, + getRecoveryPhraseDialog, + getRemoveModeratorsModal, + getSessionPasswordDialog, + getUpdateGroupMembersModal, + getUpdateGroupNameModal, + getUserDetailsModal, +} from '../../state/selectors/modal'; +import { AdminLeaveClosedGroupDialog } from '../conversation/AdminLeaveClosedGroupDialog'; +import { InviteContactsDialog } from '../conversation/InviteContactsDialog'; +import { AddModeratorsDialog } from '../conversation/ModeratorsAddDialog'; +import { RemoveModeratorsDialog } from '../conversation/ModeratorsRemoveDialog'; +import { UpdateGroupMembersDialog } from '../conversation/UpdateGroupMembersDialog'; +import { UpdateGroupNameDialog } from '../conversation/UpdateGroupNameDialog'; +import { EditProfileDialog } from '../EditProfileDialog'; +import { OnionPathModal } from '../OnionStatusDialog'; +import { UserDetailsDialog } from '../UserDetailsDialog'; +import { SessionConfirm } from './SessionConfirm'; +import { SessionNicknameDialog } from './SessionNicknameDialog'; +import { SessionPasswordModal } from './SessionPasswordModal'; +import { SessionSeedModal } from './SessionSeedModal'; + +export const ModalContainer = () => { + const confirmModalState = useSelector(getConfirmModal); + const inviteModalState = useSelector(getInviteContactModal); + const addModeratorsModalState = useSelector(getAddModeratorsModal); + const removeModeratorsModalState = useSelector(getRemoveModeratorsModal); + const updateGroupMembersModalState = useSelector(getUpdateGroupMembersModal); + const updateGroupNameModalState = useSelector(getUpdateGroupNameModal); + const userDetailsModalState = useSelector(getUserDetailsModal); + const changeNicknameModal = useSelector(getChangeNickNameDialog); + const editProfileModalState = useSelector(getEditProfileDialog); + const onionPathModalState = useSelector(getOnionPathDialog); + const recoveryPhraseModalState = useSelector(getRecoveryPhraseDialog); + const adminLeaveClosedGroupModalState = useSelector(getAdminLeaveClosedGroupDialog); + const sessionPasswordModalState = useSelector(getSessionPasswordDialog); + + return ( + <> + {confirmModalState && } + {inviteModalState && } + {addModeratorsModalState && } + {removeModeratorsModalState && } + {updateGroupMembersModalState && ( + + )} + {updateGroupNameModalState && } + {userDetailsModalState && } + {changeNicknameModal && } + {editProfileModalState && } + {onionPathModalState && } + {recoveryPhraseModalState && } + {adminLeaveClosedGroupModalState && ( + + )} + {sessionPasswordModalState && } + + ); +}; diff --git a/ts/components/session/SessionPasswordModal.tsx b/ts/components/session/SessionPasswordModal.tsx index 54ce013ec..8e6c0777e 100644 --- a/ts/components/session/SessionPasswordModal.tsx +++ b/ts/components/session/SessionPasswordModal.tsx @@ -1,25 +1,19 @@ import React from 'react'; -import { SessionModal } from './SessionModal'; import { SessionButton, SessionButtonColor } from './SessionButton'; import { missingCaseError, PasswordUtil } from '../../util/'; import { ToastUtils } from '../../session/utils'; import { SessionIconType } from './icon'; -import { DefaultTheme, withTheme } from 'styled-components'; import { getPasswordHash } from '../../data/data'; import { SessionWrapperModal } from './SessionWrapperModal'; import { SpacerLG, SpacerSM } from '../basic/Text'; -export enum PasswordAction { - Set = 'set', - Change = 'change', - Remove = 'remove', -} +import autoBind from 'auto-bind'; +import { sessionPassword } from '../../state/ducks/modalDialog'; +export type PasswordAction = 'set' | 'change' | 'remove'; interface Props { - action: PasswordAction; - onOk: any; - onClose: any; - theme: DefaultTheme; + passwordAction: PasswordAction; + onOk: () => void; } interface State { @@ -29,7 +23,7 @@ interface State { currentPasswordRetypeEntered: string | null; } -class SessionPasswordModalInner extends React.Component { +export class SessionPasswordModal extends React.Component { private passportInput: HTMLInputElement | null = null; constructor(props: any) { @@ -42,14 +36,7 @@ class SessionPasswordModalInner extends React.Component { currentPasswordRetypeEntered: null, }; - this.showError = this.showError.bind(this); - - this.setPassword = this.setPassword.bind(this); - this.closeDialog = this.closeDialog.bind(this); - - this.onPasswordInput = this.onPasswordInput.bind(this); - this.onPasswordConfirmInput = this.onPasswordConfirmInput.bind(this); - this.onPasswordRetypeInput = this.onPasswordRetypeInput.bind(this); + autoBind(this); } public componentDidMount() { @@ -60,9 +47,9 @@ class SessionPasswordModalInner extends React.Component { } public render() { - const { action, onOk } = this.props; + const { passwordAction } = this.props; const placeholders = - action === PasswordAction.Change + passwordAction === 'change' ? [ window.i18n('typeInOldPassword'), window.i18n('enterPassword'), @@ -71,10 +58,13 @@ class SessionPasswordModalInner extends React.Component { : [window.i18n('enterPassword'), window.i18n('confirmPassword')]; const confirmButtonColor = - action === PasswordAction.Remove ? SessionButtonColor.Danger : SessionButtonColor.Primary; + passwordAction === 'remove' ? SessionButtonColor.Danger : SessionButtonColor.Primary; return ( - +
@@ -87,7 +77,7 @@ class SessionPasswordModalInner extends React.Component { placeholder={placeholders[0]} onKeyUp={this.onPasswordInput} /> - {action !== PasswordAction.Remove && ( + {passwordAction !== 'remove' && ( { onKeyUp={this.onPasswordConfirmInput} /> )} - {action === PasswordAction.Change && ( + {passwordAction === 'change' && ( { SessionIconType.Lock ); - this.props.onOk(this.props.action); + this.props.onOk(); this.closeDialog(); } @@ -222,7 +212,7 @@ class SessionPasswordModalInner extends React.Component { SessionIconType.Lock ); - this.props.onOk(this.props.action); + this.props.onOk(); this.closeDialog(); } @@ -243,31 +233,30 @@ class SessionPasswordModalInner extends React.Component { window.i18n('removePasswordToastDescription') ); - this.props.onOk(this.props.action); + this.props.onOk(); this.closeDialog(); } // tslint:disable-next-line: cyclomatic-complexity private async setPassword() { - const { action } = this.props; + const { passwordAction } = this.props; const { currentPasswordEntered, currentPasswordConfirmEntered, currentPasswordRetypeEntered, } = this.state; - const { Set, Remove, Change } = PasswordAction; // Trim leading / trailing whitespace for UX const firstPasswordEntered = (currentPasswordEntered || '').trim(); const secondPasswordEntered = (currentPasswordConfirmEntered || '').trim(); const thirdPasswordEntered = (currentPasswordRetypeEntered || '').trim(); - switch (action) { - case Set: { + switch (passwordAction) { + case 'set': { await this.handleActionSet(firstPasswordEntered, secondPasswordEntered); return; } - case Change: { + case 'change': { await this.handleActionChange( firstPasswordEntered, secondPasswordEntered, @@ -275,19 +264,17 @@ class SessionPasswordModalInner extends React.Component { ); return; } - case Remove: { + case 'remove': { await this.handleActionRemove(firstPasswordEntered); return; } default: - throw missingCaseError(action); + throw missingCaseError(passwordAction); } } private closeDialog() { - if (this.props.onClose) { - this.props.onClose(); - } + window.inboxStore?.dispatch(sessionPassword(null)); } private async onPasswordInput(event: any) { @@ -317,5 +304,3 @@ class SessionPasswordModalInner extends React.Component { this.setState({ currentPasswordRetypeEntered }); } } - -export const SessionPasswordModal = withTheme(SessionPasswordModalInner); diff --git a/ts/components/session/conversation/SessionCompositionBox.tsx b/ts/components/session/conversation/SessionCompositionBox.tsx index 40c431a6b..dca24b74a 100644 --- a/ts/components/session/conversation/SessionCompositionBox.tsx +++ b/ts/components/session/conversation/SessionCompositionBox.tsx @@ -33,15 +33,9 @@ import { SessionMemberListItem } from '../SessionMemberListItem'; import autoBind from 'auto-bind'; import { SectionType } from '../ActionsPanel'; import { SessionSettingCategory } from '../settings/SessionSettings'; -import { - defaultMentionsInputReducer, - updateMentionsMembers, -} from '../../../state/ducks/mentionsInput'; import { getMentionsInput } from '../../../state/selectors/mentionsInput'; -import { useDispatch } from 'react-redux'; import { updateConfirmModal } from '../../../state/ducks/modalDialog'; import { SessionButtonColor } from '../SessionButton'; -import { any } from 'underscore'; import { SessionConfirmDialogProps } from '../SessionConfirm'; export interface ReplyingToMessageProps { @@ -93,7 +87,6 @@ interface Props { showLeftPaneSection: (section: SectionType) => void; showSettingsSection: (category: SessionSettingCategory) => void; theme: DefaultTheme; - updateConfirmModal: (props: SessionConfirmDialogProps) => any; } interface State { @@ -247,15 +240,17 @@ export class SessionCompositionBox extends React.Component { private showLinkSharingConfirmationModalDialog(e: any) { const pastedText = e.clipboardData.getData('text'); if (this.isURL(pastedText)) { - this.props.updateConfirmModal({ - shouldShowConfirm: () => !window.getSettingValue('link-preview-setting'), - title: window.i18n('linkPreviewsTitle'), - message: window.i18n('linkPreviewsConfirmMessage'), - okTheme: SessionButtonColor.Danger, - onClickOk: () => { - window.setSettingValue('link-preview-setting', true); - }, - }); + window.inboxStore?.dispatch( + updateConfirmModal({ + shouldShowConfirm: () => !window.getSettingValue('link-preview-setting'), + title: window.i18n('linkPreviewsTitle'), + message: window.i18n('linkPreviewsConfirmMessage'), + okTheme: SessionButtonColor.Danger, + onClickOk: () => { + window.setSettingValue('link-preview-setting', true); + }, + }) + ); } } diff --git a/ts/components/session/conversation/SessionConversation.tsx b/ts/components/session/conversation/SessionConversation.tsx index 55b01dae2..c599965a5 100644 --- a/ts/components/session/conversation/SessionConversation.tsx +++ b/ts/components/session/conversation/SessionConversation.tsx @@ -304,7 +304,6 @@ export class SessionConversation extends React.Component { removeAttachment={this.removeAttachment} onChoseAttachments={this.onChoseAttachments} theme={this.props.theme} - updateConfirmModal={actions.updateConfirmModal} />
diff --git a/ts/components/session/settings/SessionSettings.tsx b/ts/components/session/settings/SessionSettings.tsx index 2d3edaa94..772e8204c 100644 --- a/ts/components/session/settings/SessionSettings.tsx +++ b/ts/components/session/settings/SessionSettings.tsx @@ -13,10 +13,11 @@ import { connect } from 'react-redux'; import { getPasswordHash } from '../../../../ts/data/data'; import { SpacerLG, SpacerXS } from '../../basic/Text'; import { shell } from 'electron'; -import { PasswordAction, SessionPasswordModal } from '../SessionPasswordModal'; import { SessionConfirmDialogProps } from '../SessionConfirm'; import { mapDispatchToProps } from '../../../state/actions'; import { unblockConvoById } from '../../../interactions/conversationInteractions'; +import { sessionPassword } from '../../../state/ducks/modalDialog'; +import { PasswordAction } from '../SessionPasswordModal'; export enum SessionSettingCategory { Appearance = 'appearance', @@ -47,7 +48,6 @@ interface State { pwdLockError: string | null; mediaSetting: boolean | null; shouldLockSettings: boolean | null; - modal: JSX.Element | null; } interface ConfirmationDialogParams extends SessionConfirmDialogProps { @@ -80,7 +80,6 @@ class SettingsViewInner extends React.Component { pwdLockError: null, mediaSetting: null, shouldLockSettings: true, - modal: null, }; this.settingsViewRef = React.createRef(); @@ -174,12 +173,8 @@ class SettingsViewInner extends React.Component {

{window.i18n('password')}

- {this.state.pwdLockError && ( - <> -
{this.state.pwdLockError}
- - +
{this.state.pwdLockError}
)} { categoryTitle={window.i18n(`${category}SettingsTitle`)} /> - {this.state.modal ? this.state.modal : null} -
{shouldRenderPasswordLock ? ( this.renderPasswordLock() @@ -512,7 +505,7 @@ class SettingsViewInner extends React.Component { buttonColor: SessionButtonColor.Primary, }, onClick: () => { - this.displayPasswordModal(PasswordAction.Set); + this.displayPasswordModal('set'); }, confirmationDialogParams: undefined, }, @@ -530,7 +523,7 @@ class SettingsViewInner extends React.Component { buttonColor: SessionButtonColor.Primary, }, onClick: () => { - this.displayPasswordModal(PasswordAction.Change); + this.displayPasswordModal('change'); }, confirmationDialogParams: undefined, }, @@ -548,7 +541,7 @@ class SettingsViewInner extends React.Component { buttonColor: SessionButtonColor.Danger, }, onClick: () => { - this.displayPasswordModal(PasswordAction.Remove); + this.displayPasswordModal('remove'); }, confirmationDialogParams: undefined, }, @@ -556,25 +549,14 @@ class SettingsViewInner extends React.Component { } private displayPasswordModal(passwordAction: PasswordAction) { - this.setState({ - ...this.state, - modal: ( - { - this.clearModal(); - }} - onOk={this.onPasswordUpdated} - action={passwordAction} - /> - ), - }); - } - - private clearModal(): void { - this.setState({ - ...this.state, - modal: null, - }); + window.inboxStore?.dispatch( + sessionPassword({ + passwordAction, + onOk: () => { + this.onPasswordUpdated(passwordAction); + }, + }) + ); } private getBlockedUserSettings(): Array { diff --git a/ts/state/ducks/modalDialog.tsx b/ts/state/ducks/modalDialog.tsx index 9fba3bfa8..91ce81173 100644 --- a/ts/state/ducks/modalDialog.tsx +++ b/ts/state/ducks/modalDialog.tsx @@ -1,5 +1,6 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { SessionConfirmDialogProps } from '../../components/session/SessionConfirm'; +import { PasswordAction } from '../../components/session/SessionPasswordModal'; export type ConfirmModalState = SessionConfirmDialogProps | null; export type InviteContactModalState = { conversationId: string } | null; @@ -13,6 +14,8 @@ export type EditProfileModalState = {} | null; export type OnionPathModalState = EditProfileModalState; export type RecoveryPhraseModalState = EditProfileModalState; +export type SessionPasswordModalState = { passwordAction: PasswordAction; onOk: () => void } | null; + export type UserDetailsModalState = { conversationId: string; authorAvatarPath?: string; @@ -32,6 +35,7 @@ export type ModalState = { onionPathModal: OnionPathModalState; recoveryPhraseModal: RecoveryPhraseModalState; adminLeaveClosedGroup: AdminLeaveClosedGroupModalState; + sessionPasswordModal: SessionPasswordModalState; }; export const initialModalState: ModalState = { @@ -47,6 +51,7 @@ export const initialModalState: ModalState = { onionPathModal: null, recoveryPhraseModal: null, adminLeaveClosedGroup: null, + sessionPasswordModal: null, }; const ModalSlice = createSlice({ @@ -89,6 +94,9 @@ const ModalSlice = createSlice({ adminLeaveClosedGroup(state, action: PayloadAction) { return { ...state, adminLeaveClosedGroup: action.payload }; }, + sessionPassword(state, action: PayloadAction) { + return { ...state, sessionPasswordModal: action.payload }; + }, }, }); @@ -106,5 +114,6 @@ export const { onionPathModal, recoveryPhraseModal, adminLeaveClosedGroup, + sessionPassword, } = actions; export const modalReducer = reducer; diff --git a/ts/state/selectors/modal.ts b/ts/state/selectors/modal.ts index 769b18e23..ec3f765a8 100644 --- a/ts/state/selectors/modal.ts +++ b/ts/state/selectors/modal.ts @@ -12,6 +12,7 @@ import { OnionPathModalState, RecoveryPhraseModalState, RemoveModeratorsModalState, + SessionPasswordModalState, UpdateGroupMembersModalState, UpdateGroupNameModalState, UserDetailsModalState, @@ -80,3 +81,8 @@ export const getAdminLeaveClosedGroupDialog = createSelector( getModal, (state: ModalState): AdminLeaveClosedGroupModalState => state.adminLeaveClosedGroup ); + +export const getSessionPasswordDialog = createSelector( + getModal, + (state: ModalState): SessionPasswordModalState => state.sessionPasswordModal +);