import { useCallback, useEffect } from 'react'; import { sprintf } from 'sprintf-js'; 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'; import { bigText } from './common-styles'; import CustomScrollbars from './CustomScrollbars'; import { Brand, HeaderBarSettingsButton } from './HeaderBar'; import ImageView from './ImageView'; import { Header, Layout, SettingsContainer } from './Layout'; import List from './List'; import { ModalAlert, ModalAlertType, ModalContainer, ModalMessage } from './Modal'; const StyledCustomScrollbars = styled(CustomScrollbars)({ flex: 1, }); const StyledContainer = styled(SettingsContainer)({ paddingTop: '14px', minHeight: '100%', }); const StyledBody = styled.div({ display: 'flex', flexDirection: 'column', flex: 1, paddingBottom: 'auto', }); const StyledFooter = styled.div({ display: 'flex', flexDirection: 'column', flex: 0, padding: '18px 22px 22px', }); const StyledStatusIcon = styled.div({ alignSelf: 'center', width: '60px', height: '60px', marginBottom: '18px', }); const StyledTitle = styled.span(bigText, { lineHeight: '38px', margin: '0 22px 8px', color: colors.white, }); const StyledLabel = styled.span({ fontFamily: 'Open Sans', fontSize: '13px', fontWeight: 600, lineHeight: '20px', color: colors.white, margin: '0 22px 18px', }); const StyledDeviceList = styled(Cell.CellButtonGroup)({ marginBottom: 0, flex: '0 0', }); const StyledSpacer = styled.div({ flex: '1', }); const StyledCellContainer = styled(Cell.Container)({ marginBottom: '1px', }); const StyledDeviceName = styled(Cell.Label)({ textTransform: 'capitalize', }); const StyledRemoveDeviceButton = styled.button({ cursor: 'default', padding: 0, marginLeft: 8, backgroundColor: 'transparent', border: 'none', }); const StyledRemoveSpinner = styled(ImageView)({ alignSelf: 'center', marginTop: '10px', }); export default function TooManyDevices() { const history = useHistory(); const { fetchDevices, removeDevice, login, cancelLogin } = useAppContext(); const accountToken = useSelector((state) => state.account.accountToken)!; const devices = useSelector((state) => state.account.devices); const onRemoveDevice = useCallback( async (deviceId: string) => { await removeDevice({ accountToken, deviceId }); }, [removeDevice, accountToken], ); const continueLogin = useCallback(() => login(accountToken), [login, accountToken]); const cancel = useCallback(() => { cancelLogin(); history.reset(RoutePath.login, transitions.pop); }, [history.reset, cancelLogin]); useEffect(() => void fetchDevices(accountToken), []); const iconSource = getIconSource(devices); const title = getTitle(devices); const subtitle = getSubtitle(devices); return (
{devices !== undefined && ( <> {title} {subtitle} )} {devices !== undefined && ( { // TRANSLATORS: Button for continuing login process. messages.pgettext('device-management', 'Continue with login') } {messages.gettext('Back')} )}
); } interface IDeviceListProps { devices: Array; onRemoveDevice: (deviceId: string) => Promise; } function DeviceList(props: IDeviceListProps) { return ( {(device) => } ); } const getDeviceKey = (device: IDevice): string => device.id; interface IDeviceProps { device: IDevice; onRemove: (deviceId: string) => Promise; } function Device(props: IDeviceProps) { const [confirmationVisible, showConfirmation, hideConfirmation] = useBoolean(false); const [deleting, setDeleting] = useBoolean(false); const onRemove = useCallback(async () => { await props.onRemove(props.device.id); hideConfirmation(); setDeleting(); }, [props.onRemove, props.device.id, hideConfirmation, setDeleting]); const capitalizedDeviceName = capitalizeEveryWord(props.device.name); return ( <> {props.device.name} { // TRANSLATORS: Confirmation button when logging out other device. messages.pgettext('device-management', 'Yes, log out device') } , {messages.gettext('Back')} , ]} close={hideConfirmation}> {deleting ? ( ) : ( <> {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**?', ), { deviceName: capitalizedDeviceName }, ), )} {props.device.ports && props.device.ports.length > 0 && ( { // TRANSLATORS: Further information about consequences of logging out device. messages.pgettext( 'device-management', 'This will delete all forwarded ports. Local settings will be saved.', ) } )} )} ); } function getIconSource(devices?: Array): string { if (devices) { if (devices.length === 5) { return 'icon-fail'; } else { return 'icon-success'; } } else { return 'icon-spinner'; } } function getTitle(devices?: Array): string | undefined { if (devices) { if (devices.length === 5) { // TRANSLATORS: Page title informing user that the login failed due to too many registered // TRANSLATORS: devices on account. return messages.pgettext('device-management', 'Too many devices'); } else { // TRANSLATORS: Page title informing user that enough devices has been removed to continue // TRANSLATORS: login process. return messages.pgettext('device-management', 'Super!'); } } else { return undefined; } } function getSubtitle(devices?: Array): string | undefined { if (devices) { if (devices.length === 5) { return messages.pgettext( 'device-management', 'Please log out of at least one by removing it from the list below. You can find the corresponding device name under the device’s Account settings.', ); } else { return messages.pgettext( 'device-management', 'You can now continue logging in on this device.', ); } } else { return undefined; } }