/*
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];
}