no-fix: move pagestate provider to app component and move some common hooks to web-utils
This commit is contained in:
parent
2dc3eb0ddd
commit
8e6bf99006
@ -1,11 +1,14 @@
|
||||
import { h, FunctionalComponent } from "preact";
|
||||
import { PageStateProvider } from "../context/pageState.js";
|
||||
import { TranslationProvider } from "../context/translation.js";
|
||||
import { BankHome } from "../pages/home/index.js";
|
||||
|
||||
const App: FunctionalComponent = () => {
|
||||
return (
|
||||
<TranslationProvider>
|
||||
<PageStateProvider>
|
||||
<BankHome />
|
||||
</PageStateProvider>
|
||||
</TranslationProvider>
|
||||
);
|
||||
};
|
||||
|
121
packages/demobank-ui/src/context/pageState.ts
Normal file
121
packages/demobank-ui/src/context/pageState.ts
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { hooks } from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { ComponentChildren, createContext, h, VNode } from "preact";
|
||||
import { StateUpdater, useContext } from "preact/hooks";
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
export type Type = {
|
||||
pageState: PageStateType;
|
||||
pageStateSetter: StateUpdater<PageStateType>;
|
||||
};
|
||||
const initial: Type = {
|
||||
pageState: {
|
||||
isLoggedIn: false,
|
||||
isRawPayto: false,
|
||||
showPublicHistories: false,
|
||||
withdrawalInProgress: false,
|
||||
},
|
||||
pageStateSetter: () => {
|
||||
null;
|
||||
},
|
||||
};
|
||||
const Context = createContext<Type>(initial);
|
||||
|
||||
export const usePageContext = (): Type => useContext(Context);
|
||||
|
||||
export const PageStateProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: ComponentChildren;
|
||||
}): VNode => {
|
||||
const [pageState, pageStateSetter] = usePageState();
|
||||
|
||||
return h(Context.Provider, {
|
||||
value: { pageState, pageStateSetter },
|
||||
children,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper providing defaults.
|
||||
*/
|
||||
function usePageState(
|
||||
state: PageStateType = {
|
||||
isLoggedIn: false,
|
||||
isRawPayto: false,
|
||||
showPublicHistories: false,
|
||||
withdrawalInProgress: false,
|
||||
},
|
||||
): [PageStateType, StateUpdater<PageStateType>] {
|
||||
const ret = hooks.useNotNullLocalStorage("page-state", JSON.stringify(state));
|
||||
const retObj: PageStateType = JSON.parse(ret[0]);
|
||||
|
||||
const retSetter: StateUpdater<PageStateType> = function (val) {
|
||||
const newVal =
|
||||
val instanceof Function
|
||||
? JSON.stringify(val(retObj))
|
||||
: JSON.stringify(val);
|
||||
|
||||
ret[1](newVal);
|
||||
};
|
||||
|
||||
//when moving from one page to another
|
||||
//clean up the info and error bar
|
||||
function removeLatestInfo(val: any): ReturnType<typeof retSetter> {
|
||||
const updater = typeof val === "function" ? val : (c: any) => val;
|
||||
return retSetter((current: any) => {
|
||||
const cleanedCurrent: PageStateType = {
|
||||
...current,
|
||||
info: undefined,
|
||||
errors: undefined,
|
||||
timestamp: new Date().getTime(),
|
||||
};
|
||||
return updater(cleanedCurrent);
|
||||
});
|
||||
}
|
||||
|
||||
return [retObj, removeLatestInfo];
|
||||
}
|
||||
|
||||
/**
|
||||
* Track page state.
|
||||
*/
|
||||
export interface PageStateType {
|
||||
isLoggedIn: boolean;
|
||||
isRawPayto: boolean;
|
||||
showPublicHistories: boolean;
|
||||
withdrawalInProgress: boolean;
|
||||
error?: {
|
||||
description?: string;
|
||||
title: string;
|
||||
debug?: string;
|
||||
};
|
||||
|
||||
info?: string;
|
||||
talerWithdrawUri?: string;
|
||||
/**
|
||||
* Not strictly a presentational value, could
|
||||
* be moved in a future "withdrawal state" object.
|
||||
*/
|
||||
withdrawalId?: string;
|
||||
timestamp?: number;
|
||||
}
|
@ -22,7 +22,7 @@
|
||||
import { i18n, setupI18n } from "@gnu-taler/taler-util";
|
||||
import { createContext, h, VNode } from "preact";
|
||||
import { useContext, useEffect } from "preact/hooks";
|
||||
import { useLang } from "../hooks/useLang.js";
|
||||
import { hooks } from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { strings } from "../i18n/strings.js";
|
||||
|
||||
interface Type {
|
||||
@ -70,7 +70,7 @@ export const TranslationProvider = ({
|
||||
children,
|
||||
forceLang,
|
||||
}: Props): VNode => {
|
||||
const [lang, changeLanguage, isSaved] = useLang(initial);
|
||||
const [lang, changeLanguage, isSaved] = hooks.useLang(initial);
|
||||
useEffect(() => {
|
||||
if (forceLang) {
|
||||
changeLanguage(forceLang);
|
||||
|
49
packages/demobank-ui/src/declaration.d.ts
vendored
49
packages/demobank-ui/src/declaration.d.ts
vendored
@ -18,3 +18,52 @@ declare module "jed" {
|
||||
const x: any;
|
||||
export = x;
|
||||
}
|
||||
|
||||
/**********************************************
|
||||
* Type definitions for states and API calls. *
|
||||
*********************************************/
|
||||
|
||||
/**
|
||||
* Has the information to reach and
|
||||
* authenticate at the bank's backend.
|
||||
*/
|
||||
interface BackendStateType {
|
||||
url?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request body of POST /transactions.
|
||||
*
|
||||
* If the amount appears twice: both as a Payto parameter and
|
||||
* in the JSON dedicate field, the one on the Payto URI takes
|
||||
* precedence.
|
||||
*/
|
||||
interface TransactionRequestType {
|
||||
paytoUri: string;
|
||||
amount?: string; // with currency.
|
||||
}
|
||||
|
||||
/**
|
||||
* Request body of /register.
|
||||
*/
|
||||
interface CredentialsRequestType {
|
||||
username?: string;
|
||||
password?: string;
|
||||
repeatPassword?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request body of /register.
|
||||
*/
|
||||
// interface LoginRequestType {
|
||||
// username: string;
|
||||
// password: string;
|
||||
// }
|
||||
|
||||
interface WireTransferRequestType {
|
||||
iban?: string;
|
||||
subject?: string;
|
||||
amount?: string;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import { StateUpdater } from "preact/hooks";
|
||||
import { useLocalStorage, useNotNullLocalStorage } from "./useLocalStorage.js";
|
||||
import { hooks } from "@gnu-taler/web-util/lib/index.browser";
|
||||
export type ValueOrFunction<T> = T | ((p: T) => T);
|
||||
|
||||
const calculateRootPath = () => {
|
||||
@ -34,11 +34,11 @@ const calculateRootPath = () => {
|
||||
export function useBackendURL(
|
||||
url?: string,
|
||||
): [string, boolean, StateUpdater<string>, () => void] {
|
||||
const [value, setter] = useNotNullLocalStorage(
|
||||
const [value, setter] = hooks.useNotNullLocalStorage(
|
||||
"backend-url",
|
||||
url || calculateRootPath(),
|
||||
);
|
||||
const [triedToLog, setTriedToLog] = useLocalStorage("tried-login");
|
||||
const [triedToLog, setTriedToLog] = hooks.useLocalStorage("tried-login");
|
||||
|
||||
const checkedSetter = (v: ValueOrFunction<string>) => {
|
||||
setTriedToLog("yes");
|
||||
@ -55,13 +55,13 @@ export function useBackendDefaultToken(): [
|
||||
string | undefined,
|
||||
StateUpdater<string | undefined>,
|
||||
] {
|
||||
return useLocalStorage("backend-token");
|
||||
return hooks.useLocalStorage("backend-token");
|
||||
}
|
||||
|
||||
export function useBackendInstanceToken(
|
||||
id: string,
|
||||
): [string | undefined, StateUpdater<string | undefined>] {
|
||||
const [token, setToken] = useLocalStorage(`backend-token-${id}`);
|
||||
const [token, setToken] = hooks.useLocalStorage(`backend-token-${id}`);
|
||||
const [defaultToken, defaultSetToken] = useBackendDefaultToken();
|
||||
|
||||
// instance named 'default' use the default token
|
||||
|
@ -27,44 +27,14 @@ import {
|
||||
} from "preact/hooks";
|
||||
import talerLogo from "../../assets/logo-white.svg";
|
||||
import { LangSelectorLikePy as LangSelector } from "../../components/menu/LangSelector.js";
|
||||
import {
|
||||
useLocalStorage,
|
||||
useNotNullLocalStorage,
|
||||
} from "../../hooks/useLocalStorage.js";
|
||||
// import { Translate, useTranslator } from "../../i18n/index.js";
|
||||
import { useTranslationContext } from "../../context/translation.js";
|
||||
import { Amounts, HttpStatusCode, parsePaytoUri } from "@gnu-taler/taler-util";
|
||||
import { createHashHistory } from "history";
|
||||
import Router, { Route, route } from "preact-router";
|
||||
import { QrCodeSection } from "./QrCodeSection.js";
|
||||
|
||||
interface BankUiSettings {
|
||||
allowRegistrations: boolean;
|
||||
showDemoNav: boolean;
|
||||
bankName: string;
|
||||
demoSites: [string, string][];
|
||||
}
|
||||
|
||||
/**
|
||||
* Global settings for the demobank UI.
|
||||
*/
|
||||
const defaultSettings: BankUiSettings = {
|
||||
allowRegistrations: true,
|
||||
bankName: "Taler Bank",
|
||||
showDemoNav: true,
|
||||
demoSites: [
|
||||
["Landing", "https://demo.taler.net/"],
|
||||
["Bank", "https://bank.demo.taler.net/"],
|
||||
["Essay Shop", "https://shop.demo.taler.net/"],
|
||||
["Donations", "https://donations.demo.taler.net/"],
|
||||
["Survey", "https://survey.demo.taler.net/"],
|
||||
],
|
||||
};
|
||||
|
||||
const bankUiSettings: BankUiSettings =
|
||||
"talerDemobankSettings" in globalThis
|
||||
? (globalThis as any).talerDemobankSettings
|
||||
: defaultSettings;
|
||||
import { hooks } from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { bankUiSettings } from "../../settings.js";
|
||||
import { PageStateType, usePageContext } from "../../context/pageState.js";
|
||||
|
||||
/**
|
||||
* FIXME:
|
||||
@ -90,94 +60,6 @@ const bankUiSettings: BankUiSettings =
|
||||
/************
|
||||
* Contexts *
|
||||
***********/
|
||||
const CurrencyContext = createContext<any>(null);
|
||||
type PageContextType = [PageStateType, StateUpdater<PageStateType>];
|
||||
const PageContextDefault: PageContextType = [
|
||||
{
|
||||
isLoggedIn: false,
|
||||
isRawPayto: false,
|
||||
showPublicHistories: false,
|
||||
|
||||
withdrawalInProgress: false,
|
||||
},
|
||||
() => {
|
||||
null;
|
||||
},
|
||||
];
|
||||
const PageContext = createContext<PageContextType>(PageContextDefault);
|
||||
|
||||
/**********************************************
|
||||
* Type definitions for states and API calls. *
|
||||
*********************************************/
|
||||
|
||||
/**
|
||||
* Has the information to reach and
|
||||
* authenticate at the bank's backend.
|
||||
*/
|
||||
interface BackendStateType {
|
||||
url?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request body of POST /transactions.
|
||||
*
|
||||
* If the amount appears twice: both as a Payto parameter and
|
||||
* in the JSON dedicate field, the one on the Payto URI takes
|
||||
* precedence.
|
||||
*/
|
||||
interface TransactionRequestType {
|
||||
paytoUri: string;
|
||||
amount?: string; // with currency.
|
||||
}
|
||||
|
||||
/**
|
||||
* Request body of /register.
|
||||
*/
|
||||
interface CredentialsRequestType {
|
||||
username?: string;
|
||||
password?: string;
|
||||
repeatPassword?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request body of /register.
|
||||
*/
|
||||
// interface LoginRequestType {
|
||||
// username: string;
|
||||
// password: string;
|
||||
// }
|
||||
|
||||
interface WireTransferRequestType {
|
||||
iban?: string;
|
||||
subject?: string;
|
||||
amount?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track page state.
|
||||
*/
|
||||
interface PageStateType {
|
||||
isLoggedIn: boolean;
|
||||
isRawPayto: boolean;
|
||||
showPublicHistories: boolean;
|
||||
withdrawalInProgress: boolean;
|
||||
error?: {
|
||||
description?: string;
|
||||
title: string;
|
||||
debug?: string;
|
||||
};
|
||||
|
||||
info?: string;
|
||||
talerWithdrawUri?: string;
|
||||
/**
|
||||
* Not strictly a presentational value, could
|
||||
* be moved in a future "withdrawal state" object.
|
||||
*/
|
||||
withdrawalId?: string;
|
||||
timestamp?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bank account specific information.
|
||||
@ -294,7 +176,7 @@ async function postToBackend(
|
||||
}
|
||||
|
||||
function useTransactionPageNumber(): [number, StateUpdater<number>] {
|
||||
const ret = useNotNullLocalStorage("transaction-page", "0");
|
||||
const ret = hooks.useNotNullLocalStorage("transaction-page", "0");
|
||||
const retObj = JSON.parse(ret[0]);
|
||||
const retSetter: StateUpdater<number> = function (val) {
|
||||
const newVal =
|
||||
@ -347,7 +229,10 @@ const getBankBackendBaseUrl = (): string => {
|
||||
function useShowPublicAccount(
|
||||
state?: string,
|
||||
): [string | undefined, StateUpdater<string | undefined>] {
|
||||
const ret = useLocalStorage("show-public-account", JSON.stringify(state));
|
||||
const ret = hooks.useLocalStorage(
|
||||
"show-public-account",
|
||||
JSON.stringify(state),
|
||||
);
|
||||
const retObj: string | undefined = ret[0] ? JSON.parse(ret[0]) : ret[0];
|
||||
const retSetter: StateUpdater<string | undefined> = function (val) {
|
||||
const newVal =
|
||||
@ -367,7 +252,7 @@ type RawPaytoInputTypeOpt = RawPaytoInputType | undefined;
|
||||
function useRawPaytoInputType(
|
||||
state?: RawPaytoInputType,
|
||||
): [RawPaytoInputTypeOpt, StateUpdater<RawPaytoInputTypeOpt>] {
|
||||
const ret = useLocalStorage("raw-payto-input-state", state);
|
||||
const ret = hooks.useLocalStorage("raw-payto-input-state", state);
|
||||
const retObj: RawPaytoInputTypeOpt = ret[0];
|
||||
const retSetter: StateUpdater<RawPaytoInputTypeOpt> = function (val) {
|
||||
const newVal = val instanceof Function ? val(retObj) : val;
|
||||
@ -387,7 +272,7 @@ type WireTransferRequestTypeOpt = WireTransferRequestType | undefined;
|
||||
function useWireTransferRequestType(
|
||||
state?: WireTransferRequestType,
|
||||
): [WireTransferRequestTypeOpt, StateUpdater<WireTransferRequestTypeOpt>] {
|
||||
const ret = useLocalStorage(
|
||||
const ret = hooks.useLocalStorage(
|
||||
"wire-transfer-request-state",
|
||||
JSON.stringify(state),
|
||||
);
|
||||
@ -413,7 +298,7 @@ type CredentialsRequestTypeOpt = CredentialsRequestType | undefined;
|
||||
function useCredentialsRequestType(
|
||||
state?: CredentialsRequestType,
|
||||
): [CredentialsRequestTypeOpt, StateUpdater<CredentialsRequestTypeOpt>] {
|
||||
const ret = useLocalStorage(
|
||||
const ret = hooks.useLocalStorage(
|
||||
"credentials-request-state",
|
||||
JSON.stringify(state),
|
||||
);
|
||||
@ -439,7 +324,7 @@ type BackendStateTypeOpt = BackendStateType | undefined;
|
||||
function useBackendState(
|
||||
state?: BackendStateType,
|
||||
): [BackendStateTypeOpt, StateUpdater<BackendStateTypeOpt>] {
|
||||
const ret = useLocalStorage("backend-state", JSON.stringify(state));
|
||||
const ret = hooks.useLocalStorage("backend-state", JSON.stringify(state));
|
||||
const retObj: BackendStateTypeOpt = ret[0] ? JSON.parse(ret[0]) : ret[0];
|
||||
const retSetter: StateUpdater<BackendStateTypeOpt> = function (val) {
|
||||
const newVal =
|
||||
@ -451,67 +336,6 @@ function useBackendState(
|
||||
return [retObj, retSetter];
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep mere business information, like account balance or
|
||||
* transactions history.
|
||||
*/
|
||||
// type AccountStateTypeOpt = AccountStateType | undefined;
|
||||
// function useAccountState(
|
||||
// state?: AccountStateType,
|
||||
// ): [AccountStateTypeOpt, StateUpdater<AccountStateTypeOpt>] {
|
||||
// const ret = useLocalStorage("account-state", JSON.stringify(state));
|
||||
// const retObj: AccountStateTypeOpt = ret[0] ? JSON.parse(ret[0]) : ret[0];
|
||||
// const retSetter: StateUpdater<AccountStateTypeOpt> = function (val) {
|
||||
// const newVal =
|
||||
// val instanceof Function
|
||||
// ? JSON.stringify(val(retObj))
|
||||
// : JSON.stringify(val);
|
||||
// ret[1](newVal);
|
||||
// };
|
||||
// return [retObj, retSetter];
|
||||
// }
|
||||
|
||||
/**
|
||||
* Wrapper providing defaults.
|
||||
*/
|
||||
function usePageState(
|
||||
state: PageStateType = {
|
||||
isLoggedIn: false,
|
||||
isRawPayto: false,
|
||||
showPublicHistories: false,
|
||||
withdrawalInProgress: false,
|
||||
},
|
||||
): [PageStateType, StateUpdater<PageStateType>] {
|
||||
const ret = useNotNullLocalStorage("page-state", JSON.stringify(state));
|
||||
const retObj: PageStateType = JSON.parse(ret[0]);
|
||||
|
||||
const retSetter: StateUpdater<PageStateType> = function (val) {
|
||||
const newVal =
|
||||
val instanceof Function
|
||||
? JSON.stringify(val(retObj))
|
||||
: JSON.stringify(val);
|
||||
|
||||
ret[1](newVal);
|
||||
};
|
||||
|
||||
//when moving from one page to another
|
||||
//clean up the info and error bar
|
||||
function removeLatestInfo(val: any): ReturnType<typeof retSetter> {
|
||||
const updater = typeof val === "function" ? val : (c: any) => val;
|
||||
return retSetter((current: any) => {
|
||||
const cleanedCurrent: PageStateType = {
|
||||
...current,
|
||||
info: undefined,
|
||||
errors: undefined,
|
||||
timestamp: new Date().getTime(),
|
||||
};
|
||||
return updater(cleanedCurrent);
|
||||
});
|
||||
}
|
||||
|
||||
return [retObj, removeLatestInfo];
|
||||
}
|
||||
|
||||
/**
|
||||
* Request preparators.
|
||||
*
|
||||
@ -1045,7 +869,7 @@ function StatusBanner(Props: any): VNode | null {
|
||||
|
||||
function BankFrame(Props: any): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
const [pageState, pageStateSetter] = useContext(PageContext);
|
||||
const { pageState, pageStateSetter } = usePageContext();
|
||||
console.log("BankFrame state", pageState);
|
||||
const logOut = (
|
||||
<div class="logout">
|
||||
@ -1164,19 +988,16 @@ function ShowInputErrorLabel({
|
||||
}
|
||||
|
||||
function PaytoWireTransfer(Props: any): VNode {
|
||||
const currency = useContext(CurrencyContext);
|
||||
const [pageState, pageStateSetter] = useContext(PageContext); // NOTE: used for go-back button?
|
||||
const { pageState, pageStateSetter } = usePageContext(); // NOTE: used for go-back button?
|
||||
|
||||
const [submitData, submitDataSetter] = useWireTransferRequestType();
|
||||
// const [rawPaytoInput, rawPaytoInputSetter] = useRawPaytoInputType();
|
||||
|
||||
const [rawPaytoInput, rawPaytoInputSetter] = useState<string | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const { i18n } = useTranslationContext();
|
||||
const { focus, backendState } = Props;
|
||||
const amountRegex = "^[0-9]+(.[0-9]+)?$";
|
||||
const { focus, backendState, currency } = Props;
|
||||
const ibanRegex = "^[A-Z][A-Z][0-9]+$";
|
||||
const receiverInput = "";
|
||||
const subjectInput = "";
|
||||
let transactionData: TransactionRequestType;
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
useEffect(() => {
|
||||
@ -1213,7 +1034,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
if (!pageState.isRawPayto)
|
||||
return (
|
||||
<div>
|
||||
<div class="pure-form" name="wire-transfer-form">
|
||||
<form class="pure-form" name="wire-transfer-form">
|
||||
<p>
|
||||
<label for="iban">{i18n.str`Receiver IBAN:`}</label>
|
||||
<input
|
||||
@ -1260,22 +1081,6 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
/>
|
||||
<br />
|
||||
<label for="amount">{i18n.str`Amount:`}</label>
|
||||
<input
|
||||
type="number"
|
||||
name="amount"
|
||||
id="amount"
|
||||
placeholder="amount"
|
||||
required
|
||||
value={submitData?.amount ?? ""}
|
||||
pattern={amountRegex}
|
||||
onInput={(e): void => {
|
||||
submitDataSetter((submitData: any) => ({
|
||||
...submitData,
|
||||
amount: e.currentTarget.value,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
@ -1285,6 +1090,21 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
tabIndex={-1}
|
||||
value={currency}
|
||||
/>
|
||||
|
||||
<input
|
||||
type="number"
|
||||
name="amount"
|
||||
id="amount"
|
||||
placeholder="amount"
|
||||
required
|
||||
value={submitData?.amount ?? ""}
|
||||
onInput={(e): void => {
|
||||
submitDataSetter((submitData: any) => ({
|
||||
...submitData,
|
||||
amount: e.currentTarget.value,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<ShowInputErrorLabel
|
||||
message={errorsWire?.amount}
|
||||
isDirty={submitData?.amount !== undefined}
|
||||
@ -1349,7 +1169,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
<p>
|
||||
<a
|
||||
href="/account"
|
||||
@ -1460,7 +1280,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
* Not providing a back button, only abort.
|
||||
*/
|
||||
function TalerWithdrawalConfirmationQuestion(Props: any): VNode {
|
||||
const [pageState, pageStateSetter] = useContext(PageContext);
|
||||
const { pageState, pageStateSetter } = usePageContext();
|
||||
const { backendState } = Props;
|
||||
const { i18n } = useTranslationContext();
|
||||
const captchaNumbers = {
|
||||
@ -1474,7 +1294,7 @@ function TalerWithdrawalConfirmationQuestion(Props: any): VNode {
|
||||
<h1 class="nav">{i18n.str`Confirm Withdrawal`}</h1>
|
||||
<article>
|
||||
<div class="challenge-div">
|
||||
<form class="challenge-form">
|
||||
<form class="challenge-form" noValidate>
|
||||
<div class="pure-form" id="captcha" name="capcha-form">
|
||||
<h2>{i18n.str`Authorize withdrawal by solving challenge`}</h2>
|
||||
<p>
|
||||
@ -1562,8 +1382,8 @@ function TalerWithdrawalConfirmationQuestion(Props: any): VNode {
|
||||
*/
|
||||
function TalerWithdrawalQRCode(Props: any): VNode {
|
||||
// turns true when the wallet POSTed the reserve details:
|
||||
const [pageState, pageStateSetter] = useContext(PageContext);
|
||||
const { withdrawalId, talerWithdrawUri, accountLabel, backendState } = Props;
|
||||
const { pageState, pageStateSetter } = usePageContext();
|
||||
const { withdrawalId, talerWithdrawUri, backendState } = Props;
|
||||
const { i18n } = useTranslationContext();
|
||||
const abortButton = (
|
||||
<a
|
||||
@ -1647,37 +1467,19 @@ function TalerWithdrawalQRCode(Props: any): VNode {
|
||||
}
|
||||
|
||||
function WalletWithdraw(Props: any): VNode {
|
||||
const { backendState, pageStateSetter, focus } = Props;
|
||||
const currency = useContext(CurrencyContext);
|
||||
const { backendState, pageStateSetter, focus, currency } = Props;
|
||||
const { i18n } = useTranslationContext();
|
||||
let submitAmount = "5.00";
|
||||
const amountRegex = "^[0-9]+(.[0-9]+)?$";
|
||||
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
useEffect(() => {
|
||||
if (focus) ref.current?.focus();
|
||||
}, [focus]);
|
||||
return (
|
||||
<div id="reserve-form" class="pure-form" name="tform">
|
||||
<form id="reserve-form" class="pure-form" name="tform">
|
||||
<p>
|
||||
<label for="withdraw-amount">{i18n.str`Amount to withdraw:`}</label>
|
||||
|
||||
<input
|
||||
type="number"
|
||||
ref={ref}
|
||||
id="withdraw-amount"
|
||||
name="withdraw-amount"
|
||||
value={submitAmount}
|
||||
pattern={amountRegex}
|
||||
class="amount"
|
||||
onChange={(e): void => {
|
||||
// FIXME: validate using 'parseAmount()',
|
||||
// deactivate submit button as long as
|
||||
// amount is not valid
|
||||
submitAmount = e.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
@ -1687,6 +1489,20 @@ function WalletWithdraw(Props: any): VNode {
|
||||
tabIndex={-1}
|
||||
value={currency}
|
||||
/>
|
||||
|
||||
<input
|
||||
type="number"
|
||||
ref={ref}
|
||||
id="withdraw-amount"
|
||||
name="withdraw-amount"
|
||||
value={submitAmount}
|
||||
onChange={(e): void => {
|
||||
// FIXME: validate using 'parseAmount()',
|
||||
// deactivate submit button as long as
|
||||
// amount is not valid
|
||||
submitAmount = e.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<div>
|
||||
@ -1712,7 +1528,7 @@ function WalletWithdraw(Props: any): VNode {
|
||||
/>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1721,8 +1537,7 @@ function WalletWithdraw(Props: any): VNode {
|
||||
* then specify the details trigger the action.
|
||||
*/
|
||||
function PaymentOptions(Props: any): VNode {
|
||||
const { backendState, pageStateSetter, focus } = Props;
|
||||
const currency = useContext(CurrencyContext);
|
||||
const { backendState, pageStateSetter, currency } = Props;
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
const [tab, setTab] = useState<"charge-wallet" | "wire-transfer">(
|
||||
@ -1756,6 +1571,7 @@ function PaymentOptions(Props: any): VNode {
|
||||
<WalletWithdraw
|
||||
backendState={backendState}
|
||||
focus
|
||||
currency={currency}
|
||||
pageStateSetter={pageStateSetter}
|
||||
/>
|
||||
</div>
|
||||
@ -1766,6 +1582,7 @@ function PaymentOptions(Props: any): VNode {
|
||||
<PaytoWireTransfer
|
||||
backendState={backendState}
|
||||
focus
|
||||
currency={currency}
|
||||
pageStateSetter={pageStateSetter}
|
||||
/>
|
||||
</div>
|
||||
@ -1819,7 +1636,7 @@ function LoginForm(Props: any): VNode {
|
||||
|
||||
return (
|
||||
<div class="login-div">
|
||||
<form action="javascript:void(0);" class="login-form">
|
||||
<form action="javascript:void(0);" class="login-form" noValidate>
|
||||
<div class="pure-form">
|
||||
<h2>{i18n.str`Please login!`}</h2>
|
||||
<p class="unameFieldLabel loginFieldLabel formFieldLabel">
|
||||
@ -1903,7 +1720,7 @@ function LoginForm(Props: any): VNode {
|
||||
*/
|
||||
function RegistrationForm(Props: any): VNode {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [pageState, pageStateSetter] = useContext(PageContext);
|
||||
const { pageState, pageStateSetter } = usePageContext();
|
||||
const [submitData, submitDataSetter] = useCredentialsRequestType();
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
@ -1924,7 +1741,7 @@ function RegistrationForm(Props: any): VNode {
|
||||
<h1 class="nav">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h1>
|
||||
<article>
|
||||
<div class="register-div">
|
||||
<form action="javascript:void(0);" class="register-form">
|
||||
<form action="javascript:void(0);" class="register-form" noValidate>
|
||||
<div class="pure-form">
|
||||
<h2>{i18n.str`Please register!`}</h2>
|
||||
<p class="unameFieldLabel registerFieldLabel formFieldLabel">
|
||||
@ -2140,7 +1957,7 @@ function Account(Props: any): VNode {
|
||||
// revalidateOnFocus: false,
|
||||
// revalidateOnReconnect: false,
|
||||
});
|
||||
const [pageState, setPageState] = useContext(PageContext);
|
||||
const { pageState, pageStateSetter: setPageState } = usePageContext();
|
||||
const {
|
||||
withdrawalInProgress,
|
||||
withdrawalId,
|
||||
@ -2275,14 +2092,11 @@ function Account(Props: any): VNode {
|
||||
<section id="payments">
|
||||
<div class="payments">
|
||||
<h2>{i18n.str`Payments`}</h2>
|
||||
{/* FIXME: turn into button! */}
|
||||
<CurrencyContext.Provider value={balance.currency}>
|
||||
{Props.children}
|
||||
<PaymentOptions
|
||||
currency={balance.currency}
|
||||
backendState={backendState}
|
||||
pageStateSetter={setPageState}
|
||||
/>
|
||||
</CurrencyContext.Provider>
|
||||
</div>
|
||||
</section>
|
||||
<section id="main">
|
||||
@ -2439,12 +2253,10 @@ function PublicHistories(Props: any): VNode {
|
||||
}
|
||||
|
||||
function PublicHistoriesPage(): VNode {
|
||||
// const [backendState, backendStateSetter] = useBackendState();
|
||||
const [pageState, pageStateSetter] = usePageState();
|
||||
const { pageState, pageStateSetter } = usePageContext();
|
||||
// const { i18n } = useTranslationContext();
|
||||
return (
|
||||
<SWRWithoutCredentials baseUrl={getBankBackendBaseUrl()}>
|
||||
<PageContext.Provider value={[pageState, pageStateSetter]}>
|
||||
<BankFrame>
|
||||
<PublicHistories pageStateSetter={pageStateSetter}>
|
||||
<br />
|
||||
@ -2461,41 +2273,34 @@ function PublicHistoriesPage(): VNode {
|
||||
</a>
|
||||
</PublicHistories>
|
||||
</BankFrame>
|
||||
</PageContext.Provider>
|
||||
</SWRWithoutCredentials>
|
||||
);
|
||||
}
|
||||
|
||||
function RegistrationPage(): VNode {
|
||||
const [backendState, backendStateSetter] = useBackendState();
|
||||
const [pageState, pageStateSetter] = usePageState();
|
||||
const { i18n } = useTranslationContext();
|
||||
if (!bankUiSettings.allowRegistrations) {
|
||||
return (
|
||||
<PageContext.Provider value={[pageState, pageStateSetter]}>
|
||||
<BankFrame>
|
||||
<p>{i18n.str`Currently, the bank is not accepting new registrations!`}</p>
|
||||
</BankFrame>
|
||||
</PageContext.Provider>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<PageContext.Provider value={[pageState, pageStateSetter]}>
|
||||
<BankFrame>
|
||||
<RegistrationForm backendStateSetter={backendStateSetter} />
|
||||
</BankFrame>
|
||||
</PageContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function AccountPage(): VNode {
|
||||
const [backendState, backendStateSetter] = useBackendState();
|
||||
const [pageState, pageStateSetter] = usePageState();
|
||||
const { i18n } = useTranslationContext();
|
||||
const { pageState, pageStateSetter } = usePageContext();
|
||||
|
||||
if (!pageState.isLoggedIn) {
|
||||
return (
|
||||
<PageContext.Provider value={[pageState, pageStateSetter]}>
|
||||
<BankFrame>
|
||||
<h1 class="nav">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h1>
|
||||
<LoginForm
|
||||
@ -2503,7 +2308,6 @@ function AccountPage(): VNode {
|
||||
backendStateSetter={backendStateSetter}
|
||||
/>
|
||||
</BankFrame>
|
||||
</PageContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2525,12 +2329,10 @@ function AccountPage(): VNode {
|
||||
password={backendState.password}
|
||||
backendUrl={backendState.url}
|
||||
>
|
||||
<PageContext.Provider value={[pageState, pageStateSetter]}>
|
||||
<Account
|
||||
accountLabel={backendState.username}
|
||||
backendState={backendState}
|
||||
/>
|
||||
</PageContext.Provider>
|
||||
</SWRWithCredentials>
|
||||
);
|
||||
}
|
||||
|
27
packages/demobank-ui/src/settings.ts
Normal file
27
packages/demobank-ui/src/settings.ts
Normal file
@ -0,0 +1,27 @@
|
||||
export interface BankUiSettings {
|
||||
allowRegistrations: boolean;
|
||||
showDemoNav: boolean;
|
||||
bankName: string;
|
||||
demoSites: [string, string][];
|
||||
}
|
||||
|
||||
/**
|
||||
* Global settings for the demobank UI.
|
||||
*/
|
||||
const defaultSettings: BankUiSettings = {
|
||||
allowRegistrations: true,
|
||||
bankName: "Taler Bank",
|
||||
showDemoNav: true,
|
||||
demoSites: [
|
||||
["Landing", "https://demo.taler.net/"],
|
||||
["Bank", "https://bank.demo.taler.net/"],
|
||||
["Essay Shop", "https://shop.demo.taler.net/"],
|
||||
["Donations", "https://donations.demo.taler.net/"],
|
||||
["Survey", "https://survey.demo.taler.net/"],
|
||||
],
|
||||
};
|
||||
|
||||
export const bankUiSettings: BankUiSettings =
|
||||
"talerDemobankSettings" in globalThis
|
||||
? (globalThis as any).talerDemobankSettings
|
||||
: defaultSettings;
|
3
packages/web-util/src/hooks/index.ts
Normal file
3
packages/web-util/src/hooks/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
export { useLang } from "./useLang.js";
|
||||
export { useLocalStorage, useNotNullLocalStorage } from "./useLocalStorage.js"
|
@ -1,37 +1,2 @@
|
||||
//`ws://localhost:8003/socket`
|
||||
export function setupLiveReload(wsURL: string | undefined) {
|
||||
if (!wsURL) return;
|
||||
const ws = new WebSocket(wsURL);
|
||||
ws.addEventListener("message", (message) => {
|
||||
const event = JSON.parse(message.data);
|
||||
if (event.type === "LOG") {
|
||||
console.log(event.message);
|
||||
}
|
||||
if (event.type === "RELOAD") {
|
||||
window.location.reload();
|
||||
}
|
||||
if (event.type === "UPDATE") {
|
||||
const c = document.getElementById("container");
|
||||
if (c) {
|
||||
document.body.removeChild(c);
|
||||
}
|
||||
const d = document.createElement("div");
|
||||
d.setAttribute("id", "container");
|
||||
d.setAttribute("class", "app-container");
|
||||
document.body.appendChild(d);
|
||||
const s = document.createElement("script");
|
||||
s.setAttribute("id", "code");
|
||||
s.setAttribute("type", "application/javascript");
|
||||
s.textContent = atob(event.content);
|
||||
document.body.appendChild(s);
|
||||
}
|
||||
});
|
||||
ws.onerror = (error) => {
|
||||
console.error(error);
|
||||
};
|
||||
ws.onclose = (e) => {
|
||||
setTimeout(setupLiveReload, 500);
|
||||
};
|
||||
}
|
||||
|
||||
export * as hooks from "./hooks/index.js";
|
||||
export { renderStories, parseGroupImport } from "./stories.js";
|
||||
|
@ -17,7 +17,7 @@ function setupLiveReload(): void {
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
console.log("unsupported", event);
|
||||
console.log("unsupported", message);
|
||||
});
|
||||
|
||||
ws.addEventListener("error", (error) => {
|
||||
|
Loading…
Reference in New Issue
Block a user