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')}
);
}
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;
};