diff options
Diffstat (limited to 'packages/bank/src/pages/home/index.tsx')
-rw-r--r-- | packages/bank/src/pages/home/index.tsx | 438 |
1 files changed, 234 insertions, 204 deletions
diff --git a/packages/bank/src/pages/home/index.tsx b/packages/bank/src/pages/home/index.tsx index 3ab1481..3a4bfd6 100644 --- a/packages/bank/src/pages/home/index.tsx +++ b/packages/bank/src/pages/home/index.tsx @@ -9,7 +9,7 @@ import { QR } from '../../components/QR'; import { useNotNullLocalStorage, useLocalStorage } from '../../hooks'; import '../../scss/main.scss'; import talerLogo from '../../assets/logo-white.svg'; -import { LangSelectorLikePy as LangSelector} from '../../components/menu/LangSelector'; +import { LangSelectorLikePy as LangSelector } from '../../components/menu/LangSelector'; // Uncomment to allow test runs: // const __LIBEUFIN_UI_ALLOW_REGISTRATIONS__ = 1; @@ -146,7 +146,7 @@ function genCaptchaNumbers(): string { * Bring the state to show the public accounts page. */ function goPublicAccounts(pageStateSetter: StateUpdater<PageStateType>) { - return () => pageStateSetter((prevState) => ({...prevState, showPublicHistories: true})) + return () => pageStateSetter((prevState) => ({ ...prevState, showPublicHistories: true })) } /** @@ -161,7 +161,7 @@ function validateAmount(maybeAmount: string): any { return; } if (typeof maybeAmount !== 'undefined' || maybeAmount !== '') { - console.log(`Maybe valid amount: ${ maybeAmount}`); + console.log(`Maybe valid amount: ${maybeAmount}`); // tolerating comma instead of point. const re = RegExp(amountRegex) if (!re.test(maybeAmount)) { @@ -192,7 +192,7 @@ function parseAmount(val: string): Amount { if (!format.test(val)) throw Error(`Backend gave invalid amount: ${val}.`) const amountSplit = val.split(':'); - return {value: amountSplit[1], currency: amountSplit[0]} + return { value: amountSplit[1], currency: amountSplit[0] } } /** @@ -200,9 +200,9 @@ function parseAmount(val: string): Amount { * exception if not found. */ function getUsername(backendState: BackendStateTypeOpt): string { - if (typeof backendState === 'undefined') { + if (typeof backendState === 'undefined') throw Error('Username can\'t be found in a undefined backend state.') - } + return backendState.username; } @@ -216,9 +216,9 @@ async function postToBackend( backendState: BackendStateTypeOpt, body: string ): Promise<any> { - if (typeof backendState === 'undefined') { + if (typeof backendState === 'undefined') throw Error('Credentials can\'t be found in a undefined backend state.') - } + const { username, password } = backendState; const headers = prepareHeaders(username, password); // Backend URL must have been stored _with_ a final slash. @@ -234,7 +234,7 @@ async function postToBackend( function useTransactionPageNumber(): [number, StateUpdater<number>] { const ret = useNotNullLocalStorage('transaction-page', '0'); const retObj = JSON.parse(ret[0]); - const retSetter: StateUpdater<number> = function(val) { + const retSetter: StateUpdater<number> = function (val) { const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) ret[1](newVal) } @@ -248,7 +248,7 @@ function prepareHeaders(username: string, password: string) { const headers = new Headers(); headers.append( 'Authorization', - `Basic ${Buffer.from(`${username }:${ password}`).toString('base64')}` + `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}` ); headers.append( 'Content-Type', @@ -265,7 +265,7 @@ const getRootPath = () => { const maybeRootPath = typeof window !== undefined ? window.location.origin + window.location.pathname : '/'; - if (!maybeRootPath.endsWith('/')) return `${maybeRootPath }/`; + if (!maybeRootPath.endsWith('/')) return `${maybeRootPath}/`; return maybeRootPath; }; @@ -284,7 +284,7 @@ function useShowPublicAccount( const ret = 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 retSetter: StateUpdater<string | undefined> = function (val) { const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) ret[1](newVal) } @@ -302,7 +302,7 @@ function useRawPaytoInputType( const ret = useLocalStorage('raw-payto-input-state', state); const retObj: RawPaytoInputTypeOpt = ret[0]; - const retSetter: StateUpdater<RawPaytoInputTypeOpt> = function(val) { + const retSetter: StateUpdater<RawPaytoInputTypeOpt> = function (val) { const newVal = val instanceof Function ? val(retObj) : val ret[1](newVal) } @@ -323,7 +323,7 @@ function useWireTransferRequestType( const ret = useLocalStorage('wire-transfer-request-state', JSON.stringify(state)); const retObj: WireTransferRequestTypeOpt = ret[0] ? JSON.parse(ret[0]) : ret[0]; - const retSetter: StateUpdater<WireTransferRequestTypeOpt> = function(val) { + const retSetter: StateUpdater<WireTransferRequestTypeOpt> = function (val) { const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) ret[1](newVal) } @@ -342,7 +342,7 @@ function useCredentialsRequestType( const ret = useLocalStorage('credentials-request-state', JSON.stringify(state)); const retObj: CredentialsRequestTypeOpt = ret[0] ? JSON.parse(ret[0]) : ret[0]; - const retSetter: StateUpdater<CredentialsRequestTypeOpt> = function(val) { + const retSetter: StateUpdater<CredentialsRequestTypeOpt> = function (val) { const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) ret[1](newVal) } @@ -361,7 +361,7 @@ function useBackendState( const ret = useLocalStorage('backend-state', JSON.stringify(state)); const retObj: BackendStateTypeOpt = ret[0] ? JSON.parse(ret[0]) : ret[0]; - const retSetter: StateUpdater<BackendStateTypeOpt> = function(val) { + const retSetter: StateUpdater<BackendStateTypeOpt> = function (val) { const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) ret[1](newVal) } @@ -379,7 +379,7 @@ function useAccountState( 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 retSetter: StateUpdater<AccountStateTypeOpt> = function (val) { const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) ret[1](newVal) } @@ -403,7 +403,7 @@ function usePageState( const ret = useNotNullLocalStorage('page-state', JSON.stringify(state)); const retObj: PageStateType = JSON.parse(ret[0]); console.log('Current page state', retObj); - const retSetter: StateUpdater<PageStateType> = function(val) { + const retSetter: StateUpdater<PageStateType> = function (val) { const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) console.log('Setting new page state', newVal) ret[1](newVal) @@ -437,12 +437,12 @@ async function abortWithdrawalCall( ) { if (typeof backendState === 'undefined') { console.log('No credentials found.'); - pageStateSetter((prevState) => ({...prevState, hasError: true, error: 'No credentials found.'})) + pageStateSetter((prevState) => ({ ...prevState, hasError: true, error: 'No credentials found.' })) return; } if (typeof withdrawalId === 'undefined') { console.log('No withdrawal ID found.'); - pageStateSetter((prevState) => ({...prevState, hasError: true, error: 'No withdrawal ID found.'})) + pageStateSetter((prevState) => ({ ...prevState, hasError: true, error: 'No withdrawal ID found.' })) return; } @@ -465,13 +465,14 @@ async function abortWithdrawalCall( `access-api/accounts/${backendState.username}/withdrawals/${withdrawalId}/abort`, backendState.url ) - var res = await fetch(url.href, {method: 'POST', headers}) + var res = await fetch(url.href, { method: 'POST', headers }) } catch (error) { console.log('Could not abort the withdrawal', error); pageStateSetter((prevState) => ({ ...prevState, hasError: true, - error: `Could not abort the withdrawal: ${error}`})) + error: `Could not abort the withdrawal: ${error}` + })) return; } if (!res.ok) { @@ -479,17 +480,19 @@ async function abortWithdrawalCall( pageStateSetter((prevState) => ({ ...prevState, hasError: true, - error: `Withdrawal abortion gave response error (${res.status})`})) + error: `Withdrawal abortion gave response error (${res.status})` + })) return; - } + } console.log('Withdrawal operation aborted!'); pageStateSetter((prevState) => { const { talerWithdrawUri, withdrawalId, ...rest } = prevState; return { ...rest, withdrawalOutcome: 'Withdrawal aborted!' - }}) - + } + }) + } /** @@ -510,15 +513,15 @@ async function confirmWithdrawalCall( if (typeof backendState === 'undefined') { console.log('No credentials found.'); - pageStateSetter((prevState) => ({...prevState, hasError: true, error: 'No credentials found.'})) + pageStateSetter((prevState) => ({ ...prevState, hasError: true, error: 'No credentials found.' })) return; } if (typeof withdrawalId === 'undefined') { console.log('No withdrawal ID found.'); - pageStateSetter((prevState) => ({...prevState, hasError: true, error: 'No withdrawal ID found.'})) + pageStateSetter((prevState) => ({ ...prevState, hasError: true, error: 'No withdrawal ID found.' })) return; } - + let res: Response; try { const { username, password } = backendState; const headers = prepareHeaders(username, password); @@ -538,7 +541,7 @@ async function confirmWithdrawalCall( `access-api/accounts/${backendState.username}/withdrawals/${withdrawalId}/confirm`, backendState.url ) - var res = await fetch(url.href, { + res = await fetch(url.href, { method: 'POST', headers }) @@ -547,25 +550,28 @@ async function confirmWithdrawalCall( pageStateSetter((prevState) => ({ ...prevState, hasError: true, - error: `Could not confirm the withdrawal: ${error}`})) + error: `Could not confirm the withdrawal: ${error}` + })) return; } - if (!res.ok) { + if (res ? !res.ok : true) { // assume not ok if res is null console.log(`Withdrawal confirmation gave response error (${res.status})`, res.statusText); pageStateSetter((prevState) => ({ ...prevState, hasError: true, - error: `Withdrawal confirmation gave response error (${res.status})`})) + error: `Withdrawal confirmation gave response error (${res.status})` + })) return; - } + } console.log('Withdrawal operation confirmed!'); pageStateSetter((prevState) => { const { talerWithdrawUri, ...rest } = prevState; return { ...rest, withdrawalOutcome: 'Withdrawal confirmed!' - }}) - + } + }) + } /** @@ -596,7 +602,8 @@ async function createTransactionCall( pageStateSetter((prevState) => ({ ...prevState, hasError: true, - error: `Could not create the wire transfer: ${error}`})) + error: `Could not create the wire transfer: ${error}` + })) return; } // POST happened, status not sure yet. @@ -606,7 +613,8 @@ async function createTransactionCall( pageStateSetter((prevState) => ({ ...prevState, hasError: true, - error: `Transfer creation gave response error: ${responseText} (${res.status})`})) + error: `Transfer creation gave response error: ${responseText} (${res.status})` + })) return; } // status is 200 OK here, tell the user. @@ -636,7 +644,7 @@ async function createWithdrawalCall( ) { if (typeof backendState === 'undefined') { console.log('Page has a problem: no credentials found in the state.'); - pageStateSetter((prevState) => ({...prevState, hasError: true, error: 'No credentials given.'})) + pageStateSetter((prevState) => ({ ...prevState, hasError: true, error: 'No credentials given.' })) return; } try { @@ -651,7 +659,7 @@ async function createWithdrawalCall( var res = await fetch(url.href, { method: 'POST', headers, - body: JSON.stringify({amount}), + body: JSON.stringify({ amount }), } ); } catch (error) { @@ -659,7 +667,8 @@ async function createWithdrawalCall( pageStateSetter((prevState) => ({ ...prevState, hasError: true, - error: `Could not create withdrawal operation: ${error}`})) + error: `Could not create withdrawal operation: ${error}` + })) return; } if (!res.ok) { @@ -668,7 +677,8 @@ async function createWithdrawalCall( pageStateSetter((prevState) => ({ ...prevState, hasError: true, - error: `Withdrawal creation gave response error: ${responseText} (${res.status})`})) + error: `Withdrawal creation gave response error: ${responseText} (${res.status})` + })) return; } @@ -678,7 +688,8 @@ async function createWithdrawalCall( ...prevState, withdrawalInProgress: true, talerWithdrawUri: resp.taler_withdraw_uri, - withdrawalId: resp.withdrawal_id})) + withdrawalId: resp.withdrawal_id + })) } async function loginCall( @@ -697,9 +708,9 @@ async function loginCall( * whether the credentials are valid. */ pageStateSetter((prevState) => ({ ...prevState, isLoggedIn: true })); let baseUrl = getRootPath(); - if (!baseUrl.endsWith('/')) { + if (!baseUrl.endsWith('/')) baseUrl += '/'; - } + backendStateSetter((prevState) => ({ ...prevState, url: baseUrl, @@ -733,9 +744,9 @@ async function registrationCall( * is not empty, then the concatenation made by URL() * drops the last path element. */ - if (!baseUrl.endsWith('/')) { + if (!baseUrl.endsWith('/')) baseUrl += '/' - } + const headers = new Headers(); headers.append( 'Content-Type', @@ -784,9 +795,9 @@ async function registrationCall( function Currency(): VNode { const { data, error } = useSWR(`${getRootPath()}integration-api/config`, fetcher); - if (typeof error !== 'undefined') { + if (typeof error !== 'undefined') return <b>error: currency could not be retrieved</b>; - } + if (typeof data === 'undefined') return <Fragment>"..."</Fragment>; console.log('found bank config', data); return data.currency; @@ -801,8 +812,9 @@ function ErrorBanner(Props: any): VNode | null { <a href="#" onClick={() => { pageStateSetter((prevState: PageStateType) => { delete prevState.error; // delete error message - return {...prevState, hasError: false} // delete error state - })}}> + return { ...prevState, hasError: false } // delete error state + }) + }}> {i18n`Clear`} </a> </p>); @@ -842,9 +854,9 @@ function BankFrame(Props: any): VNode { ['Survey', '__DEMO_SITE_SURVEY_URL__'], ]; const demo_sites = []; - for (const i in DEMO_SITES) { + for (const i in DEMO_SITES) demo_sites.push(<a href={DEMO_SITES[i][1]}>{DEMO_SITES[i][0]}</a>) - } + return ( <Fragment> <header class="demobar" style="display: flex; flex-direction: row; justify-content: space-between;"> @@ -856,19 +868,19 @@ function BankFrame(Props: any): VNode { </h1>{ maybeDemoContent(<p><Translate> This part of the demo shows how a bank that supports - Taler directly would work. In addition to using your own - bank account, you can also see the transaction history of - some <a href="#" onClick={goPublicAccounts(pageStateSetter)}>Public Accounts</a>. - </Translate></p> - ) - } + Taler directly would work. In addition to using your own + bank account, you can also see the transaction history of + some <a href="#" onClick={goPublicAccounts(pageStateSetter)}>Public Accounts</a>. + </Translate></p> + ) + } </div> <a href="https://taler.net/"> <img src={talerLogo} - height="100" - width="224" - style="margin: 2em 2em" /> + height="100" + width="224" + style="margin: 2em 2em" /> </a> </header> <div style="display:flex; flex-direction: column;" class="navcontainer"> @@ -876,7 +888,7 @@ function BankFrame(Props: any): VNode { {maybeDemoContent(<Fragment>{demo_sites}</Fragment>)} <div class="right"> <LangSelector /> - </div> + </div> </nav> </div> <section id="main" class="content"> @@ -908,21 +920,19 @@ function PaytoWireTransfer(Props: any): VNode { const focusInput = useRef(null); useEffect(() => { console.log('Now focus', focusInput); - if (focusInput.current) { - // @ts-ignore - focusInput.current.focus(); - } + if (focusInput.current) + (focusInput.current as any).focus(); }, []); console.log('wire form page state', pageState); const goBackForm = <a href="#" onClick={ () => { - pageStateSetter((prevState: PageStateType) => ({...prevState, tryManualTransfer: false})) + pageStateSetter((prevState: PageStateType) => ({ ...prevState, tryManualTransfer: false })) submitDataSetter(undefined) } }>{i18n`Go back`}</a>; const goBackRawPayto = <a href="#" onClick={ () => { - pageStateSetter((prevState: PageStateType) => ({...prevState, isRawPayto: false})) + pageStateSetter((prevState: PageStateType) => ({ ...prevState, isRawPayto: false })) rawPaytoInputSetter(undefined) } @@ -938,13 +948,14 @@ function PaytoWireTransfer(Props: any): VNode { ref={focusInput} type="text" placeholder="receiver iban" - required + required pattern={ibanRegex} onInput={(e): void => { submitDataSetter((submitData: any) => ({ ...submitData, iban: e.currentTarget.value, - }))}} /><br /><br /> + })) + }} /><br /><br /> <input type="text" placeholder="subject" @@ -953,7 +964,8 @@ function PaytoWireTransfer(Props: any): VNode { submitDataSetter((submitData: any) => ({ ...submitData, subject: e.currentTarget.value, - }))}} /><br /><br /> + })) + }} /><br /><br /> <input type="text" placeholder="amount" @@ -967,7 +979,8 @@ function PaytoWireTransfer(Props: any): VNode { submitDataSetter((submitData: any) => ({ ...submitData, amount: e.currentTarget.value.replace(',', '.'), - }))}} /> <label>{currency}</label><br /><br /> + })) + }} /> <label>{currency}</label><br /><br /> <input type="submit" value="Send" @@ -983,7 +996,7 @@ function PaytoWireTransfer(Props: any): VNode { ) { console.log('Not all the fields were given.'); pageStateSetter((prevState: PageStateType) => - ({...prevState, hasError: true, error: 'Field(s) missing.'})) + ({ ...prevState, hasError: true, error: 'Field(s) missing.' })) return; } transactionData = { @@ -996,14 +1009,14 @@ function PaytoWireTransfer(Props: any): VNode { pageStateSetter, submitDataSetter // need here only to be cleaned. ); - }} /> + }} /> </div> <p><a href="#" onClick={() => { - console.log('switch to raw payto form'); - pageStateSetter((prevState: any) => ({...prevState, isRawPayto: true})); - }}>{i18n`Want to try the raw payto://-format?`} + console.log('switch to raw payto form'); + pageStateSetter((prevState: any) => ({ ...prevState, isRawPayto: true })); + }}>{i18n`Want to try the raw payto://-format?`} </a></p> </div> {goBackForm} @@ -1015,7 +1028,7 @@ function PaytoWireTransfer(Props: any): VNode { <h2>{i18n`Wire transfer`}</h2> <p>{i18n`Transfer money via the Payto system:`}<br /><br /> Address pattern: <code style="font-size: 15px"> - payto://iban/[receiver-iban]?message=[subject]&amount=[{currency}:X.Y] + payto://iban/[receiver-iban]?message=[subject]&amount=[{currency}:X.Y] </code> </p> <div name="payto-form"> @@ -1031,15 +1044,15 @@ function PaytoWireTransfer(Props: any): VNode { <input class="pure-button pure-button-primary" type="submit" value={i18n`Confirm`} - onClick={() => { + onClick={() => { // empty string evaluates to false. if (!rawPaytoInput) { console.log('Didn\'t get any raw Payto string!'); return; } - transactionData = {paytoUri: rawPaytoInput}; + transactionData = { paytoUri: rawPaytoInput }; if (typeof transactionData.paytoUri === 'undefined' || - transactionData.paytoUri.length === 0) return; + transactionData.paytoUri.length === 0) return; createTransactionCall( transactionData, Props.backendState, @@ -1082,26 +1095,26 @@ function TalerWithdrawalConfirmationQuestion(Props: any): VNode { <input type="submit" value="confirm" - onClick={ () => { + onClick={() => { if (captchaAnswer == (captchaNumbers.a + captchaNumbers.b).toString()) { confirmWithdrawalCall( backendState, pageState.withdrawalId, pageStateSetter) - return; - } + return; + } pageStateSetter((prevState: PageStateType) => - ({...prevState, hasError: true, error: 'Answer is wrong.'})) + ({ ...prevState, hasError: true, error: 'Answer is wrong.' })) }} /> <input type="submit" value="abort" - onClick={ () => - abortWithdrawalCall( + onClick={() => + abortWithdrawalCall( backendState, pageState.withdrawalId, pageStateSetter - )} /> + )} /> </div> <p><Translate> A this point, a <b>real</b> bank would ask for an additional @@ -1111,19 +1124,19 @@ function TalerWithdrawalConfirmationQuestion(Props: any): VNode { </Fragment>); } -function QrCodeSection({talerWithdrawUri, abortButton}:{talerWithdrawUri:string, abortButton: h.JSX.Element}) { +function QrCodeSection({ talerWithdrawUri, abortButton }: { talerWithdrawUri: string, abortButton: h.JSX.Element }) { const i18n = useTranslator(); useEffect(() => { //Taler Wallet WebExtension is listening to headers response and tab updates. //In the SPA there is no header response with the Taler URI so //this hack manually triggers the tab update after the QR is in the DOM. - window.location.href = `${window.location.href.split('#')[0] }#` - },[]) + window.location.href = `${window.location.href.split('#')[0]}#` + }, []) return <section id="main" class="content"> <h1 class="nav">{i18n`Withdraw to a Taler Wallet`}</h1> <p>{i18n`You can use this QR code to withdraw to your mobile wallet:`}</p> - {QR({text: talerWithdrawUri})} + {QR({ text: talerWithdrawUri })} <p>Click <a id="linkqr" href={talerWithdrawUri}>{i18n`this link`}</a> to open your Taler wallet!</p> <br /> {abortButton} @@ -1148,7 +1161,8 @@ function TalerWithdrawalQRCode(Props: any): VNode { pageStateSetter((prevState: PageStateType) => { const { withdrawalOutcome, withdrawalId, talerWithdrawUri, ...rest } = prevState; return { ...rest, withdrawalInProgress: false }; - })}}>{i18n`Abort`}</a> + }) + }}>{i18n`Abort`}</a> console.log(`Showing withdraw URI: ${talerWithdrawUri}`); // waiting for the wallet: @@ -1166,15 +1180,15 @@ function TalerWithdrawalQRCode(Props: any): VNode { } // data didn't arrive yet and wallet didn't communicate: - if (typeof data === 'undefined') { + if (typeof data === 'undefined') return <p>{i18n`Waiting the bank to create the operaion...`}</p> - } + /** * Wallet didn't communicate withdrawal details yet: */ console.log('withdrawal status', data); - if (data.aborted) { + if (data.aborted) pageStateSetter((prevState: PageStateType) => { const { withdrawalOutcome, @@ -1188,7 +1202,7 @@ function TalerWithdrawalQRCode(Props: any): VNode { error: i18n`This withdrawal was aborted!` }; }) - } + if (!data.selection_done) { setTimeout(() => mutate(), 1000); // check again after 1 second. @@ -1205,7 +1219,7 @@ function TalerWithdrawalQRCode(Props: any): VNode { * Let the user choose an amount and submit the withdtawal. */ function TalerWithdrawal(Props: any): VNode { - const {backendState, pageStateSetter} = Props; + const { backendState, pageStateSetter } = Props; const currency = useContext(CurrencyContext); const i18n = useTranslator(); let submitAmount = '5.00'; // must match the first <select> child. @@ -1228,7 +1242,8 @@ function TalerWithdrawal(Props: any): VNode { `${currency}:${submitAmount}`, backendState, pageStateSetter - )}} />; + ) + }} />; return (<article> <div> @@ -1238,22 +1253,23 @@ function TalerWithdrawal(Props: any): VNode { name="tform"> {i18n`Amount to withdraw`}: <select id="reserve-amount" - name="withdraw-amount" - class="amount" autofocus + name="withdraw-amount" + class="amount" autofocus onChange={(e): void => { - submitAmount = e.currentTarget.value; }}> + submitAmount = e.currentTarget.value; + }}> <option value="5.00">5.00</option> <option value="10.00">10.00</option> <option value="15.00">15.00</option> <option value="20.00">20.00</option> </select> <input - type="text" - readonly - class="currency-indicator" - size={currency.length} - tabIndex={-1} value={currency} /> - {submitButton} + type="text" + readonly + class="currency-indicator" + size={currency.length} + tabIndex={-1} value={currency} /> + {submitButton} </div> </div> </article>); @@ -1263,7 +1279,7 @@ function TalerWithdrawal(Props: any): VNode { * Collect and submit login data. */ function LoginForm(Props: any): VNode { - const {backendStateSetter, pageStateSetter} = Props; + const { backendStateSetter, pageStateSetter } = Props; const [submitData, submitDataSetter] = useCredentialsRequestType(); const i18n = useTranslator(); // FIXME: try removing the outer Fragment. @@ -1279,23 +1295,25 @@ function LoginForm(Props: any): VNode { submitDataSetter((submitData: any) => ({ ...submitData, username: e.currentTarget.value, - }))}} /> + })) + }} /> <input type="password" value={submitData && submitData.password} placeholder="password" required onInput={(e): void => { - submitDataSetter((submitData: any) => ({ + submitDataSetter((submitData: any) => ({ ...submitData, password: e.currentTarget.value, - }))}} /> + })) + }} /> <button autofocus type="submit" class="pure-button pure-button-primary" onClick={() => { - if (typeof submitData === 'undefined') { + if (typeof submitData === 'undefined') { console.log('login data is undefined', submitData); return; } @@ -1306,7 +1324,7 @@ function LoginForm(Props: any): VNode { loginCall( // Deep copy, to avoid the cleanup // below make data disappear. - {...submitData}, + { ...submitData }, backendStateSetter, pageStateSetter ); @@ -1340,10 +1358,11 @@ function RegistrationForm(Props: any): VNode { required autofocus onInput={(e): void => { - submitDataSetter((submitData: any) => ({ + submitDataSetter((submitData: any) => ({ ...submitData, username: e.currentTarget.value, - }))}} /> + })) + }} /> <br /> {i18n`Password:`} <input @@ -1353,10 +1372,11 @@ function RegistrationForm(Props: any): VNode { required autofocus onInput={(e): void => { - submitDataSetter((submitData: any) => ({ + submitDataSetter((submitData: any) => ({ ...submitData, password: e.currentTarget.value, - }))}} /> + })) + }} /> <br /> {/* {i18n`Phone number:`} @@ -1375,28 +1395,28 @@ function RegistrationForm(Props: any): VNode { <br /> */} <button - autofocus + autofocus class="pure-button pure-button-primary" onClick={() => { - console.log('maybe submitting the registration..'); - console.log(submitData); - if (typeof submitData === 'undefined') { + console.log('maybe submitting the registration..'); + console.log(submitData); + if (typeof submitData === 'undefined') { console.log(`submit data ${submitData} is undefined`); return; } - if ((typeof submitData.password === 'undefined') || - (typeof submitData.username === 'undefined')) { + if ((typeof submitData.password === 'undefined') || + (typeof submitData.username === 'undefined')) { console.log('username or password is undefined'); return; } - if (submitData.password.length === 0 || - submitData.username.length === 0) { + if (submitData.password.length === 0 || + submitData.username.length === 0) { console.log('username or password are the empty string'); return; } - console.log('submitting the registration..'); + console.log('submitting the registration..'); registrationCall( - {...submitData}, + { ...submitData }, Props.backendStateSetter, // will store BE URL, if OK. pageStateSetter ); @@ -1408,15 +1428,19 @@ function RegistrationForm(Props: any): VNode { * strings due to a non lively update of the <input> fields * after setting to undefined. */ - submitDataSetter({username: '', password: ''})}}>{i18n`Register`}</button> - // FIXME: should use a different color + submitDataSetter({ username: '', password: '' }) + }}> + {i18n`Register`} + </button> + {/* FIXME: should use a different color */} <button - autofocus + autofocus class="pure-button pure-button-primary" onClick={() => { - pageStateSetter((prevState: PageStateType) =>({...prevState, tryRegister: false}))}}> - {i18n`cancel`}</button> - + pageStateSetter((prevState: PageStateType) => ({ ...prevState, tryRegister: false })) + }}> + {i18n`cancel`} + </button> </form> </div> </article> @@ -1435,7 +1459,7 @@ function Transactions(Props: any): VNode { ); if (typeof error !== 'undefined') { console.log('transactions not found error', error); - switch(error.status) { + switch (error.status) { case 404: { return <p>Transactions page {pageNumber} was not found.</p> } @@ -1463,7 +1487,7 @@ function Transactions(Props: any): VNode { </tr> </thead> <tbody> - {data.transactions.map((item: any) => { + {data.transactions.map((item: any, idx: number) => { const sign = item.direction == 'DBIT' ? '-' : ''; const counterpart = item.direction == 'DBIT' ? item.creditorIban : item.debtorIban; // Pattern: @@ -1473,7 +1497,7 @@ function Transactions(Props: any): VNode { const dateRegex = /^([0-9]{4})-([0-9]{2})-([0-9]{1,2})/ const dateParse = dateRegex.exec(item.date) const date = dateParse !== null ? `${dateParse[3]}/${dateParse[2]} ${dateParse[1]}` : 'date not found' - return (<tr> + return (<tr key={idx}> <td>{date}</td> <td>{sign}{item.amount} {item.currency}</td> <td>{counterpart}</td> @@ -1512,9 +1536,9 @@ function Account(Props: any): VNode { */ const [txPageNumber, setTxPageNumber] = useTransactionPageNumber() const txsPages = [] - for (let i = 0; i <= txPageNumber; i++) { + for (let i = 0; i <= txPageNumber; i++) txsPages.push(<Transactions accountLabel={accountLabel} pageNumber={i} />) - } + if (typeof error !== 'undefined') { console.log('account error', error); /** @@ -1522,7 +1546,7 @@ function Account(Props: any): VNode { * of pageStateSetter, after having decided the error * message in the case-branch. */ - switch(error.status) { + switch (error.status) { case 404: { pageStateSetter((prevState: PageStateType) => ({ ...prevState, @@ -1532,18 +1556,18 @@ function Account(Props: any): VNode { })); /** - * 404 should never stick to the cache, because they - * taint successful future registrations. How? After - * registering, the user gets navigated to this page, - * therefore a previous 404 on this SWR key (the requested - * resource) would still appear as valid and cause this - * page not to be shown! A typical case is an attempted - * login of a unregistered user X, and then a registration - * attempt of the same user X: in this case, the failed - * login would cache a 404 error to X's profile, resulting - * in the legitimate request after the registration to still - * be flagged as 404. Clearing the cache should prevent - * this. */ + * 404 should never stick to the cache, because they + * taint successful future registrations. How? After + * registering, the user gets navigated to this page, + * therefore a previous 404 on this SWR key (the requested + * resource) would still appear as valid and cause this + * page not to be shown! A typical case is an attempted + * login of a unregistered user X, and then a registration + * attempt of the same user X: in this case, the failed + * login would cache a 404 error to X's profile, resulting + * in the legitimate request after the registration to still + * be flagged as 404. Clearing the cache should prevent + * this. */ (cache as any).clear(); return <p>Profile not found...</p>; } @@ -1552,7 +1576,7 @@ function Account(Props: any): VNode { ...prevState, hasError: true, isLoggedIn: false, - error: i18n`Wrong credentials given.` + error: i18n`Wrong credentials given.` })); return <p>Wrong credentials...</p>; } @@ -1561,7 +1585,7 @@ function Account(Props: any): VNode { ...prevState, hasError: true, isLoggedIn: false, - error: i18n`Account information could not be retrieved.` + error: i18n`Account information could not be retrieved.` })); return <p>Unknown problem...</p>; } @@ -1574,7 +1598,7 @@ function Account(Props: any): VNode { * such state is usually successful, as errors should * have been reported earlier. */ - if (transferOutcome) { + if (transferOutcome) return <BankFrame> <p>{transferOutcome}</p> <button onClick={() => { @@ -1583,16 +1607,18 @@ function Account(Props: any): VNode { tryManualTransfer, // Still show the wire transfer form? transferOutcome, ...rest } = prevState; - return {...rest};})}}> + return { ...rest }; + }) + }}> {i18n`Close wire transfer`} </button> </BankFrame> - } + /** * Withdrawal reached a final state: show it. */ - if (withdrawalOutcome) { + if (withdrawalOutcome) return <BankFrame> <p>{withdrawalOutcome}</p> <button onClick={() => { @@ -1601,11 +1627,13 @@ function Account(Props: any): VNode { return { ...rest, withdrawalInProgress: false - };})}}> + }; + }) + }}> {i18n`Close Taler withdrawal`} </button> </BankFrame> - } + /** * This block shows the withdrawal QR code. @@ -1625,21 +1653,21 @@ function Account(Props: any): VNode { return ( <BankFrame> <TalerWithdrawalQRCode - accountLabel={accountLabel} - backendState={backendState} - withdrawalId={withdrawalId} - talerWithdrawUri={talerWithdrawUri} /> + accountLabel={accountLabel} + backendState={backendState} + withdrawalId={withdrawalId} + talerWithdrawUri={talerWithdrawUri} /> </BankFrame> ); } const balance = parseAmount(data.balance.amount) - if (tryManualTransfer) { + if (tryManualTransfer) return ( <BankFrame> <CurrencyContext.Provider value={balance.currency}> <PaytoWireTransfer backendState={backendState} /> </CurrencyContext.Provider></BankFrame>); - } + return (<BankFrame> <div> <h1 class="nav"> @@ -1648,7 +1676,7 @@ function Account(Props: any): VNode { </div> <section id="menu"> <p>{i18n`Bank account balance:`} <br /> - { data.balance.credit_debit_indicator == 'debit' ? (<b>-</b>) : null } + {data.balance.credit_debit_indicator == 'debit' ? (<b>-</b>) : null} <b>{`${balance.value} ${balance.currency}`}</b></p> </section> <CurrencyContext.Provider value={balance.currency}> @@ -1663,7 +1691,7 @@ function Account(Props: any): VNode { <Transactions pageNumber="0" accountLabel={accountLabel} /> <p><a href="#" onClick={() => pageStateSetter((prevState: PageStateType) => - ({...prevState, tryManualTransfer: true})) + ({ ...prevState, tryManualTransfer: true })) }>{i18n`Transfer money manually`}</a></p> </article> </section> @@ -1678,7 +1706,7 @@ function SWRWithCredentials(props: any): VNode { const headers = new Headers(); headers.append( 'Authorization', - `Basic ${Buffer.from(`${username }:${ password}`).toString('base64')}` + `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}` ); console.log('Likely backend base URL', backendUrl); return ( @@ -1686,13 +1714,13 @@ function SWRWithCredentials(props: any): VNode { value={{ fetcher: (url) => fetch(backendUrl + url || '', { headers }).then( - (r) => { - if (!r.ok) { - throw {status: r.status, json: r.json()}; - } + (r) => { + if (!r.ok) + throw { status: r.status, json: r.json() }; + return r.json() - } - ), + } + ), }}>{props.children}</SWRConfig> ); } @@ -1705,13 +1733,13 @@ function SWRWithoutCredentials(Props: any): VNode { value={{ fetcher: (url) => fetch(baseUrl + url || '').then( - (r) => { - if (!r.ok) { - throw {status: r.status, json: r.json()}; - } + (r) => { + if (!r.ok) + throw { status: r.status, json: r.json() }; + return r.json() - } - ), + } + ), }}>{Props.children}</SWRConfig> ); } @@ -1726,7 +1754,7 @@ function PublicHistories(Props: any): VNode { if (typeof error !== 'undefined') { console.log('account error', error); - switch(error.status) { + switch (error.status) { case 404: console.log('public accounts: 404', error); Props.pageStateSetter((prevState: PageStateType) => ({ @@ -1742,7 +1770,7 @@ function PublicHistories(Props: any): VNode { ...prevState, hasError: true, showPublicHistories: false, - error: i18n`List of public accounts could not be retrieved.` + error: i18n`List of public accounts could not be retrieved.` })); break; } @@ -1766,7 +1794,7 @@ function PublicHistories(Props: any): VNode { accountsBar.push( <li class={isSelected ? 'pure-menu-selected pure-menu-item' : 'pure-menu-item pure-menu'}> <a href="#" - class="pure-menu-link" + class="pure-menu-link" onClick={() => setShowAccount(account.accountLabel)}>{account.accountLabel}</a> </li> ); @@ -1798,37 +1826,38 @@ export function BankHome(): VNode { const setTxPageNumber = useTransactionPageNumber()[1]; const i18n = useTranslator(); - if (pageState.showPublicHistories) { + if (pageState.showPublicHistories) return (<SWRWithoutCredentials baseUrl={getRootPath()}> <PageContext.Provider value={[pageState, pageStateSetter]}> <BankFrame> <PublicHistories pageStateSetter={pageStateSetter}> - <br /> + <br /> <a class="pure-button" onClick={() => { pageStateSetter((prevState: PageStateType) => - ({...prevState, showPublicHistories: false}))}}>Go back</a> + ({ ...prevState, showPublicHistories: false })) + }}>Go back</a> </PublicHistories> </BankFrame> </PageContext.Provider> </SWRWithoutCredentials>); - } + if (pageState.tryRegister) { // @ts-expect-error Global variable unknown to ts console.log('allow registrations?', __LIBEUFIN_UI_ALLOW_REGISTRATIONS__) // @ts-expect-error Global variable unknown to ts - if (__LIBEUFIN_UI_ALLOW_REGISTRATIONS__) { + if (__LIBEUFIN_UI_ALLOW_REGISTRATIONS__) return ( <PageContext.Provider value={[pageState, pageStateSetter]}> <BankFrame> - <RegistrationForm backendStateSetter={backendStateSetter} /> - </BankFrame> + <RegistrationForm backendStateSetter={backendStateSetter} /> + </BankFrame> </PageContext.Provider> ); - } + return ( <PageContext.Provider value={[pageState, pageStateSetter]}> <BankFrame> - <p>{i18n`Currently, the bank is not accepting new registrations!`}</p> + <p>{i18n`Currently, the bank is not accepting new registrations!`}</p> </BankFrame> </PageContext.Provider> ); @@ -1861,19 +1890,20 @@ export function BankHome(): VNode { * bank did.) FIXME: currency needed at startup too. */ const regMsg = function () { // @ts-expect-error Global variable unknown to ts - if (__LIBEUFIN_UI_ALLOW_REGISTRATIONS__) { + if (__LIBEUFIN_UI_ALLOW_REGISTRATIONS__) return (<Fragment> <p><Translate>If you are a new customer please - <a href="#" onClick={() => - {pageStateSetter((prevState) => - ({...prevState, tryRegister: true}))}}>register!</a> - </Translate></p>{ - maybeDemoContent(<p><Translate>Registration is fast and + <a href="#" onClick={() => { + pageStateSetter((prevState) => + ({ ...prevState, tryRegister: true })) + }}>register!</a> + </Translate></p>{ + maybeDemoContent(<p><Translate>Registration is fast and free, and it gives you a registration bonus of 100 <Currency /> - </Translate></p>) - /*close JS block of optional content*/ } + </Translate></p>) + /*close JS block of optional content*/} </Fragment>); // close return of registrations allowance. - } // close 'then' branch of registrations allowance. + // close 'then' branch of registrations allowance. } // close helper function. return ( <PageContext.Provider value={[pageState, pageStateSetter]}> @@ -1884,7 +1914,7 @@ export function BankHome(): VNode { backendStateSetter={backendStateSetter} /> {regMsg()} {maybeDemoContent(<p><Translate> - To view transactions of public accounts, please <a href="#" + To view transactions of public accounts, please <a href="#" onClick={goPublicAccounts(pageStateSetter)}>click here</a>. </Translate></p> )} |