diff options
Diffstat (limited to 'gui/src/renderer/components')
| -rw-r--r-- | gui/src/renderer/components/Account.tsx | 92 | ||||
| -rw-r--r-- | gui/src/renderer/components/AccountStyles.tsx | 7 | ||||
| -rw-r--r-- | gui/src/renderer/components/TooManyDevices.tsx | 70 |
3 files changed, 135 insertions, 34 deletions
diff --git a/gui/src/renderer/components/Account.tsx b/gui/src/renderer/components/Account.tsx index a612f2a07e..4a84e3c110 100644 --- a/gui/src/renderer/components/Account.tsx +++ b/gui/src/renderer/components/Account.tsx @@ -10,6 +10,7 @@ import { AccountRows, AccountRowValue, DeviceRowValue, + StyledSpinnerContainer, StyledBuyCreditButton, StyledContainer, StyledRedeemVoucherButton, @@ -18,10 +19,12 @@ import AccountTokenLabel from './AccountTokenLabel'; import * as AppButton from './AppButton'; import { AriaDescribed, AriaDescription, AriaDescriptionGroup } from './AriaGroup'; import { Layout } from './Layout'; +import { ModalAlert, ModalAlertType, ModalMessage } from './Modal'; import { NavigationBar, NavigationItems, TitleBarItem } from './NavigationBar'; import SettingsHeader, { HeaderTitle } from './SettingsHeader'; -import { AccountToken } from '../../shared/daemon-rpc-types'; +import { AccountToken, IDevice } from '../../shared/daemon-rpc-types'; +import ImageView from './ImageView'; import { BackAction } from './KeyboardNavigation'; interface IProps { @@ -30,13 +33,22 @@ interface IProps { accountExpiry?: string; expiryLocale: string; isOffline: boolean; + prepareLogout: () => void; + cancelLogout: () => void; onLogout: () => void; onClose: () => void; onBuyMore: () => Promise<void>; updateAccountData: () => void; + getDevice: () => Promise<IDevice | undefined>; } -export default class Account extends React.Component<IProps> { +interface IState { + logoutDialogState: 'hidden' | 'checking-ports' | 'confirm'; +} + +export default class Account extends React.Component<IProps, IState> { + public state: IState = { logoutDialogState: 'hidden' }; + public componentDidMount() { this.props.updateAccountData(); } @@ -65,7 +77,7 @@ export default class Account extends React.Component<IProps> { <AccountRows> <AccountRow> <AccountRowLabel> - {messages.pgettext('account-view', 'Device name')} + {messages.pgettext('device-management', 'Device name')} </AccountRowLabel> <DeviceRowValue>{this.props.deviceName}</DeviceRowValue> </AccountRow> @@ -114,16 +126,88 @@ export default class Account extends React.Component<IProps> { <StyledRedeemVoucherButton /> - <AppButton.RedButton onClick={this.props.onLogout}> + <AppButton.RedButton onClick={this.onTryLogout}> {messages.pgettext('account-view', 'Log out')} </AppButton.RedButton> </AccountFooter> </AccountContainer> </StyledContainer> + + {this.renderLogoutDialog()} </Layout> </BackAction> ); } + + private renderLogoutDialog() { + const modalType = + this.state.logoutDialogState === 'checking-ports' ? undefined : ModalAlertType.warning; + + const message = + this.state.logoutDialogState === 'checking-ports' ? ( + <StyledSpinnerContainer> + <ImageView source="icon-spinner" width={60} height={60} /> + </StyledSpinnerContainer> + ) : ( + <ModalMessage> + { + // TRANSLATORS: This is is a further explanation of what happens when logging out. + messages.pgettext( + 'device-management', + 'The ports forwarded to this device will be deleted if you log out.', + ) + } + </ModalMessage> + ); + + const buttons = + this.state.logoutDialogState === 'checking-ports' + ? [] + : [ + <AppButton.RedButton key="logout" onClick={this.props.onLogout}> + { + // TRANSLATORS: Confirmation button when logging out + messages.pgettext('device-management', 'Log out anyway') + } + </AppButton.RedButton>, + <AppButton.BlueButton key="back" onClick={this.cancelLogout}> + {messages.gettext('Back')} + </AppButton.BlueButton>, + ]; + + return ( + <ModalAlert + isOpen={this.state.logoutDialogState !== 'hidden'} + type={modalType} + buttons={buttons}> + {message} + </ModalAlert> + ); + } + + private onTryLogout = async () => { + this.setState({ logoutDialogState: 'checking-ports' }); + this.props.prepareLogout(); + + const device = await this.props.getDevice(); + if (device === undefined) { + this.onHideLogoutConfirmationDialog(); + } else if (device.ports !== undefined && device.ports.length > 0) { + this.setState({ logoutDialogState: 'confirm' }); + } else { + this.props.onLogout(); + this.onHideLogoutConfirmationDialog(); + } + }; + + private cancelLogout = () => { + this.props.cancelLogout(); + this.onHideLogoutConfirmationDialog(); + }; + + private onHideLogoutConfirmationDialog = () => { + this.setState({ logoutDialogState: 'hidden' }); + }; } function FormattedAccountExpiry(props: { expiry?: string; locale: string }) { diff --git a/gui/src/renderer/components/AccountStyles.tsx b/gui/src/renderer/components/AccountStyles.tsx index 34ce600fde..549b0cda7d 100644 --- a/gui/src/renderer/components/AccountStyles.tsx +++ b/gui/src/renderer/components/AccountStyles.tsx @@ -52,6 +52,13 @@ export const AccountOutOfTime = styled(AccountRowValue)({ color: colors.red, }); +export const StyledSpinnerContainer = styled.div({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + padding: '8px 0', +}); + export const AccountFooter = styled.div({ display: 'flex', flexDirection: 'column', diff --git a/gui/src/renderer/components/TooManyDevices.tsx b/gui/src/renderer/components/TooManyDevices.tsx index 56ecbd99c1..b743dd2aec 100644 --- a/gui/src/renderer/components/TooManyDevices.tsx +++ b/gui/src/renderer/components/TooManyDevices.tsx @@ -4,10 +4,12 @@ import styled from 'styled-components'; import { colors } from '../../config.json'; import { IDevice } from '../../shared/daemon-rpc-types'; import { messages } from '../../shared/gettext'; +import { capitalizeEveryWord } from '../../shared/string-helpers'; import { useAppContext } from '../context'; import { transitions, useHistory } from '../lib/history'; import { RoutePath } from '../lib/routes'; import { useBoolean } from '../lib/utilityHooks'; +import { formatMarkdown } from '../markdown-formatter'; import { useSelector } from '../redux/store'; import * as AppButton from './AppButton'; import * as Cell from './cell'; @@ -192,6 +194,8 @@ function Device(props: IDeviceProps) { setDeleting(); }, [props.onRemove, props.device.id, hideConfirmation, setDeleting]); + const capitalizedDeviceName = capitalizeEveryWord(props.device.name); + return ( <> <StyledCellContainer> @@ -213,36 +217,42 @@ function Device(props: IDeviceProps) { /> </StyledRemoveDeviceButton> </StyledCellContainer> - {confirmationVisible && ( - <ModalAlert - type={ModalAlertType.warning} - iconColor={colors.red} - buttons={[ - <AppButton.RedButton key="remove" onClick={onRemove} disabled={deleting}> - { - // TRANSLATORS: Confirmation button when logging out other device. - messages.pgettext('device-management', 'Yes, log out device') - } - </AppButton.RedButton>, - <AppButton.BlueButton key="back" onClick={hideConfirmation} disabled={deleting}> - {messages.gettext('Back')} - </AppButton.BlueButton>, - ]} - close={hideConfirmation}> - {deleting ? ( - <ImageView source="icon-spinner" /> - ) : ( - <> - <ModalMessage> - {sprintf( + <ModalAlert + isOpen={confirmationVisible} + type={ModalAlertType.warning} + iconColor={colors.red} + buttons={[ + <AppButton.RedButton key="remove" onClick={onRemove} disabled={deleting}> + { + // TRANSLATORS: Confirmation button when logging out other device. + messages.pgettext('device-management', 'Yes, log out device') + } + </AppButton.RedButton>, + <AppButton.BlueButton key="back" onClick={hideConfirmation} disabled={deleting}> + {messages.gettext('Back')} + </AppButton.BlueButton>, + ]} + close={hideConfirmation}> + {deleting ? ( + <ImageView source="icon-spinner" /> + ) : ( + <> + <ModalMessage> + {formatMarkdown( + sprintf( // TRANSLATORS: Text displayed above button which logs out another device. + // TRANSLATORS: The text enclosed in "**" will appear bold. + // TRANSLATORS: Available placeholders: + // TRANSLATORS: %(deviceName)s - The name of the device to log out. messages.pgettext( 'device-management', - 'Are you sure you want to log out of %(deviceName)s?', + 'Are you sure you want to log out of **%(deviceName)s**?', ), - { deviceName: props.device.name }, - )} - </ModalMessage> + { deviceName: capitalizedDeviceName }, + ), + )} + </ModalMessage> + {props.device.ports && props.device.ports.length > 0 && ( <ModalMessage> { // TRANSLATORS: Further information about consequences of logging out device. @@ -252,10 +262,10 @@ function Device(props: IDeviceProps) { ) } </ModalMessage> - </> - )} - </ModalAlert> - )} + )} + </> + )} + </ModalAlert> </> ); } |
