aboutsummaryrefslogtreecommitdiff
path: root/packages/demobank-ui
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-02-04 12:04:27 -0300
committerSebastian <sebasjm@gmail.com>2024-02-05 09:18:22 -0300
commitb71d6f2b11342bd22197289ad3872d8a341686b5 (patch)
treed263482af0121d87000476fd0306530a77580817 /packages/demobank-ui
parent83ff7de59b8a00b313ecb00f4c6150a37c38902f (diff)
downloadwallet-core-b71d6f2b11342bd22197289ad3872d8a341686b5.tar.xz
wip DD39: removed webRequest permission and changes made into demobank
Diffstat (limited to 'packages/demobank-ui')
-rw-r--r--packages/demobank-ui/src/Routing.tsx153
-rw-r--r--packages/demobank-ui/src/components/app.tsx8
-rw-r--r--packages/demobank-ui/src/context/navigation.ts80
-rw-r--r--packages/demobank-ui/src/context/wallet-integration.ts90
-rw-r--r--packages/demobank-ui/src/index.html42
-rw-r--r--packages/demobank-ui/src/pages/OperationState/views.tsx16
-rw-r--r--packages/demobank-ui/src/pages/PaymentOptions.tsx49
-rw-r--r--packages/demobank-ui/src/pages/ProfileNavigation.tsx10
-rw-r--r--packages/demobank-ui/src/pages/QrCodeSection.tsx19
-rw-r--r--packages/demobank-ui/src/pages/WalletWithdrawForm.tsx31
-rw-r--r--packages/demobank-ui/src/pages/WithdrawalQRCode.tsx46
-rw-r--r--packages/demobank-ui/src/pages/business/CreateCashout.tsx2
-rw-r--r--packages/demobank-ui/src/route.ts124
13 files changed, 396 insertions, 274 deletions
diff --git a/packages/demobank-ui/src/Routing.tsx b/packages/demobank-ui/src/Routing.tsx
index e73493d60..442a276a0 100644
--- a/packages/demobank-ui/src/Routing.tsx
+++ b/packages/demobank-ui/src/Routing.tsx
@@ -48,6 +48,8 @@ import { RemoveAccount } from "./pages/admin/RemoveAccount.js";
import { CreateCashout } from "./pages/business/CreateCashout.js";
import { ShowCashoutDetails } from "./pages/business/ShowCashoutDetails.js";
import { RouteParamsType, urlPattern, useCurrentLocation } from "./route.js";
+import { useNavigationContext } from "./context/navigation.js";
+import { useEffect } from "preact/hooks";
export function Routing(): VNode {
const backend = useBackendState();
@@ -89,12 +91,18 @@ function PublicRounting({
}): VNode {
const settings = useSettingsContext();
const { i18n } = useTranslationContext();
- const [loc, routeTo] = useCurrentLocation(publicPages);
+ const location = useCurrentLocation(publicPages);
+ const { navigateTo } = useNavigationContext()
const { api } = useBankCoreApiContext();
const [notification, notify, handleError] = useLocalNotification();
- if (loc === undefined) {
- routeTo("login", {});
+ useEffect(() => {
+ if (location === undefined) {
+ navigateTo(privatePages.home.url({}))
+ }
+ }, [location])
+
+ if (location === undefined) {
return <Fragment />;
}
@@ -132,7 +140,7 @@ function PublicRounting({
});
}
- switch (loc.name) {
+ switch (location.name) {
case "login": {
return (
<Fragment>
@@ -148,17 +156,17 @@ function PublicRounting({
return <PublicHistoriesPage />;
}
case "operationDetails": {
- const { wopid } = loc.values as RouteParamsType<
- typeof loc.parent,
- typeof loc.name
+ const { wopid } = location.values as RouteParamsType<
+ typeof location.parent,
+ typeof location.name
>;
return (
<WithdrawalOperationPage
operationId={wopid}
- onOperationAborted={() => routeTo("login", {})}
+ onOperationAborted={() => navigateTo(publicPages.login.url({}))}
routeClose={publicPages.login}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+ onAuthorizationRequired={() => navigateTo(publicPages.solveSecondFactor.url({}))}
/>
);
}
@@ -176,18 +184,17 @@ function PublicRounting({
case "solveSecondFactor": {
return (
<SolveChallengePage
- onChallengeCompleted={() => routeTo("login", {})}
+ onChallengeCompleted={() => navigateTo(publicPages.login.url({}))}
routeClose={publicPages.login}
/>
);
}
default:
- assertUnreachable(loc.name);
+ assertUnreachable(location.name);
}
}
export const privatePages = {
- home: urlPattern(/\/account/, () => "#/account"),
homeChargeWallet: urlPattern(
/\/account\/charge-wallet/,
() => "#/account/charge-wallet",
@@ -196,6 +203,7 @@ export const privatePages = {
/\/account\/wire-transfer/,
() => "#/account/wire-transfer",
),
+ home: urlPattern(/\/account/, () => "#/account"),
solveSecondFactor: urlPattern(/\/2fa/, () => "#/2fa"),
cashoutCreate: urlPattern(/\/new-cashout/, () => "#/new-cashout"),
cashoutDetails: urlPattern<{ cid: string }>(
@@ -233,7 +241,7 @@ export const privatePages = {
({ account }) => `#/profile/${account}/cashouts`,
),
operationDetails: urlPattern<{ wopid: string }>(
- /\/operation\/(?<wopid>[a-zA-Z0-9]+)/,
+ /\/operation\/(?<wopid>[a-zA-Z0-9-]+)/,
({ wopid }) => `#/operation/${wopid}`,
),
};
@@ -245,33 +253,38 @@ function PrivateRouting({
username: string;
isAdmin: boolean;
}): VNode {
- const [loc, routeTo] = useCurrentLocation(privatePages);
+ const { navigateTo } = useNavigationContext()
+ const location = useCurrentLocation(privatePages);
+ useEffect(() => {
+ if (location === undefined) {
+ navigateTo(privatePages.home.url({}))
+ }
+ }, [location])
- if (loc === undefined) {
- routeTo("home", {});
+ if (location === undefined) {
return <Fragment />;
}
- switch (loc.name) {
+ switch (location.name) {
case "operationDetails": {
- const { wopid } = loc.values as RouteParamsType<
- typeof loc.parent,
- typeof loc.name
+ const { wopid } = location.values as RouteParamsType<
+ typeof location.parent,
+ typeof location.name
>;
return (
<WithdrawalOperationPage
operationId={wopid}
- onOperationAborted={() => routeTo("home", {})}
+ onOperationAborted={() => navigateTo(privatePages.home.url({}))}
routeClose={privatePages.home}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
/>
);
}
case "solveSecondFactor": {
return (
<SolveChallengePage
- onChallengeCompleted={() => routeTo("home", {})}
+ onChallengeCompleted={() => navigateTo(privatePages.home.url({}))}
routeClose={privatePages.home}
/>
);
@@ -286,64 +299,64 @@ function PrivateRouting({
return (
<CreateNewAccount
routeCancel={privatePages.home}
- onCreateSuccess={() => routeTo("home", {})}
+ onCreateSuccess={() => navigateTo(privatePages.home.url({}))}
/>
);
}
case "accountDetails": {
- const { account } = loc.values as RouteParamsType<
- typeof loc.parent,
- typeof loc.name
+ const { account } = location.values as RouteParamsType<
+ typeof location.parent,
+ typeof location.name
>;
return (
<ShowAccountDetails
account={account}
- onUpdateSuccess={() => routeTo("home", {})}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+ onUpdateSuccess={() => navigateTo(privatePages.home.url({}))}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
routeClose={privatePages.home}
/>
);
}
case "accountChangePassword": {
- const { account } = loc.values as RouteParamsType<
- typeof loc.parent,
- typeof loc.name
+ const { account } = location.values as RouteParamsType<
+ typeof location.parent,
+ typeof location.name
>;
return (
<UpdateAccountPassword
focus
account={account}
- onUpdateSuccess={() => routeTo("home", {})}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+ onUpdateSuccess={() => navigateTo(privatePages.home.url({}))}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
routeClose={privatePages.home}
/>
);
}
case "accountDelete": {
- const { account } = loc.values as RouteParamsType<
- typeof loc.parent,
- typeof loc.name
+ const { account } = location.values as RouteParamsType<
+ typeof location.parent,
+ typeof location.name
>;
return (
<RemoveAccount
account={account}
- onUpdateSuccess={() => routeTo("home", {})}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+ onUpdateSuccess={() => navigateTo(privatePages.home.url({}))}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
routeCancel={privatePages.home}
/>
);
}
case "accountCashouts": {
- const { account } = loc.values as RouteParamsType<
- typeof loc.parent,
- typeof loc.name
+ const { account } = location.values as RouteParamsType<
+ typeof location.parent,
+ typeof location.name
>;
return (
<CashoutListForAccount
account={account}
routeCashoutDetails={privatePages.cashoutDetails}
routeClose={privatePages.home}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
/>
);
}
@@ -351,8 +364,8 @@ function PrivateRouting({
return (
<RemoveAccount
account={username}
- onUpdateSuccess={() => routeTo("home", {})}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+ onUpdateSuccess={() => navigateTo(privatePages.home.url({}))}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
routeCancel={privatePages.home}
/>
);
@@ -361,8 +374,8 @@ function PrivateRouting({
return (
<ShowAccountDetails
account={username}
- onUpdateSuccess={() => routeTo("home", {})}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+ onUpdateSuccess={() => navigateTo(privatePages.home.url({}))}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
routeClose={privatePages.home}
/>
);
@@ -372,8 +385,8 @@ function PrivateRouting({
<UpdateAccountPassword
focus
account={username}
- onUpdateSuccess={() => routeTo("home", {})}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+ onUpdateSuccess={() => navigateTo(privatePages.home.url({}))}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
routeClose={privatePages.home}
/>
);
@@ -383,7 +396,7 @@ function PrivateRouting({
<CashoutListForAccount
account={username}
routeCashoutDetails={privatePages.cashoutDetails}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
routeClose={privatePages.home}
/>
);
@@ -392,7 +405,7 @@ function PrivateRouting({
if (isAdmin) {
return (
<AdminHome
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
routeCreate={privatePages.accountCreate}
routeRemoveAccount={privatePages.accountDelete}
routeShowAccount={privatePages.accountDetails}
@@ -408,9 +421,9 @@ function PrivateRouting({
routeChargeWallet={privatePages.homeChargeWallet}
routeWireTransfer={privatePages.homeWireTransfer}
routeClose={privatePages.home}
- onClose={() => routeTo("home", {})}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
- onOperationCreated={(wopid) => routeTo("operationDetails", { wopid })}
+ onClose={() => navigateTo(privatePages.home.url({}))}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
+ onOperationCreated={(wopid) => navigateTo(privatePages.operationDetails.url({ wopid }))}
/>
);
}
@@ -418,15 +431,15 @@ function PrivateRouting({
return (
<CreateCashout
account={username}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
routeClose={privatePages.home}
/>
);
}
case "cashoutDetails": {
- const { cid } = loc.values as RouteParamsType<
- typeof loc.parent,
- typeof loc.name
+ const { cid } = location.values as RouteParamsType<
+ typeof location.parent,
+ typeof location.name
>;
return (
<ShowCashoutDetails
@@ -436,16 +449,16 @@ function PrivateRouting({
);
}
case "wireTranserCreate": {
- const { destination } = loc.values as RouteParamsType<
- typeof loc.parent,
- typeof loc.name
+ const { destination } = location.values as RouteParamsType<
+ typeof location.parent,
+ typeof location.name
>;
return (
<WireTransfer
toAccount={destination}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
routeCancel={privatePages.home}
- onSuccess={() => routeTo("home", {})}
+ onSuccess={() => navigateTo(privatePages.home.url({}))}
/>
);
}
@@ -457,9 +470,9 @@ function PrivateRouting({
routeChargeWallet={privatePages.homeChargeWallet}
routeWireTransfer={privatePages.homeWireTransfer}
routeClose={privatePages.home}
- onClose={() => routeTo("home", {})}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
- onOperationCreated={(wopid) => routeTo("operationDetails", { wopid })}
+ onClose={() => navigateTo(privatePages.home.url({}))}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
+ onOperationCreated={(wopid) => navigateTo(privatePages.operationDetails.url({ wopid }))}
/>
);
}
@@ -471,13 +484,13 @@ function PrivateRouting({
routeChargeWallet={privatePages.homeChargeWallet}
routeWireTransfer={privatePages.homeWireTransfer}
routeClose={privatePages.home}
- onClose={() => routeTo("home", {})}
- onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
- onOperationCreated={(wopid) => routeTo("operationDetails", { wopid })}
+ onClose={() => navigateTo(privatePages.home.url({}))}
+ onAuthorizationRequired={() => navigateTo(privatePages.solveSecondFactor.url({}))}
+ onOperationCreated={(wopid) => navigateTo(privatePages.operationDetails.url({ wopid }))}
/>
);
}
default:
- assertUnreachable(loc.name);
+ assertUnreachable(location.name);
}
}
diff --git a/packages/demobank-ui/src/components/app.tsx b/packages/demobank-ui/src/components/app.tsx
index 31013388b..97778e6d7 100644
--- a/packages/demobank-ui/src/components/app.tsx
+++ b/packages/demobank-ui/src/components/app.tsx
@@ -30,6 +30,8 @@ import { SettingsProvider } from "../context/settings.js";
import { strings } from "../i18n/strings.js";
import { BankFrame } from "../pages/BankFrame.js";
import { BankUiSettings, fetchSettings } from "../settings.js";
+import { TalerWalletIntegrationBrowserProvider } from "../context/wallet-integration.js";
+import { BrowserHashNavigationProvider } from "../context/navigation.js";
const WITH_LOCAL_STORAGE_CACHE = false;
const App: FunctionalComponent = () => {
@@ -78,7 +80,11 @@ const App: FunctionalComponent = () => {
keepPreviousData: true,
}}
>
- <Routing />
+ <TalerWalletIntegrationBrowserProvider>
+ <BrowserHashNavigationProvider>
+ <Routing />
+ </BrowserHashNavigationProvider>
+ </TalerWalletIntegrationBrowserProvider>
</SWRConfig>
</BankCoreApiProvider>
</BackendStateProvider>
diff --git a/packages/demobank-ui/src/context/navigation.ts b/packages/demobank-ui/src/context/navigation.ts
new file mode 100644
index 000000000..fc1460c02
--- /dev/null
+++ b/packages/demobank-ui/src/context/navigation.ts
@@ -0,0 +1,80 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022-2024 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 { ComponentChildren, createContext, h, VNode } from "preact";
+import { useContext, useEffect, useState } from "preact/hooks";
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+export type Type = {
+ path: string;
+ params: Record<string, string>;
+ navigateTo: (path: string) => void;
+ // addNavigationListener: (listener: (path: string, params: Record<string, string>) => void) => (() => void);
+};
+
+// @ts-expect-error shold not be used without provider
+const Context = createContext<Type>(undefined);
+
+export const useNavigationContext = (): Type => useContext(Context);
+
+function getPathAndParamsFromWindow() {
+ const path = typeof window !== "undefined" ? window.location.hash.substring(1) : "/";
+ const params: Record<string, string> = {}
+ if (typeof window !== "undefined") {
+ for (const [key, value] of new URLSearchParams(window.location.search)) {
+ params[key] = value;
+ }
+ }
+ return { path, params }
+}
+
+const { path: initialPath, params: initialParams } = getPathAndParamsFromWindow()
+
+// there is a posibility that if the browser does a redirection
+// (which doesn't go through navigatTo function) and that exectued
+// too early (before addEventListener runs) it won't be taking
+// into account
+const PopStateEventType = "popstate";
+
+export const BrowserHashNavigationProvider = ({ children }: { children: ComponentChildren }): VNode => {
+ const [{ path, params }, setState] = useState({ path: initialPath, params: initialParams })
+ if (typeof window === "undefined") {
+ throw Error("Can't use BrowserHashNavigationProvider if there is no window object")
+ }
+ function navigateTo(path: string) {
+ const { params } = getPathAndParamsFromWindow()
+ setState({ path, params })
+ window.location.href = path
+ }
+
+ useEffect(() => {
+ function eventListener() {
+ setState(getPathAndParamsFromWindow())
+ }
+ window.addEventListener(PopStateEventType, eventListener);
+ return () => {
+ window.removeEventListener(PopStateEventType, eventListener)
+ }
+ }, [])
+ return h(Context.Provider, {
+ value: { path, params, navigateTo },
+ children,
+ });
+};
diff --git a/packages/demobank-ui/src/context/wallet-integration.ts b/packages/demobank-ui/src/context/wallet-integration.ts
new file mode 100644
index 000000000..47bdc90ec
--- /dev/null
+++ b/packages/demobank-ui/src/context/wallet-integration.ts
@@ -0,0 +1,90 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022-2024 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 {
+ stringifyTalerUri,
+ TalerUri
+} from "@gnu-taler/taler-util";
+import {
+ ComponentChildren,
+ createContext,
+ h,
+ VNode
+} from "preact";
+import { useContext } from "preact/hooks";
+
+/**
+ * https://docs.taler.net/design-documents/039-taler-browser-integration.html
+ *
+ * @param uri
+ */
+function createHeadMetaTag(uri: TalerUri, onNotFound?: () => void) {
+
+ const meta = document.createElement("meta");
+ meta.setAttribute("name", "taler-uri");
+ meta.setAttribute("content", stringifyTalerUri(uri));
+
+ document.head.appendChild(meta);
+
+ let walletFound = false
+ window.addEventListener("beforeunload", () => {
+ walletFound = true
+ })
+ setTimeout(() => {
+ if (!walletFound && onNotFound) {
+ onNotFound()
+ }
+ }, 10)//very short timeout
+}
+interface Type {
+ /**
+ * Tell the active wallet that an action is found
+ *
+ * @param uri
+ * @returns
+ */
+ publishTalerAction: (uri: TalerUri, onNotFound?: () => void) => void;
+}
+
+// @ts-expect-error default value to undefined, should it be another thing?
+const Context = createContext<Type>(undefined);
+
+export const useTalerWalletIntegrationAPI = (): Type => useContext(Context);
+
+export const TalerWalletIntegrationBrowserProvider = ({ children }: { children: ComponentChildren }): VNode => {
+ const value: Type = {
+ publishTalerAction: createHeadMetaTag
+ };
+ return h(Context.Provider, {
+ value,
+ children,
+ });
+};
+
+
+export const TalerWalletIntegrationTestingProvider = ({
+ children,
+ value,
+}: {
+ children: ComponentChildren;
+ value: Type;
+}): VNode => {
+
+ return h(Context.Provider, {
+ value,
+ children,
+ });
+};
diff --git a/packages/demobank-ui/src/index.html b/packages/demobank-ui/src/index.html
index 720b678a3..6e0638e3f 100644
--- a/packages/demobank-ui/src/index.html
+++ b/packages/demobank-ui/src/index.html
@@ -17,25 +17,25 @@
-->
<!doctype html>
<html lang="en" class="h-full bg-gray-100">
- <head>
- <meta http-equiv="content-type" content="text/html; charset=utf-8" />
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width,initial-scale=1" />
- <meta name="taler-support" content="uri" />
- <meta name="mobile-web-app-capable" content="yes" />
- <meta name="apple-mobile-web-app-capable" content="yes" />
- <link
- rel="icon"
- href="data:;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///////////////////////////////////////////////////////////////////////////////////////////////////7//v38//78/P/+/fz//vz7///+/v/+/f3//vz7///+/v/+/fz//v38///////////////////////+/v3///7+/////////////////////////////////////////////////////////v3//v79///////+/v3///////r28v/ct5//06SG/9Gffv/Xqo7/7N/V/9e2nf/bsJb/6uDW/9Sskf/euKH/+/j2///////+/v3//////+3azv+/eE3/2rWd/9Kkhv/Vr5T/48i2/8J+VP/Qn3//3ryn/795Tf/WrpP/2LCW/8B6T//w4Nb///////Pn4P+/d0v/9u3n/+7d0v/EhV7//v///+HDr//fxLD/zph2/+TJt//8/Pv/woBX//Lm3f/y5dz/v3hN//bu6f/JjGn/4sW0///////Df1j/8OLZ//v6+P+/elH/+vj1//jy7f+/elL//////+zYzP/Eg13//////967p//MlHT/wn5X///////v4Nb/yY1s///////jw7H/06KG////////////z5t9/+fNvf//////x4pn//Pp4v/8+vn/w39X/8WEX///////5s/A/9CbfP//////27Oc/9y2n////////////9itlf/gu6f//////86Vdf/r2Mz//////8SCXP/Df1j//////+7d0v/KkG7//////+HBrf/VpYr////////////RnoH/5sq6///////Ii2n/8ubf//39/P/Cf1j/xohk/+bNvv//////wn5W//Tq4//58/D/wHxV//7+/f/59fH/v3xU//39/P/w4Nf/xIFb///////hw7H/yo9t/+/f1f/AeU3/+/n2/+nSxP/FhmD//////9qzm//Upon/4MSx/96+qf//////xINc/+3bz//48e3/v3hN//Pn3///////6M+//752S//gw6//06aK/8J+VP/kzLr/zZd1/8OCWv/q18r/17KZ/9Ooi//fv6r/v3dK/+vWyP///////v39///////27un/1aeK/9Opjv/m1cf/1KCC/9a0nP/n08T/0Jx8/82YdP/QnHz/16yR//jx7P///////v39///////+/f3///7+///////+//7//v7+///////+/v7//v/+/////////////////////////v7//v79///////////////////+/v/+/Pv//v39///+/v/+/Pv///7+//7+/f/+/Pv//v39//79/P/+/Pv///7+////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
- />
- <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
- <title>Bank</title>
- <!-- Entry point for the bank SPA. -->
- <script type="module" src="index.js"></script>
- <link rel="stylesheet" href="index.css" />
- </head>
- <body class="h-full">
- <div id="app"></div>
- </body>
-</html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
+ <meta name="taler-support" content="uri,api" />
+ <meta name="mobile-web-app-capable" content="yes" />
+ <meta name="apple-mobile-web-app-capable" content="yes" />
+ <link rel="icon"
+ href="data:;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///////////////////////////////////////////////////////////////////////////////////////////////////7//v38//78/P/+/fz//vz7///+/v/+/f3//vz7///+/v/+/fz//v38///////////////////////+/v3///7+/////////////////////////////////////////////////////////v3//v79///////+/v3///////r28v/ct5//06SG/9Gffv/Xqo7/7N/V/9e2nf/bsJb/6uDW/9Sskf/euKH/+/j2///////+/v3//////+3azv+/eE3/2rWd/9Kkhv/Vr5T/48i2/8J+VP/Qn3//3ryn/795Tf/WrpP/2LCW/8B6T//w4Nb///////Pn4P+/d0v/9u3n/+7d0v/EhV7//v///+HDr//fxLD/zph2/+TJt//8/Pv/woBX//Lm3f/y5dz/v3hN//bu6f/JjGn/4sW0///////Df1j/8OLZ//v6+P+/elH/+vj1//jy7f+/elL//////+zYzP/Eg13//////967p//MlHT/wn5X///////v4Nb/yY1s///////jw7H/06KG////////////z5t9/+fNvf//////x4pn//Pp4v/8+vn/w39X/8WEX///////5s/A/9CbfP//////27Oc/9y2n////////////9itlf/gu6f//////86Vdf/r2Mz//////8SCXP/Df1j//////+7d0v/KkG7//////+HBrf/VpYr////////////RnoH/5sq6///////Ii2n/8ubf//39/P/Cf1j/xohk/+bNvv//////wn5W//Tq4//58/D/wHxV//7+/f/59fH/v3xU//39/P/w4Nf/xIFb///////hw7H/yo9t/+/f1f/AeU3/+/n2/+nSxP/FhmD//////9qzm//Upon/4MSx/96+qf//////xINc/+3bz//48e3/v3hN//Pn3///////6M+//752S//gw6//06aK/8J+VP/kzLr/zZd1/8OCWv/q18r/17KZ/9Ooi//fv6r/v3dK/+vWyP///////v39///////27un/1aeK/9Opjv/m1cf/1KCC/9a0nP/n08T/0Jx8/82YdP/QnHz/16yR//jx7P///////v39///////+/f3///7+///////+//7//v7+///////+/v7//v/+/////////////////////////v7//v79///////////////////+/v/+/Pv//v39///+/v/+/Pv///7+//7+/f/+/Pv//v39//79/P/+/Pv///7+////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" />
+ <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
+ <title>Bank</title>
+ <!-- Entry point for the bank SPA. -->
+ <script type="module" src="index.js"></script>
+ <link rel="stylesheet" href="index.css" />
+</head>
+
+<body class="h-full">
+ <div id="app"></div>
+</body>
+
+</html> \ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx b/packages/demobank-ui/src/pages/OperationState/views.tsx
index ac3724eb8..4d193505e 100644
--- a/packages/demobank-ui/src/pages/OperationState/views.tsx
+++ b/packages/demobank-ui/src/pages/OperationState/views.tsx
@@ -36,6 +36,7 @@ import { useBankState } from "../../hooks/bank-state.js";
import { usePreferences } from "../../hooks/preferences.js";
import { ShouldBeSameUser } from "../WithdrawalConfirmationQuestion.js";
import { State } from "./index.js";
+import { useTalerWalletIntegrationAPI } from "../../context/wallet-integration.js";
export function InvalidPaytoView({ payto }: State.InvalidPayto) {
return <div>Payto from server is not valid &quot;{payto}&quot;</div>;
@@ -328,23 +329,12 @@ export function ReadyView({
onAbort: doAbort,
}: State.Ready): VNode<Record<string, never>> {
const { i18n } = useTranslationContext();
+ const walletInegrationApi = useTalerWalletIntegrationAPI()
const [notification, notify, errorHandler] = useLocalNotification();
const talerWithdrawUri = stringifyWithdrawUri(uri);
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.
- // WebExtension will be using
- // https://developer.chrome.com/docs/extensions/reference/tabs/#event-onUpdated
- document.title = `${document.title} ${uri.withdrawalOperationId}`;
- const meta = document.createElement("meta");
- meta.setAttribute("name", "taler-uri");
- meta.setAttribute("content", talerWithdrawUri);
- document.head.insertBefore(
- meta,
- document.head.children.length ? document.head.children[0] : null,
- );
+ walletInegrationApi.publishTalerAction(uri)
}, []);
async function onAbort() {
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index 53086d4cc..51a6a17a9 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -14,13 +14,43 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { AmountJson } from "@gnu-taler/taler-util";
-import { VNode, h } from "preact";
+import { AmountJson, TalerError } from "@gnu-taler/taler-util";
+import { Fragment, VNode, h } from "preact";
import { useBankState } from "../hooks/bank-state.js";
import { PaytoWireTransferForm } from "./PaytoWireTransferForm.js";
import { WalletWithdrawForm } from "./WalletWithdrawForm.js";
import { RouteDefinition } from "../route.js";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { useWithdrawalDetails } from "../hooks/access.js";
+import { useEffect } from "preact/hooks";
+
+function ShowOperationPendingTag({ woid, onOperationAlreadyCompleted }: { woid: string, onOperationAlreadyCompleted?: () => void }): VNode {
+ const { i18n } = useTranslationContext();
+ const result = useWithdrawalDetails(woid);
+ const error = !result || result instanceof TalerError || result.type === "fail"
+ const completed = !error && (result.body.status === "aborted" || result.body.status === "confirmed")
+ useEffect(() => {
+ if (completed && onOperationAlreadyCompleted) {
+ onOperationAlreadyCompleted()
+ }
+ }, [completed])
+
+ if (error || completed) {
+ return <Fragment />;
+ }
+
+ return <span class="flex items-center gap-x-1.5 w-fit rounded-md bg-green-100 px-2 py-1 text-xs font-medium text-green-700 whitespace-pre">
+ <svg
+ class="h-1.5 w-1.5 fill-green-500"
+ viewBox="0 0 6 6"
+ aria-hidden="true"
+ >
+ <circle cx="3" cy="3" r="3" />
+ </svg>
+ <i18n.Translate>operation ready</i18n.Translate>
+ </span>
+
+}
/**
* Let the user choose a payment option,
@@ -46,7 +76,7 @@ export function PaymentOptions({
routeWireTransfer: RouteDefinition<Record<string, never>>;
}): VNode {
const { i18n } = useTranslationContext();
- const [bankState] = useBankState();
+ const [bankState, updateBankState] = useBankState();
return (
<div class="mt-4">
@@ -98,16 +128,9 @@ export function PaymentOptions({
</i18n.Translate>
</div>
{!!bankState.currentWithdrawalOperationId && (
- <span class="flex items-center gap-x-1.5 w-fit rounded-md bg-green-100 px-2 py-1 text-xs font-medium text-green-700 whitespace-pre">
- <svg
- class="h-1.5 w-1.5 fill-green-500"
- viewBox="0 0 6 6"
- aria-hidden="true"
- >
- <circle cx="3" cy="3" r="3" />
- </svg>
- <i18n.Translate>operation ready</i18n.Translate>
- </span>
+ <ShowOperationPendingTag woid={bankState.currentWithdrawalOperationId} onOperationAlreadyCompleted={() => {
+ updateBankState("currentWithdrawalOperationId", undefined)
+ }} />
)}
</div>
</label>
diff --git a/packages/demobank-ui/src/pages/ProfileNavigation.tsx b/packages/demobank-ui/src/pages/ProfileNavigation.tsx
index a6615d578..02f30d8e8 100644
--- a/packages/demobank-ui/src/pages/ProfileNavigation.tsx
+++ b/packages/demobank-ui/src/pages/ProfileNavigation.tsx
@@ -19,6 +19,7 @@ import { privatePages } from "../Routing.js";
import { useBankCoreApiContext } from "../context/config.js";
import { useBackendState } from "../hooks/backend.js";
import { assertUnreachable } from "@gnu-taler/taler-util";
+import { useNavigationContext } from "../context/navigation.js";
export function ProfileNavigation({
current,
@@ -32,6 +33,7 @@ export function ProfileNavigation({
credentials.status !== "loggedIn"
? false
: !credentials.isUserAdministrator;
+ const { navigateTo } = useNavigationContext()
return (
<div>
<div class="sm:hidden">
@@ -46,19 +48,19 @@ export function ProfileNavigation({
const op = e.currentTarget.value as typeof current;
switch (op) {
case "details": {
- window.location.href = privatePages.myAccountDetails.url({});
+ navigateTo(privatePages.myAccountDetails.url({}));
return;
}
case "delete": {
- window.location.href = privatePages.myAccountDelete.url({});
+ navigateTo(privatePages.myAccountDelete.url({}));
return;
}
case "credentials": {
- window.location.href = privatePages.myAccountPassword.url({});
+ navigateTo(privatePages.myAccountPassword.url({}));
return;
}
case "cashouts": {
- window.location.href = privatePages.myAccountCashouts.url({});
+ navigateTo(privatePages.myAccountCashouts.url({}));
return;
}
default:
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index f21134aa1..037849804 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -19,7 +19,7 @@ import {
HttpStatusCode,
stringifyWithdrawUri,
TranslatedString,
- WithdrawUriResult,
+ WithdrawUriResult
} from "@gnu-taler/taler-util";
import {
LocalNotificationBanner,
@@ -30,6 +30,7 @@ import { Fragment, h, VNode } from "preact";
import { useEffect } from "preact/hooks";
import { QR } from "../components/QR.js";
import { useBankCoreApiContext } from "../context/config.js";
+import { useTalerWalletIntegrationAPI } from "../context/wallet-integration.js";
import { useBackendState } from "../hooks/backend.js";
export function QrCodeSection({
@@ -40,25 +41,15 @@ export function QrCodeSection({
onAborted: () => void;
}): VNode {
const { i18n } = useTranslationContext();
+ const walletInegrationApi = useTalerWalletIntegrationAPI()
const talerWithdrawUri = stringifyWithdrawUri(withdrawUri);
const { state: credentials } = useBackendState();
const creds = credentials.status !== "loggedIn" ? undefined : credentials;
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.
- // WebExtension will be using
- // https://developer.chrome.com/docs/extensions/reference/tabs/#event-onUpdated
- document.title = `${document.title} ${withdrawUri.withdrawalOperationId}`;
- const meta = document.createElement("meta");
- meta.setAttribute("name", "taler-uri");
- meta.setAttribute("content", talerWithdrawUri);
- document.head.insertBefore(
- meta,
- document.head.children.length ? document.head.children[0] : null,
- );
+ walletInegrationApi.publishTalerAction(withdrawUri)
}, []);
+
const [notification, notify, handleError] = useLocalNotification();
const { api } = useBankCoreApiContext();
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index 1e48b818a..9f7f46c4f 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -21,6 +21,7 @@ import {
TranslatedString,
assertUnreachable,
parseWithdrawUri,
+ stringifyWithdrawUri,
} from "@gnu-taler/taler-util";
import {
Attention,
@@ -41,6 +42,8 @@ import { RouteDefinition } from "../route.js";
import { undefinedIfEmpty } from "../utils.js";
import { OperationState } from "./OperationState/index.js";
import { InputAmount, doAutoFocus } from "./PaytoWireTransferForm.js";
+import { useTalerWalletIntegrationAPI } from "../context/wallet-integration.js";
+import { useNavigationContext } from "../context/navigation.js";
const RefAmount = forwardRef(InputAmount);
@@ -57,18 +60,32 @@ function OldWithdrawalForm({
}): VNode {
const { i18n } = useTranslationContext();
const [settings] = usePreferences();
+
+ // const walletInegrationApi = useTalerWalletIntegrationAPI()
+ // const { navigateTo } = useNavigationContext();
+
const [bankState, updateBankState] = useBankState();
+ const { api } = useBankCoreApiContext();
const { state: credentials } = useBackendState();
const creds = credentials.status !== "loggedIn" ? undefined : credentials;
- const { api } = useBankCoreApiContext();
const [amountStr, setAmountStr] = useState<string | undefined>(
`${settings.maxWithdrawalAmount}`,
);
const [notification, notify, handleError] = useLocalNotification();
if (bankState.currentWithdrawalOperationId) {
+ // FIXME: doing the preventDefault is not optimal
+
+ // const suri = stringifyWithdrawUri({
+ // bankIntegrationApiBaseUrl: api.getIntegrationAPI().baseUrl,
+ // withdrawalOperationId: bankState.currentWithdrawalOperationId,
+ // });
+ // const uri = parseWithdrawUri(suri)!
+ const url = privatePages.operationDetails.url({
+ wopid: bankState.currentWithdrawalOperationId,
+ })
return (
<Attention type="warning" title={i18n.str`There is an operation already`}>
<span ref={focus ? doAutoFocus : undefined} />
@@ -77,9 +94,13 @@ function OldWithdrawalForm({
</i18n.Translate>{" "}
<a
class="font-semibold text-yellow-700 hover:text-yellow-600"
- href={privatePages.operationDetails.url({
- wopid: bankState.currentWithdrawalOperationId,
- })}
+ href={url}
+ // onClick={(e) => {
+ // e.preventDefault()
+ // walletInegrationApi.publishTalerAction(uri, () => {
+ // navigateTo(url)
+ // })
+ // }}
>
<i18n.Translate>this page</i18n.Translate>
</a>
@@ -324,7 +345,7 @@ export function WalletWithdrawForm({
onAuthorizationRequired={onAuthorizationRequired}
routeClose={routeCancel}
onAbort={onOperationAborted}
- // route={routeCancel}
+ // route={routeCancel}
/>
)}
</div>
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index 3cf552f39..03f6556af 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -75,29 +75,39 @@ export function WithdrawalQRCode({
if (data.status === "aborted") {
return (
- <section id="main" class="content">
- <h1 class="nav">{i18n.str`Operation aborted`}</h1>
- <section>
- <p>
- <i18n.Translate>
- The wire transfer to the Taler Exchange operator's account was
- aborted, your balance was not affected.
- </i18n.Translate>
- </p>
- <p>
- <i18n.Translate>
- You can close this page now or continue to the account page.
- </i18n.Translate>
- </p>
+ <div class="relative ml-auto mr-auto transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6">
+ <div>
+ <div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-yellow-100">
+ <svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+ <path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
+ </svg>
+ </div>
+ <div class="mt-3 text-center sm:mt-5">
+ <h3
+ class="text-base font-semibold leading-6 text-gray-900"
+ id="modal-title"
+ >
+ <i18n.Translate>Operation aborted</i18n.Translate>
+ </h3>
+ <div class="mt-2">
+ <p class="text-sm text-gray-500">
+ <i18n.Translate>
+ The wire transfer to the Taler Exchange operator's account was
+ aborted from somewhere else, your balance was not affected.
+ </i18n.Translate>
+ </p>
+ </div>
+ </div>
+ </div>
+ <div class="mt-5 sm:mt-6">
<a
href={routeClose.url({})}
- class="pure-button pure-button-primary"
- style={{ float: "right" }}
+ class="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
<i18n.Translate>Continue</i18n.Translate>
</a>
- </section>
- </section>
+ </div>
+ </div>
);
}
diff --git a/packages/demobank-ui/src/pages/business/CreateCashout.tsx b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
index 75e0a9b84..46da5c847 100644
--- a/packages/demobank-ui/src/pages/business/CreateCashout.tsx
+++ b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
@@ -316,7 +316,7 @@ export function CreateCashout({
const cashoutDisabled =
config.supported_tan_channels.length < 1 ||
!resultAccount.body.cashout_payto_uri;
- console.log("disab", cashoutDisabled);
+
const cashoutAccount = !resultAccount.body.cashout_payto_uri
? undefined
: parsePaytoUri(resultAccount.body.cashout_payto_uri);
diff --git a/packages/demobank-ui/src/route.ts b/packages/demobank-ui/src/route.ts
index 72b405791..912ba274d 100644
--- a/packages/demobank-ui/src/route.ts
+++ b/packages/demobank-ui/src/route.ts
@@ -13,7 +13,7 @@
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 { useEffect, useState } from "preact/hooks";
+import { useNavigationContext } from "./context/navigation.js";
export function urlPattern<
T extends Record<string, string> = Record<string, never>,
@@ -24,29 +24,6 @@ export function urlPattern<
};
}
-// export function Router({
-// pageList,
-// onNotFound,
-// }: {
-// pageList: Array<PageEntry<unknown>>;
-// onNotFound: () => VNode;
-// }): VNode {
-// const current = useCurrentLocation<unknown>(pageList);
-// if (current !== undefined) {
-// const d = current.page.url
-// if (typeof current.page.url === "string") {
-// const p = current.page.url
-// return create(current.page.view, {});
-// }
-// const p = current.page.url
-// return create(current.page.view, current.values);
-// }
-// return onNotFound();
-// }
-// type PagesMap<T extends object> = {
-// [name in keyof T]: PageDefinition<any>;
-// };
-
export type RouteDefinition<T> = {
pattern: RegExp;
url: (p: T) => string;
@@ -72,17 +49,9 @@ export type RouteParamsType<
type Location<E, T extends RouteMap<E>, NAME extends keyof T> = {
parent: T;
name: NAME;
- // mapped values from params and url
values: RouteParamsType<T, NAME>;
};
-const STARTUP_SPA_LOCATION =
- typeof window !== "undefined" ? window.location.hash.substring(1) : "/";
-const STARTUP_SPA_PARAMS =
- typeof window !== "undefined"
- ? new URLSearchParams(window.location.search)
- : new URLSearchParams();
-
/**
* Search path in the pageList
* get the values from the path found
@@ -91,11 +60,11 @@ const STARTUP_SPA_PARAMS =
* @param path
* @param params
*/
-function doSync<DEF, RM extends RouteMap<DEF>, ROUTES extends keyof RM>(
+function findMatch<DEF, RM extends RouteMap<DEF>, ROUTES extends keyof RM>(
pagesMap: RM,
pageList: Array<ROUTES>,
path: string,
- params: URLSearchParams,
+ params: Record<string, string>,
): Location<DEF, RM, ROUTES> | undefined {
for (let idx = 0; idx < pageList.length; idx++) {
const name = pageList[idx];
@@ -103,9 +72,10 @@ function doSync<DEF, RM extends RouteMap<DEF>, ROUTES extends keyof RM>(
if (found !== null) {
const values =
found.groups === undefined ? {} : structuredClone(found.groups);
- params.forEach((v, k) => {
- values[k] = v;
- });
+
+ Object.entries(params).forEach(([key, value]) => {
+ values[key] = value
+ })
// @ts-expect-error values is a map string which is equivalent to the RouteParamsType
return { name, parent: pagesMap, values };
@@ -114,87 +84,13 @@ function doSync<DEF, RM extends RouteMap<DEF>, ROUTES extends keyof RM>(
return undefined;
}
-const PopStateEventType = "popstate";
-
export function useCurrentLocation<
DEF,
RM extends RouteMap<DEF>,
ROUTES extends keyof RM,
>(pagesMap: RM) {
const pageList = Object.keys(pagesMap) as Array<ROUTES>;
- const [currentLocation, setCurrentLocation] = useState<
- Location<DEF, RM, ROUTES> | undefined
- >(doSync(pagesMap, pageList, STARTUP_SPA_LOCATION, STARTUP_SPA_PARAMS));
- useEffect(() => {
- window.addEventListener(PopStateEventType, () => {
- const path = window.location.hash.substring(1);
- console.log("event", path);
- const l = doSync(
- pagesMap,
- pageList,
- path,
- new URLSearchParams(window.location.search),
- );
- setCurrentLocation(l);
- });
- }, []);
- function routeTo<N extends ROUTES>(
- n: N,
- values: RouteParamsType<RM, N>,
- ): void {
- setCurrentLocation({
- parent: pagesMap,
- name: n,
- values,
- });
- }
- return [currentLocation, routeTo] as const;
-}
-
-// function doestUrlMatchToRoute(
-// url: string,
-// route: string,
-// ): undefined | Record<string, string> {
-// const paramsPattern = /(?:\?([^#]*))?$/;
+ const { path, params } = useNavigationContext()
-// const urlSeg = url.replace(paramsPattern, "").split("/");
-// const routeSeg = route.split("/");
-// let max = Math.max(urlSeg.length, routeSeg.length);
-
-// const result: Record<string, string> = {};
-// for (let i = 0; i < max; i++) {
-// if (routeSeg[i] && routeSeg[i].charAt(0) === ":") {
-// const param = routeSeg[i].replace(/(^:|[+*?]+$)/g, "");
-
-// const flags = (routeSeg[i].match(/[+*?]+$/) || EMPTY)[0] || "";
-// const plus = ~flags.indexOf("+");
-// const star = ~flags.indexOf("*");
-// const val = urlSeg[i] || "";
-
-// if (!val && !star && (flags.indexOf("?") < 0 || plus)) {
-// return undefined;
-// }
-// result[param] = decodeURIComponent(val);
-// if (plus || star) {
-// result[param] = urlSeg.slice(i).map(decodeURIComponent).join("/");
-// break;
-// }
-// } else if (routeSeg[i] !== urlSeg[i]) {
-// return undefined;
-// }
-// }
-
-// const params = url.match(paramsPattern);
-// if (params && params[1]) {
-// const paramList = params[1].split("&");
-// for (let i = 0; i < paramList.length; i++) {
-// const idx = paramList[i].indexOf("=");
-// const name = paramList[i].substring(0, idx);
-// const value = paramList[i].substring(idx + 1);
-// result[decodeURIComponent(name)] = decodeURIComponent(value);
-// }
-// }
-
-// return result;
-// }
-// const EMPTY: Record<string, string> = {};
+ return findMatch(pagesMap, pageList, path, params);
+}