/* This file is part of GNU Taler (C) 2022 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ import { AbsoluteTime, Codec, TalerCorebankApi, buildCodecForObject, buildCodecForUnion, codecForAbsoluteTime, codecForAny, codecForConstString, codecForString, codecForTanTransmission, codecOptional } from "@gnu-taler/taler-util"; import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser"; export type ChallengeInProgess = DeleteAccountChallenge | UpdateAccountChallenge | UpdatePasswordChallenge | CreateTransactionChallenge | ConfirmWithdrawalChallenge | CashoutChallenge; type BaseChallenge = { id: string, operation: OpType, sent: AbsoluteTime, info?: TalerCorebankApi.TanTransmission, request: ReqType } type DeleteAccountChallenge = BaseChallenge<"delete-account", string> type UpdateAccountChallenge = BaseChallenge<"update-account", TalerCorebankApi.AccountReconfiguration> type UpdatePasswordChallenge = BaseChallenge<"update-password", TalerCorebankApi.AccountPasswordChange> type CreateTransactionChallenge = BaseChallenge<"create-transaction", TalerCorebankApi.CreateTransactionRequest> type ConfirmWithdrawalChallenge = BaseChallenge<"confirm-withdrawal", string> type CashoutChallenge = BaseChallenge<"create-cashout", TalerCorebankApi.CashoutRequest> const codecForChallengeUpdatePassword = (): Codec => buildCodecForObject() .property("operation", codecForConstString("update-password")) .property("id", codecForString()) .property("sent", codecForAbsoluteTime) .property("info", codecOptional(codecForTanTransmission())) .property("request", codecForAny()) .build("UpdatePasswordChallenge"); const codecForChallengeDeleteAccount = (): Codec => buildCodecForObject() .property("operation", codecForConstString("delete-account")) .property("id", codecForString()) .property("sent", codecForAbsoluteTime) .property("request", codecForString()) .property("info", codecOptional(codecForTanTransmission())) .build("DeleteAccountChallenge"); const codecForChallengeUpdateAccount = (): Codec => buildCodecForObject() .property("operation", codecForConstString("update-account")) .property("id", codecForString()) .property("sent", codecForAbsoluteTime) .property("info", codecOptional(codecForTanTransmission())) .property("request", codecForAny()) .build("UpdateAccountChallenge"); const codecForChallengeCreateTransaction = (): Codec => buildCodecForObject() .property("operation", codecForConstString("create-transaction")) .property("id", codecForString()) .property("sent", codecForAbsoluteTime) .property("info", codecOptional(codecForTanTransmission())) .property("request", codecForAny()) .build("CreateTransactionChallenge"); const codecForChallengeConfirmWithdrawal = (): Codec => buildCodecForObject() .property("operation", codecForConstString("confirm-withdrawal")) .property("id", codecForString()) .property("sent", codecForAbsoluteTime) .property("info", codecOptional(codecForTanTransmission())) .property("request", codecForString()) .build("ConfirmWithdrawalChallenge"); const codecForChallengeCashout = (): Codec => buildCodecForObject() .property("operation", codecForConstString("create-cashout")) .property("id", codecForString()) .property("sent", codecForAbsoluteTime) .property("info", codecOptional(codecForTanTransmission())) .property("request", codecForAny()) .build("CashoutChallenge"); const codecForChallenge = (): Codec => buildCodecForUnion() .discriminateOn("operation") .alternative("confirm-withdrawal", codecForChallengeConfirmWithdrawal()) .alternative("create-cashout", codecForChallengeCashout()) .alternative("create-transaction", codecForChallengeCreateTransaction()) .alternative("delete-account", codecForChallengeDeleteAccount()) .alternative("update-account", codecForChallengeUpdateAccount()) .alternative("update-password", codecForChallengeUpdatePassword()) .build("ChallengeInProgess"); interface BankState { currentWithdrawalOperationId: string | undefined; currentChallenge: ChallengeInProgess | undefined; } export const codecForBankState = (): Codec => buildCodecForObject() .property("currentWithdrawalOperationId", codecOptional(codecForString())) .property("currentChallenge", codecOptional(codecForChallenge())) .build("BankState"); const defaultBankState: BankState = { currentWithdrawalOperationId: undefined, currentChallenge: undefined, }; const BANK_STATE_KEY = buildStorageKey( "bank-app-state", codecForBankState(), ); export function useBankState(): [ Readonly, (key: T, value: BankState[T]) => void, () => void, ] { const { value, update } = useLocalStorage( BANK_STATE_KEY, defaultBankState, ); function updateField(k: T, v: BankState[T]) { const newValue = { ...value, [k]: v }; update(newValue); } function reset() { update(defaultBankState) } return [value, updateField, reset]; }