import { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState, } from 'react'; import { links } from '../../config.json'; import { formatDate, hasExpired } from '../../shared/account-expiry'; import { messages } from '../../shared/gettext'; import { useAppContext } from '../context'; import useActions from '../lib/actionsHook'; import { useHistory } from '../lib/history'; import { useBoolean } from '../lib/utilityHooks'; import account from '../redux/account/actions'; import { useSelector } from '../redux/store'; import { AccountContainer, AccountOutOfTime, AccountRow, AccountRowLabel, AccountRows, AccountRowValue, DeviceRowValue, StyledDeviceNameRow, StyledSpinnerContainer, } from './AccountStyles'; import AccountTokenLabel from './AccountTokenLabel'; import * as AppButton from './AppButton'; import { AriaDescribed, AriaDescription, AriaDescriptionGroup } from './AriaGroup'; import DeviceInfoButton from './DeviceInfoButton'; import ImageView from './ImageView'; import { BackAction } from './KeyboardNavigation'; import { Footer, Layout, SettingsContainer } from './Layout'; import { ModalAlert, ModalAlertType, ModalMessage } from './Modal'; import { NavigationBar, NavigationItems, TitleBarItem } from './NavigationBar'; import { RedeemVoucherButton } from './RedeemVoucher'; import SettingsHeader, { HeaderTitle } from './SettingsHeader'; type DialogStage = 'checking-ports' | 'confirm' | undefined; export default function Account() { return ( ); } function AccountComponent() { const history = useHistory(); const isOffline = useSelector((state) => state.connection.isBlocked); const { updateAccountData, openLinkWithAuth } = useAppContext(); const { onTryLogout } = useLogoutContext(); const onBuyMore = useCallback(async () => { await openLinkWithAuth(links.purchase); }, []); useEffect(() => { updateAccountData(); }, []); return ( { // TRANSLATORS: Title label in navigation bar messages.pgettext('account-view', 'Account') } {messages.pgettext('account-view', 'Account')} {messages.pgettext('device-management', 'Device name')} {messages.pgettext('account-view', 'Account number')} {messages.pgettext('account-view', 'Paid until')}
{messages.gettext('Buy more credit')} {messages.pgettext('account-view', 'Log out')}
); } function LogoutDialog() { const { dialogStage, dialogVisible, onConfirmLogout, onCancelLogout } = useLogoutContext(); const modalType = dialogStage === 'checking-ports' ? undefined : ModalAlertType.warning; const message = dialogStage === 'checking-ports' ? ( ) : ( { // 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.', ) } ); const buttons = dialogStage === 'checking-ports' ? [] : [ { // TRANSLATORS: Confirmation button when logging out messages.pgettext('device-management', 'Log out anyway') } , {messages.gettext('Back')} , ]; return ( {message} ); } function DeviceNameRow() { const deviceName = useSelector((state) => state.account.deviceName); return ( {deviceName} ); } function AccountNumberRow() { const accountToken = useSelector((state) => state.account.accountToken); return ; } function AccountExpiryRow() { const accountExpiry = useSelector((state) => state.account.expiry); const expiryLocale = useSelector((state) => state.userInterface.locale); return ; } function FormattedAccountExpiry(props: { expiry?: string; locale: string }) { if (props.expiry) { if (hasExpired(props.expiry)) { return ( {messages.pgettext('account-view', 'OUT OF TIME')} ); } else { return {formatDate(props.expiry, props.locale)}; } } else { return ( {messages.pgettext('account-view', 'Currently unavailable')} ); } } type LogoutContextType = { dialogStage: DialogStage; dialogVisible: boolean; onConfirmLogout: () => void; onCancelLogout: () => void; onTryLogout: () => Promise; }; const LogoutContext = createContext(undefined); const LogoutContextProvider = ({ children }: { children: ReactNode }) => { const { cancelLogout, prepareLogout } = useActions(account); const { logout, getDeviceState } = useAppContext(); const [dialogStage, setDialogStage] = useState(); const [dialogVisible, showDialog, hideDialog] = useBoolean(false); const onConfirmLogout = useCallback(async () => { hideDialog(); await logout(); }, []); const onTryLogout = useCallback(async () => { showDialog(); setDialogStage('checking-ports'); prepareLogout(); const deviceState = await getDeviceState(); if ( deviceState?.type === 'logged in' && deviceState.accountAndDevice.device?.ports !== undefined && deviceState.accountAndDevice.device.ports.length > 0 ) { setDialogStage('confirm'); } else { await onConfirmLogout(); } }, [onConfirmLogout, prepareLogout]); const onCancelLogout = useCallback(() => { cancelLogout(); hideDialog(); }, [cancelLogout]); const value: LogoutContextType = useMemo( () => ({ dialogStage, dialogVisible, onConfirmLogout, onCancelLogout, onTryLogout, }), [dialogVisible, dialogStage, onConfirmLogout, onCancelLogout, onTryLogout], ); return {children}; }; const useLogoutContext = () => { const context = useContext(LogoutContext); if (!context) { throw new Error('useAccount must be used within an LogoutContextProvider'); } return context; };