diff options
author | Sebastian <sebasjm@gmail.com> | 2022-06-02 12:20:36 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2022-06-02 12:20:36 -0300 |
commit | 5d9390bb3437171353db99e7afd8cbcca432ba5c (patch) | |
tree | a71d71d26f32d8d4334c8d49f8766d8a287340de | |
parent | af7b107f455b01e136db2211c357cc59a506139a (diff) |
typechecked dynamic path
10 files changed, 146 insertions, 123 deletions
diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx index 58783a087..9c8e1ea29 100644 --- a/packages/taler-wallet-webextension/src/NavigationBar.tsx +++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx @@ -40,43 +40,94 @@ import settingsIcon from "./svg/settings_black_24dp.svg"; * @author sebasjm */ -export enum Pages { - welcome = "/welcome", - - balance = "/balance", - balance_history = "/balance/history/:currency?", - balance_manual_withdraw = "/balance/manual-withdraw/:currency?", - balance_deposit = "/balance/deposit/:currency", - balance_transaction = "/balance/transaction/:tid", - - dev = "/dev", - - backup = "/backup", - backup_provider_detail = "/backup/provider/:pid", - backup_provider_add = "/backup/provider/add", - - settings = "/settings", - settings_exchange_add = "/settings/exchange/add/:currency?", - - cta = "/cta/:action", - cta_pay = "/cta/pay", - cta_refund = "/cta/refund", - cta_tips = "/cta/tip", - cta_withdraw = "/cta/withdraw", - cta_deposit = "/cta/deposit", +type PageLocation<DynamicPart extends object> = { + pattern: string; + (params: DynamicPart): string; +}; + +function replaceAll( + pattern: string, + vars: Record<string, string>, + values: Record<string, any>, +): string { + let result = pattern; + for (const v in vars) { + result = result.replace(vars[v], values[v]); + } + return result; } +function pageDefinition<T extends object>(pattern: string): PageLocation<T> { + const patternParams = pattern.match(/(:[\w?]*)/g); + if (!patternParams) + throw Error( + `page definition pattern ${pattern} doesn't have any parameter`, + ); + + const vars = patternParams.reduce((prev, cur) => { + const pName = cur.match(/(\w+)/g); + + //skip things like :? in the path pattern + if (!pName || !pName[0]) return prev; + const name = pName[0]; + return { ...prev, [name]: cur }; + }, {} as Record<string, string>); + + const f = (values: T): string => replaceAll(pattern, vars, values); + f.pattern = pattern; + return f; +} + +export const Pages = { + welcome: "/welcome", + balance: "/balance", + balanceHistory: pageDefinition<{ currency?: string }>( + "/balance/history/:currency?", + ), + balanceManualWithdraw: pageDefinition<{ currency?: string }>( + "/balance/manual-withdraw/:currency?", + ), + balanceDeposit: pageDefinition<{ currency: string }>( + "/balance/deposit/:currency", + ), + balanceTransaction: pageDefinition<{ tid: string }>( + "/balance/transaction/:tid", + ), + dev: "/dev", + + backup: "/backup", + backupProviderDetail: pageDefinition<{ pid: string }>( + "/backup/provider/:pid", + ), + backupProviderAdd: "/backup/provider/add", + + settings: "/settings", + settingsExchangeAdd: pageDefinition<{ currency?: string }>( + "/settings/exchange/add/:currency?", + ), + + cta: pageDefinition<{ action: string }>("/cta/:action"), + ctaPay: "/cta/pay", + ctaRefund: "/cta/refund", + ctaTips: "/cta/tip", + ctaWithdraw: "/cta/withdraw", + ctaDeposit: "/cta/deposit", +}; + export function PopupNavBar({ path = "" }: { path?: string }): VNode { const { i18n } = useTranslationContext(); return ( <NavigationHeader> - <a href="/balance" class={path.startsWith("/balance") ? "active" : ""}> + <a + href={Pages.balance} + class={path.startsWith("/balance") ? "active" : ""} + > <i18n.Translate>Balance</i18n.Translate> </a> - <a href="/backup" class={path.startsWith("/backup") ? "active" : ""}> + <a href={Pages.backup} class={path.startsWith("/backup") ? "active" : ""}> <i18n.Translate>Backup</i18n.Translate> </a> - <a href="/settings"> + <a href={Pages.settings}> <SvgIcon title={i18n.str`Settings`} dangerouslySetInnerHTML={{ __html: settingsIcon }} @@ -92,21 +143,27 @@ export function WalletNavBar({ path = "" }: { path?: string }): VNode { return ( <NavigationHeaderHolder> <NavigationHeader> - <a href="/balance" class={path.startsWith("/balance") ? "active" : ""}> + <a + href={Pages.balance} + class={path.startsWith("/balance") ? "active" : ""} + > <i18n.Translate>Balance</i18n.Translate> </a> - <a href="/backup" class={path.startsWith("/backup") ? "active" : ""}> + <a + href={Pages.backup} + class={path.startsWith("/backup") ? "active" : ""} + > <i18n.Translate>Backup</i18n.Translate> </a> <JustInDevMode> - <a href="/dev" class={path.startsWith("/dev") ? "active" : ""}> + <a href={Pages.dev} class={path.startsWith("/dev") ? "active" : ""}> <i18n.Translate>Dev</i18n.Translate> </a> </JustInDevMode> <a - href="/settings" + href={Pages.settings} class={path.startsWith("/settings") ? "active" : ""} > <i18n.Translate>Settings</i18n.Translate> diff --git a/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx b/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx index 2fc2d2d38..55287903a 100644 --- a/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx +++ b/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx @@ -14,7 +14,7 @@ import Banner from "./Banner.js"; import { Time } from "./Time.js"; interface Props extends JSX.HTMLAttributes { - goToTransaction: (id: string) => void; + goToTransaction: (id: string) => Promise<void>; } export function PendingTransactions({ goToTransaction }: Props): VNode { @@ -46,7 +46,7 @@ export function PendingTransactionsView({ transactions, goToTransaction, }: { - goToTransaction: (id: string) => void; + goToTransaction: (id: string) => Promise<void>; transactions: Transaction[]; }): VNode { return ( diff --git a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx index bfffa3267..127d0411a 100644 --- a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx +++ b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx @@ -120,7 +120,7 @@ function TransactionLayout(props: TransactionLayoutProps): VNode { const { i18n } = useTranslationContext(); return ( <HistoryRow - href={Pages.balance_transaction.replace(":tid", props.id)} + href={Pages.balanceTransaction({ tid: props.id })} style={{ backgroundColor: props.pending ? "lightcyan" : "inherit", alignItems: "center", diff --git a/packages/taler-wallet-webextension/src/popup/Application.tsx b/packages/taler-wallet-webextension/src/popup/Application.tsx index ba2886a41..d78677a9e 100644 --- a/packages/taler-wallet-webextension/src/popup/Application.tsx +++ b/packages/taler-wallet-webextension/src/popup/Application.tsx @@ -45,8 +45,9 @@ function CheckTalerActionComponent(): VNode { const [talerActionUrl] = useTalerActionURL(); useEffect(() => { - if (talerActionUrl) - route(Pages.cta.replace(":action", encodeURIComponent(talerActionUrl))); + if (talerActionUrl) { + route(Pages.cta({ action: encodeURIComponent(talerActionUrl) })); + } }, [talerActionUrl]); return <Fragment />; @@ -60,8 +61,8 @@ export function Application(): VNode { {({ devMode }: { devMode: boolean }) => ( <IoCProviderForRuntime> <PendingTransactions - goToTransaction={(txId: string) => - redirectTo(Pages.balance_transaction.replace(":tid", txId)) + goToTransaction={(tid: string) => + redirectTo(Pages.balanceTransaction({ tid })) } /> <Match> @@ -74,24 +75,18 @@ export function Application(): VNode { path={Pages.balance} component={BalancePage} goToWalletManualWithdraw={() => - redirectTo( - Pages.balance_manual_withdraw.replace(":currency?", ""), - ) + redirectTo(Pages.balanceManualWithdraw({})) } goToWalletDeposit={(currency: string) => - redirectTo( - Pages.balance_deposit.replace(":currency", currency), - ) + redirectTo(Pages.balanceDeposit({ currency })) } goToWalletHistory={(currency: string) => - redirectTo( - Pages.balance_history.replace(":currency?", currency), - ) + redirectTo(Pages.balanceHistory({ currency })) } /> <Route - path={Pages.cta} + path={Pages.cta.pattern} component={function Action({ action }: { action: string }) { const [, setDismissed] = useTalerActionURL(); @@ -110,37 +105,37 @@ export function Application(): VNode { <Route path={Pages.backup} component={BackupPage} - onAddProvider={() => redirectTo(Pages.backup_provider_add)} + onAddProvider={() => redirectTo(Pages.backupProviderAdd)} /> <Route - path={Pages.backup_provider_detail} + path={Pages.backupProviderDetail.pattern} component={ProviderDetailPage} onBack={() => redirectTo(Pages.backup)} /> <Route - path={Pages.balance_transaction} + path={Pages.balanceTransaction.pattern} component={RedirectToWalletPage} /> <Route - path={Pages.balance_manual_withdraw} + path={Pages.balanceManualWithdraw.pattern} component={RedirectToWalletPage} /> <Route - path={Pages.balance_deposit} + path={Pages.balanceDeposit.pattern} component={RedirectToWalletPage} /> <Route - path={Pages.balance_history} + path={Pages.balanceHistory.pattern} component={RedirectToWalletPage} /> <Route - path={Pages.backup_provider_add} + path={Pages.backupProviderAdd} component={RedirectToWalletPage} /> <Route path={Pages.settings} component={RedirectToWalletPage} /> <Route - path={Pages.settings_exchange_add} + path={Pages.settingsExchangeAdd.pattern} component={RedirectToWalletPage} /> <Route path={Pages.dev} component={RedirectToWalletPage} /> diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx b/packages/taler-wallet-webextension/src/wallet/Application.tsx index 37ea80d96..84aa0dd84 100644 --- a/packages/taler-wallet-webextension/src/wallet/Application.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx @@ -93,10 +93,8 @@ export function Application(): VNode { }} > <PendingTransactions - goToTransaction={(txId: string) => - redirectTo( - Pages.balance_transaction.replace(":tid", txId), - ) + goToTransaction={(tid: string) => + redirectTo(Pages.balanceTransaction({ tid })) } /> </div> @@ -122,50 +120,37 @@ export function Application(): VNode { */} <Route - path={Pages.balance_history} + path={Pages.balanceHistory.pattern} component={HistoryPage} goToWalletDeposit={(currency: string) => - redirectTo( - Pages.balance_deposit.replace(":currency", currency), - ) + redirectTo(Pages.balanceDeposit({ currency })) } goToWalletManualWithdraw={(currency?: string) => - redirectTo( - Pages.balance_manual_withdraw.replace( - ":currency?", - currency || "", - ), - ) + redirectTo(Pages.balanceManualWithdraw({ currency })) } /> <Route - path={Pages.balance_transaction} + path={Pages.balanceTransaction.pattern} component={TransactionPage} goToWalletHistory={(currency?: string) => - redirectTo( - Pages.balance_history.replace(":currency?", currency || ""), - ) + redirectTo(Pages.balanceHistory({ currency })) } /> <Route - path={Pages.balance_manual_withdraw} + path={Pages.balanceManualWithdraw.pattern} component={ManualWithdrawPage} onCancel={() => redirectTo(Pages.balance)} /> <Route - path={Pages.balance_deposit} + path={Pages.balanceDeposit.pattern} component={DepositPage} onCancel={(currency: string) => { - redirectTo( - Pages.balance_history.replace(":currency?", currency), - ); + redirectTo(Pages.balanceHistory({ currency })); }} onSuccess={(currency: string) => { - redirectTo( - Pages.balance_history.replace(":currency?", currency), - ); + redirectTo(Pages.balanceHistory({ currency })); setGlobalNotification( <i18n.Translate> All done, your transaction is in progress @@ -184,15 +169,15 @@ export function Application(): VNode { <Route path={Pages.backup} component={BackupPage} - onAddProvider={() => redirectTo(Pages.backup_provider_add)} + onAddProvider={() => redirectTo(Pages.backupProviderAdd)} /> <Route - path={Pages.backup_provider_detail} + path={Pages.backupProviderDetail.pattern} component={ProviderDetailPage} onBack={() => redirectTo(Pages.backup)} /> <Route - path={Pages.backup_provider_add} + path={Pages.backupProviderAdd} component={ProviderAddPage} onBack={() => redirectTo(Pages.backup)} /> @@ -201,7 +186,7 @@ export function Application(): VNode { * SETTINGS */} <Route - path={Pages.settings_exchange_add} + path={Pages.settingsExchangeAdd.pattern} component={ExchangeAddPage} onBack={() => redirectTo(Pages.balance)} /> @@ -216,22 +201,17 @@ export function Application(): VNode { * CALL TO ACTION */} <Route - path={Pages.cta_pay} + path={Pages.ctaPay} component={PayPage} goToWalletManualWithdraw={(currency?: string) => - redirectTo( - Pages.balance_manual_withdraw.replace( - ":currency?", - currency || "", - ), - ) + redirectTo(Pages.balanceManualWithdraw({ currency })) } goBack={() => redirectTo(Pages.balance)} /> - <Route path={Pages.cta_refund} component={RefundPage} /> - <Route path={Pages.cta_tips} component={TipPage} /> - <Route path={Pages.cta_withdraw} component={WithdrawPage} /> - <Route path={Pages.cta_deposit} component={DepositPageCTA} /> + <Route path={Pages.ctaRefund} component={RefundPage} /> + <Route path={Pages.ctaTips} component={TipPage} /> + <Route path={Pages.ctaWithdraw} component={WithdrawPage} /> + <Route path={Pages.ctaDeposit} component={DepositPageCTA} /> {/** * NOT FOUND @@ -240,13 +220,13 @@ export function Application(): VNode { <Route path={Pages.balance} component={Redirect} - to={Pages.balance_history.replace(":currency?", "")} + to={Pages.balanceHistory({})} /> <Route default component={Redirect} - to={Pages.balance_history.replace(":currency?", "")} + to={Pages.balanceHistory({})} /> </Router> </WalletBox> @@ -268,5 +248,7 @@ function Redirect({ to }: { to: string }): null { } function shouldShowPendingOperations(path: string): boolean { + // FIXME: replace includes with a match API like preact router does + // [Pages.balanceHistory, Pages.dev, Pages.settings, Pages.backup] return ["/balance/history/", "/dev", "/settings", "/backup"].includes(path); } diff --git a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx index 1f23be856..b1c34c213 100644 --- a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx @@ -167,10 +167,9 @@ function BackupLayout(props: TransactionLayoutProps): VNode { <RowBorderGray> <div style={{ color: !props.active ? "grey" : undefined }}> <a - href={Pages.backup_provider_detail.replace( - ":pid", - encodeURIComponent(props.id), - )} + href={Pages.backupProviderDetail({ + pid: encodeURIComponent(props.id), + })} > <span>{props.title}</span> </a> diff --git a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx index 502e9f8ad..d2a9c1366 100644 --- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx +++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx @@ -165,10 +165,7 @@ export function CreateManualWithdraw({ </i18n.Translate> </BoldLight> <LinkPrimary - href={Pages.settings_exchange_add.replace( - ":currency?", - initialCurrency, - )} + href={Pages.settingsExchangeAdd({ currency: initialCurrency })} style={{ marginLeft: "auto" }} > <i18n.Translate>Add Exchange</i18n.Translate> @@ -194,7 +191,7 @@ export function CreateManualWithdraw({ <i18n.Translate>No exchange configured</i18n.Translate> </BoldLight> <LinkPrimary - href={Pages.settings_exchange_add.replace(":currency?", "")} + href={Pages.settingsExchangeAdd({})} style={{ marginLeft: "auto" }} > <i18n.Translate>Add Exchange</i18n.Translate> @@ -242,7 +239,7 @@ export function CreateManualWithdraw({ </Input> <div style={{ display: "flex", justifyContent: "space-between" }}> <LinkPrimary - href={Pages.settings_exchange_add.replace(":currency?", "")} + href={Pages.settingsExchangeAdd({})} style={{ marginLeft: "auto" }} > <i18n.Translate>Add Exchange</i18n.Translate> diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx index e71ea48f0..074b0d356 100644 --- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx @@ -156,9 +156,6 @@ export function View({ [exchange_name: string]: CalculatedCoinfInfo[]; }, ); - function Item({ children }: any) { - return <div>{children}</div>; - } return ( <div> <p> diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.tsx index be75c1eac..55f046259 100644 --- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx @@ -178,9 +178,7 @@ export function SettingsView({ )} <div style={{ display: "flex", justifyContent: "space-between" }}> <div /> - <LinkPrimary - href={Pages.settings_exchange_add.replace(":currency?", "")} - > + <LinkPrimary href={Pages.settingsExchangeAdd({})}> <i18n.Translate>Add an exchange</i18n.Translate> </LinkPrimary> </div> diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index b78039ddd..89f7064c5 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -352,10 +352,9 @@ export function TransactionView({ <td> {<Amount value={r.amountEffective} />}{" "} <a - href={Pages.balance_transaction.replace( - ":tid", - r.transactionId, - )} + href={Pages.balanceTransaction({ + tid: r.transactionId, + })} > was refunded </a>{" "} @@ -556,10 +555,9 @@ export function TransactionView({ title={<i18n.Translate>Original order ID</i18n.Translate>} text={ <a - href={Pages.balance_transaction.replace( - ":tid", - transaction.refundedTransactionId, - )} + href={Pages.balanceTransaction({ + tid: transaction.refundedTransactionId, + })} > {transaction.info.orderId} </a> |