diff options
| author | Oskar <oskar@mullvad.net> | 2025-08-28 09:50:03 +0200 |
|---|---|---|
| committer | Oskar <oskar@mullvad.net> | 2025-08-29 07:38:40 +0200 |
| commit | 55dd829f370156e9e20bbe76a35fde791740f40d (patch) | |
| tree | 594cf7810eb25d92b076095841483bf49a488ee4 | |
| parent | fc7b1ee2321a69622fb8d83f23d13bded0191e6f (diff) | |
| download | mullvadvpn-55dd829f370156e9e20bbe76a35fde791740f40d.tar.xz mullvadvpn-55dd829f370156e9e20bbe76a35fde791740f40d.zip | |
Add confirmation dialog when clearing account history
| -rw-r--r-- | desktop/packages/mullvad-vpn/src/renderer/components/views/login/ClearAccountHistoryDialog.tsx | 48 | ||||
| -rw-r--r-- | desktop/packages/mullvad-vpn/src/renderer/components/views/login/Login.tsx | 125 |
2 files changed, 121 insertions, 52 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/login/ClearAccountHistoryDialog.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/login/ClearAccountHistoryDialog.tsx new file mode 100644 index 0000000000..b2c3e370d5 --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/login/ClearAccountHistoryDialog.tsx @@ -0,0 +1,48 @@ +import { messages } from '../../../../shared/gettext'; +import { Button } from '../../../lib/components'; +import { ModalAlert, ModalAlertType, ModalMessage } from '../../Modal'; + +interface Props { + visible: boolean; + onConfirm: () => void; + onHide: () => void; +} + +export default function ClearAccountHistoryDialog(props: Props) { + return ( + <ModalAlert + isOpen={props.visible} + type={ModalAlertType.caution} + buttons={[ + <Button variant="destructive" key="confirm" onClick={props.onConfirm}> + <Button.Text> + { + // TRANSLATORS: Button label in confirmation dialog that confirms a remove action. + messages.gettext('Remove') + } + </Button.Text> + </Button>, + <Button key="back" onClick={props.onHide}> + <Button.Text>{messages.gettext('Cancel')}</Button.Text> + </Button>, + ]} + close={props.onHide}> + <ModalMessage> + { + // TRANSLATORS: Text that informs the user about the consequences of clearing the saved + // TRANSLATORS: account number. + messages.pgettext( + 'login-view', + 'Removing the saved account number from this device cannot be undone.', + ) + } + </ModalMessage> + <ModalMessage> + { + // TRANSLATORS: Text that asks the user if they really want to remove the saved account. + messages.pgettext('login-view', 'Do you want to remove the saved account number?') + } + </ModalMessage> + </ModalAlert> + ); +} diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/login/Login.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/login/Login.tsx index 4446ae96ff..395467ac14 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/views/login/Login.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/login/Login.tsx @@ -27,6 +27,7 @@ import { useSelector } from '../../../redux/store'; import Accordion from '../../Accordion'; import { AppMainHeader } from '../../app-main-header'; import { Container, Layout } from '../../Layout'; +import ClearAccountHistoryDialog from './ClearAccountHistoryDialog'; import { StyledAccountDropdownContainer, StyledAccountDropdownItem, @@ -96,6 +97,7 @@ interface IProps { interface IState { isActive: boolean; + clearAccountHistoryDialogVisible: boolean; } const MIN_ACCOUNT_NUMBER_LENGTH = 10; @@ -103,6 +105,7 @@ const MIN_ACCOUNT_NUMBER_LENGTH = 10; class Login extends React.Component<IProps, IState> { public state: IState = { isActive: true, + clearAccountHistoryDialogVisible: false, }; private accountInput = React.createRef<HTMLInputElement>(); @@ -299,9 +302,18 @@ class Login extends React.Component<IProps, IState> { }; private onClearAccountHistory = () => { + this.setState({ clearAccountHistoryDialogVisible: true }); + }; + + private onConfirmClearAccountHistory = () => { + this.hideClearAccountHistoryDialog(); void this.clearAccountHistory(); }; + private hideClearAccountHistoryDialog = () => { + this.setState({ clearAccountHistoryDialogVisible: false }); + }; + private async clearAccountHistory() { try { await this.props.clearAccountHistory(); @@ -321,59 +333,68 @@ class Login extends React.Component<IProps, IState> { this.props.loginState.method === 'existing_account'; return ( - <Flex $flexDirection="column" $gap="small"> - <Label htmlFor={inputId} data-testid="subtitle"> - {this.formSubtitle()} - </Label> - <StyledAccountInputGroup - $active={allowInteraction && this.state.isActive} - $editable={allowInteraction} - $error={hasError} - onSubmit={this.onSubmit}> - <StyledAccountInputBackdrop> - <StyledInput - id={inputId} - allowedCharacters="[0-9]" - separator=" " - groupLength={4} - placeholder="0000 0000 0000 0000" - value={this.props.accountNumber || ''} - disabled={!allowInteraction} - onFocus={this.onFocus} - onBlur={this.onBlur} - handleChange={this.onInputChange} - autoFocus={true} - ref={this.accountInput} - aria-autocomplete="list" - /> - <StyledInputButton - type="submit" - $visible={allowLogin} - disabled={!allowLogin} - aria-label={ - // TRANSLATORS: This is used by screenreaders to communicate the login button. - messages.pgettext('accessibility', 'Login') - }> - <StyledInputSubmitIcon - $visible={ - this.props.loginState.type !== 'logging in' && !this.props.isPerformingPostUpgrade - } - icon="chevron-right" - size="large" + <> + <Flex $flexDirection="column" $gap="small"> + <Label htmlFor={inputId} data-testid="subtitle"> + {this.formSubtitle()} + </Label> + <StyledAccountInputGroup + $active={allowInteraction && this.state.isActive} + $editable={allowInteraction} + $error={hasError} + onSubmit={this.onSubmit}> + <StyledAccountInputBackdrop> + <StyledInput + id={inputId} + allowedCharacters="[0-9]" + separator=" " + groupLength={4} + placeholder="0000 0000 0000 0000" + value={this.props.accountNumber || ''} + disabled={!allowInteraction} + onFocus={this.onFocus} + onBlur={this.onBlur} + handleChange={this.onInputChange} + autoFocus={true} + ref={this.accountInput} + aria-autocomplete="list" /> - </StyledInputButton> - </StyledAccountInputBackdrop> - <Accordion expanded={this.shouldShowAccountHistory()}> - <StyledAccountDropdownContainer> - <AccountDropdown - item={this.props.accountHistory} - onSelect={this.onSelectAccountFromHistory} - onRemove={this.onClearAccountHistory} - /> - </StyledAccountDropdownContainer> - </Accordion> - </StyledAccountInputGroup> - </Flex> + <StyledInputButton + type="submit" + $visible={allowLogin} + disabled={!allowLogin} + aria-label={ + // TRANSLATORS: This is used by screenreaders to communicate the login button. + messages.pgettext('accessibility', 'Login') + }> + <StyledInputSubmitIcon + $visible={ + this.props.loginState.type !== 'logging in' && + !this.props.isPerformingPostUpgrade + } + icon="chevron-right" + size="large" + /> + </StyledInputButton> + </StyledAccountInputBackdrop> + <Accordion expanded={this.shouldShowAccountHistory()}> + <StyledAccountDropdownContainer> + <AccountDropdown + item={this.props.accountHistory} + onSelect={this.onSelectAccountFromHistory} + onRemove={this.onClearAccountHistory} + /> + </StyledAccountDropdownContainer> + </Accordion> + </StyledAccountInputGroup> + </Flex> + + <ClearAccountHistoryDialog + visible={this.state.clearAccountHistoryDialogVisible} + onConfirm={this.onConfirmClearAccountHistory} + onHide={this.hideClearAccountHistoryDialog} + /> + </> ); } |
