diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle index d15b1b4..fbf30d7 100644 --- a/android/app/capacitor.build.gradle +++ b/android/app/capacitor.build.gradle @@ -10,6 +10,7 @@ android { apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { implementation project(':capacitor-app') + implementation project(':capacitor-browser') implementation project(':capacitor-clipboard') implementation project(':capacitor-filesystem') implementation project(':capacitor-toast') diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 3d84b0e..9ab751f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ - + + + + + + + + + + + + + + + + + - + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 944e099..1181194 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -3,5 +3,5 @@ Mutiny Wallet Mutiny Wallet com.mutinywallet.mutinywallet - com.mutinywallet.mutinywallet + mutiny diff --git a/android/capacitor.settings.gradle b/android/capacitor.settings.gradle index ce6a69a..88aa349 100644 --- a/android/capacitor.settings.gradle +++ b/android/capacitor.settings.gradle @@ -5,6 +5,9 @@ project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capa include ':capacitor-app' project(':capacitor-app').projectDir = new File('../node_modules/.pnpm/@capacitor+app@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/app/android') +include ':capacitor-browser' +project(':capacitor-browser').projectDir = new File('../node_modules/.pnpm/@capacitor+browser@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/browser/android') + include ':capacitor-clipboard' project(':capacitor-clipboard').projectDir = new File('../node_modules/.pnpm/@capacitor+clipboard@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/clipboard/android') diff --git a/package.json b/package.json index 823a4b2..3f84403 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "dependencies": { "@capacitor/android": "^5.2.2", "@capacitor/app": "^5.0.6", + "@capacitor/browser": "^5.0.6", "@capacitor/clipboard": "^5.0.6", "@capacitor/core": "^5.2.2", "@capacitor/filesystem": "^5.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4e5b9be..d3af49b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,9 @@ importers: '@capacitor/app': specifier: ^5.0.6 version: 5.0.6(@capacitor/core@5.2.2) + '@capacitor/browser': + specifier: ^5.0.6 + version: 5.0.6(@capacitor/core@5.2.2) '@capacitor/clipboard': specifier: ^5.0.6 version: 5.0.6(@capacitor/core@5.2.2) @@ -1513,6 +1516,14 @@ packages: - typescript dev: true + /@capacitor/browser@5.0.6(@capacitor/core@5.2.2): + resolution: {integrity: sha512-wEI7Na6PVzSP/00ud7pjbBwXwVG7HywCdy2fJT/hzF6yuHn4tDirbOvbr1JKd9LZqKs2Xn+TapV38JhBRhX6YA==} + peerDependencies: + '@capacitor/core': ^5.0.0 + dependencies: + '@capacitor/core': 5.2.2 + dev: false + /@capacitor/cli@3.9.0: resolution: {integrity: sha512-NkbVZhYb0oPdh/XArE2ZmOwPFJbla5meShGhv3DxKCXeKn1rt92ile+2xOgtB/j+mL7f9cqQzTQM/11sGQzMAg==} engines: {node: '>=12.4.0'} @@ -3895,8 +3906,8 @@ packages: - supports-color dev: true - /@storybook/builder-vite@7.4.0-alpha.0(typescript@4.9.5)(vite@4.4.7): - resolution: {integrity: sha512-YklpIqtFfRLieOtYYbV4bImrbNNEFN8h3dIKM7ekshOkTw5+Ag2xx5zwwxJ1IgHu9C/Tbmgeeo+CO7YaGO56cA==} + /@storybook/builder-vite@7.4.0-alpha.1(typescript@4.9.5)(vite@4.4.7): + resolution: {integrity: sha512-BgQkoT6QlF8cRQIv/wk+utGVu9urhoMQmiv0fule/iZdiOL1oF/3CXxcWPk8FouV6fkOjvOCvO1kTvLzcwlm6g==} peerDependencies: '@preact/preset-vite': '*' typescript: '>= 4.3.x' @@ -3910,15 +3921,15 @@ packages: vite-plugin-glimmerx: optional: true dependencies: - '@storybook/channels': 7.4.0-alpha.0 - '@storybook/client-logger': 7.4.0-alpha.0 - '@storybook/core-common': 7.4.0-alpha.0 - '@storybook/csf-plugin': 7.4.0-alpha.0 + '@storybook/channels': 7.4.0-alpha.1 + '@storybook/client-logger': 7.4.0-alpha.1 + '@storybook/core-common': 7.4.0-alpha.1 + '@storybook/csf-plugin': 7.4.0-alpha.1 '@storybook/mdx2-csf': 1.1.0 - '@storybook/node-logger': 7.4.0-alpha.0 - '@storybook/preview': 7.4.0-alpha.0 - '@storybook/preview-api': 7.4.0-alpha.0 - '@storybook/types': 7.4.0-alpha.0 + '@storybook/node-logger': 7.4.0-alpha.1 + '@storybook/preview': 7.4.0-alpha.1 + '@storybook/preview-api': 7.4.0-alpha.1 + '@storybook/types': 7.4.0-alpha.1 '@types/find-cache-dir': 3.2.1 browser-assert: 1.2.1 es-module-lexer: 0.9.3 @@ -3958,14 +3969,14 @@ packages: tiny-invariant: 1.3.1 dev: true - /@storybook/channels@7.4.0-alpha.0: - resolution: {integrity: sha512-Mh07rR524vtpDTG1BRNpHqH+BfH95DDqVIeG9YZI/z093B+MTwTAaVOcr5iReY4de01VeeC3mPDkPfvvHfqnSQ==} + /@storybook/channels@7.4.0-alpha.1: + resolution: {integrity: sha512-G02qB9LshnqIA6XLF6CWCr/CRcJHf/IAnWBCmsTFKKH4D+v7bYiVFdFf/fYAzDlfWhWkAnVL2DOXS+86XqF37A==} dependencies: - '@storybook/client-logger': 7.4.0-alpha.0 - '@storybook/core-events': 7.4.0-alpha.0 + '@storybook/client-logger': 7.4.0-alpha.1 + '@storybook/core-events': 7.4.0-alpha.1 '@storybook/global': 5.0.0 qs: 6.11.2 - telejson: 7.1.0 + telejson: 7.2.0 tiny-invariant: 1.3.1 dev: true @@ -4032,8 +4043,8 @@ packages: '@storybook/global': 5.0.0 dev: true - /@storybook/client-logger@7.4.0-alpha.0: - resolution: {integrity: sha512-gQTfh3gFVHpmw2CGZSWsk9qZDZuY/AdZZYC+uvyXtXLF1+TrmBkmJTWBEjk93Hm9LJu83Q+z1scCqIUyu6aGHQ==} + /@storybook/client-logger@7.4.0-alpha.1: + resolution: {integrity: sha512-/HsN2qRqlqiVkZBYKQcO+Ddr57XaQEx1TyACZ/TlT69LZoOpq5FbufvuLVOc2BO9Y3otH1IDLVc0IIV9q7U4HA==} dependencies: '@storybook/global': 5.0.0 dev: true @@ -4166,11 +4177,11 @@ packages: - supports-color dev: true - /@storybook/core-common@7.4.0-alpha.0: - resolution: {integrity: sha512-kSxx/S8KeG7qcUm/0OCvNiDlZyge21A2kiwmLrG+EorQc6uCfKXUne8MgDT6tpjddWLu9Ek/so2HFSqBRfwBsA==} + /@storybook/core-common@7.4.0-alpha.1: + resolution: {integrity: sha512-FHCZAWuy9c1/CXULBc9V82dHyK+NQ2DqVR3lM7HRSklp7TzmdPQJo/lz/Tgr5eq9iQ988WR3UDtmye4lLG6RYQ==} dependencies: - '@storybook/node-logger': 7.4.0-alpha.0 - '@storybook/types': 7.4.0-alpha.0 + '@storybook/node-logger': 7.4.0-alpha.1 + '@storybook/types': 7.4.0-alpha.1 '@types/find-cache-dir': 3.2.1 '@types/node': 16.18.40 '@types/node-fetch': 2.6.4 @@ -4204,8 +4215,10 @@ packages: resolution: {integrity: sha512-7Pkgwmj/9B7Z3NNSn2swnviBrg9L1VeYSFw6JJKxtQskt8QoY8LxAsPzVMlHjqRmO6sO7lHo9FgpzIFxdmFaAA==} dev: true - /@storybook/core-events@7.4.0-alpha.0: - resolution: {integrity: sha512-+W41+PQVGruVKRqBmyubl88QmUj88fKefKU8qObNaJZnAicHa6ohb7cgBTD+qGuPwt1S4oWwAXFLvZxU21K6mQ==} + /@storybook/core-events@7.4.0-alpha.1: + resolution: {integrity: sha512-0nTJzu8Gr81L3kGpoYnUZPdKzsHkoVa3nMR9W0pnEY4KyJF3UAYST/qjZaMc62PfJinDz39zH3iXaF9+0tN4zA==} + dependencies: + ts-dedent: 2.2.0 dev: true /@storybook/core-server@7.2.2: @@ -4269,10 +4282,10 @@ packages: - supports-color dev: true - /@storybook/csf-plugin@7.4.0-alpha.0: - resolution: {integrity: sha512-qESMMYLm3d8kUgqdvnr3r6AJAaUmlC5fxHhcoAXz8+0PMmY2WOaP8N/k14kI+lbF9OhdNajfUkQSpB63xgWq0g==} + /@storybook/csf-plugin@7.4.0-alpha.1: + resolution: {integrity: sha512-QeDQLNRQi0co45AFV7bqf3JWf0/mKFLQDcKsMFfdN2TrazUoVu4sV4CMF1XEPpY4AF5pcfWOvAOzQ7NvbyvPOg==} dependencies: - '@storybook/csf-tools': 7.4.0-alpha.0 + '@storybook/csf-tools': 7.4.0-alpha.1 unplugin: 1.4.0 transitivePeerDependencies: - supports-color @@ -4294,15 +4307,15 @@ packages: - supports-color dev: true - /@storybook/csf-tools@7.4.0-alpha.0: - resolution: {integrity: sha512-ylzTMRRNRho8GfUz6u+SbFWmOMkyX5+XlceA4TbHz/uMQ6tjxf9S/ofDrEdkRuUCOHGnYwelLAn7DT77yBKohQ==} + /@storybook/csf-tools@7.4.0-alpha.1: + resolution: {integrity: sha512-dDExFS6JrBBPO7DMF1nLialzzcnZwdOd9tNrPBuJuwdT+BLbDNQVID4txReqMFD/gmL+YebN7Nv7JNuJubBtKA==} dependencies: '@babel/generator': 7.22.10 '@babel/parser': 7.22.10 '@babel/traverse': 7.22.10 '@babel/types': 7.22.10 '@storybook/csf': 0.1.1 - '@storybook/types': 7.4.0-alpha.0 + '@storybook/types': 7.4.0-alpha.1 fs-extra: 11.1.1 recast: 0.23.4 ts-dedent: 2.2.0 @@ -4425,8 +4438,8 @@ packages: resolution: {integrity: sha512-UVjXJ3nRsGI+yyVFCDKFCjkzrQsUSAMORSlo5vOqypO3PjSahGQBgKjlKnZGXwvdGKB2FW56PbKnb/sPBI/kPg==} dev: true - /@storybook/node-logger@7.4.0-alpha.0: - resolution: {integrity: sha512-oZHtVYuC2+VEhfbCWWSmra6bsMicqgyEIQY5296N9+pmQPOGoEQvkt75i1NXkJF/lTPnxRAJu699F3OYsTSQgQ==} + /@storybook/node-logger@7.4.0-alpha.1: + resolution: {integrity: sha512-GoVM7h5uRzjKZHEA7VjadLqL7J3ucAMIdHoArSsFDItvJI2Y7w29dzGm7AI0gK4C6ctBoB0Jd7uaC0tXaF1abQ==} dev: true /@storybook/postinstall@7.2.2: @@ -4471,15 +4484,15 @@ packages: util-deprecate: 1.0.2 dev: true - /@storybook/preview-api@7.4.0-alpha.0: - resolution: {integrity: sha512-iZVBsRtNST8MqQ1Sj/6nPszuL1qFKlw6ByQfWsoCgYPLAgb3nh3/vckapszAWud+YyW8QZbm53fL/Yg+aN4YUg==} + /@storybook/preview-api@7.4.0-alpha.1: + resolution: {integrity: sha512-aLidk1Y1/yGWreQcYB38Y9fr1IF1rplD6RsFg7Pg8IbCjibhrOQ1o92HqCN/cRw20hYTZ/FEpvzPCnt1druDTw==} dependencies: - '@storybook/channels': 7.4.0-alpha.0 - '@storybook/client-logger': 7.4.0-alpha.0 - '@storybook/core-events': 7.4.0-alpha.0 + '@storybook/channels': 7.4.0-alpha.1 + '@storybook/client-logger': 7.4.0-alpha.1 + '@storybook/core-events': 7.4.0-alpha.1 '@storybook/csf': 0.1.1 '@storybook/global': 5.0.0 - '@storybook/types': 7.4.0-alpha.0 + '@storybook/types': 7.4.0-alpha.1 '@types/qs': 6.9.7 dequal: 2.0.3 lodash: 4.17.21 @@ -4490,8 +4503,8 @@ packages: util-deprecate: 1.0.2 dev: true - /@storybook/preview@7.4.0-alpha.0: - resolution: {integrity: sha512-qAcXK2y1/aqJWYibbNFrOgntl2RdinwmuVB2LAyzqiZK3KWu/qZQuC5fJZb8I2E4JEwzdT+V2/Upss7DMyCRAA==} + /@storybook/preview@7.4.0-alpha.1: + resolution: {integrity: sha512-LRfgOqyYiMaQBpaTgClsd0NCNClysdTwF6VTYWnEpisxtzI0HdcRwBTFLzppwnHGnZAg+zvpNjNEPjmuMvI1aw==} dev: true /@storybook/react-dom-shim@7.2.2(react-dom@18.2.0)(react@18.2.0): @@ -4600,10 +4613,10 @@ packages: file-system-cache: 2.3.0 dev: true - /@storybook/types@7.4.0-alpha.0: - resolution: {integrity: sha512-fUkrqGaI3kn8lfvcshmaNmQXs2LXG91iZSts2Sbmbk/Gnr1CnqV/NxIFc+qOAiesOwaLf9a7zD8+/izsLQUtQw==} + /@storybook/types@7.4.0-alpha.1: + resolution: {integrity: sha512-YHLi1ypmS82xpkuSCFeAfmRf4xjJl6+QdO+a4JfIbqjuKApyDl78czawXkrjT638fbw7Rz0gbySrLYUju7WntA==} dependencies: - '@storybook/channels': 7.4.0-alpha.0 + '@storybook/channels': 7.4.0-alpha.1 '@types/babel__core': 7.20.1 '@types/express': 4.17.17 file-system-cache: 2.3.0 @@ -11980,7 +11993,7 @@ packages: resolution: {integrity: sha512-dD+VMYC5fBBQNesVb+mjB0LOkZIf100SQFbjAt9/sDstNUvc5ce3yZwLYXzgcOc7jcSMkrBu/cZNRzEM4YIAyw==} engines: {node: ^14.18 || >=16} dependencies: - '@storybook/builder-vite': 7.4.0-alpha.0(typescript@4.9.5)(vite@4.4.7) + '@storybook/builder-vite': 7.4.0-alpha.1(typescript@4.9.5)(vite@4.4.7) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -12279,6 +12292,12 @@ packages: memoizerific: 1.11.3 dev: true + /telejson@7.2.0: + resolution: {integrity: sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==} + dependencies: + memoizerific: 1.11.3 + dev: true + /temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} diff --git a/public/.well-known/assetlinks.json b/public/.well-known/assetlinks.json new file mode 100644 index 0000000..fdf9ae9 --- /dev/null +++ b/public/.well-known/assetlinks.json @@ -0,0 +1,9 @@ +[{ + "relation": ["delegate_permission/common.handle_all_urls"], + "target": { + "namespace": "android_app", + "package_name": "com.mutinywallet.mutinywallet", + "sha256_cert_fingerprints": + ["F8:20:46:16:9B:79:AA:D1:2A:77:CA:D6:1F:0E:F6:A4:D7:72:3A:5E:DE:C6:70:18:E9:9E:B4:DA:0B:44:17:60"] + } +}] diff --git a/src/components/App.tsx b/src/components/App.tsx index 540a1a2..87b947b 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1,6 +1,4 @@ -import { App as CapacitorApp } from "@capacitor/app"; -import { Capacitor } from "@capacitor/core"; -import { Match, onCleanup, Show, Suspense, Switch } from "solid-js"; +import { Match, Show, Suspense, Switch } from "solid-js"; import { A } from "solid-start"; import settings from "~/assets/icons/settings.svg"; @@ -30,25 +28,6 @@ export function App() { const i18n = useI18n(); const [state, _actions] = useMegaStore(); - // Check if the platform is Android to handle back - if (Capacitor.getPlatform() === "android") { - const { remove } = CapacitorApp.addListener( - "backButton", - ({ canGoBack }) => { - if (!canGoBack) { - CapacitorApp.exitApp(); - } else { - window.history.back(); - } - } - ); - - // Ensure the listener is cleaned up when the component is destroyed - onCleanup(() => { - remove(); - }); - } - return ( diff --git a/src/root.tsx b/src/root.tsx index 7981c28..bebcf63 100644 --- a/src/root.tsx +++ b/src/root.tsx @@ -1,5 +1,5 @@ // @refresh reload -import { Suspense } from "solid-js"; +import { onCleanup, Suspense } from "solid-js"; import { Body, ErrorBoundary, @@ -10,15 +10,62 @@ import { Meta, Routes, Scripts, - Title + Title, + useNavigate } from "solid-start"; // eslint-disable-next-line import "@mutinywallet/ui/style.css"; +import { App as CapacitorApp } from "@capacitor/app"; +import { Capacitor } from "@capacitor/core"; + import { ErrorDisplay, I18nProvider, Toaster } from "~/components"; import { Provider as MegaStoreProvider } from "~/state/megaStore"; +function GlobalListeners() { + // listeners for native navigation handling + // Check if the platform is Android to handle back + if (Capacitor.getPlatform() === "android") { + const { remove } = CapacitorApp.addListener( + "backButton", + ({ canGoBack }) => { + if (!canGoBack) { + CapacitorApp.exitApp(); + } else { + window.history.back(); + } + } + ); + + // Ensure the listener is cleaned up when the component is destroyed + onCleanup(() => { + console.debug("cleaning up backButton listener"); + remove(); + }); + } + + // Handle app links on native platforms + if (Capacitor.isNativePlatform()) { + const navigate = useNavigate(); + const { remove } = CapacitorApp.addListener("appUrlOpen", (data) => { + const url = new URL(data.url); + const path = url.pathname; + const urlParams = new URLSearchParams(url.search); + + console.log(`Navigating to ${path}?${urlParams.toString()}`); + navigate(`${path}?${urlParams.toString()}`); + }); + + onCleanup(() => { + console.debug("cleaning up appUrlOpen listener"); + remove(); + }); + } + + return null; +} + export default function Root() { return ( @@ -81,6 +128,7 @@ export default function Root() { + diff --git a/src/routes/settings/Connections.tsx b/src/routes/settings/Connections.tsx index 48658b6..c8795c3 100644 --- a/src/routes/settings/Connections.tsx +++ b/src/routes/settings/Connections.tsx @@ -1,6 +1,8 @@ +import { Browser } from "@capacitor/browser"; import { NwcProfile } from "@mutinywallet/mutiny-wasm"; import { createResource, createSignal, For, Show } from "solid-js"; import { QRCodeSVG } from "solid-qr-code"; +import { useSearchParams } from "solid-start"; import { BackLink, @@ -40,8 +42,8 @@ function Nwc() { } }); - const urlParams = new URLSearchParams(window.location.search); - const queryName = urlParams.get("name"); + const [searchParams, setSearchParams] = useSearchParams(); + const queryName = searchParams.name; const [formName, setFormName] = createSignal(queryName || ""); const [dialogOpen, setDialogOpen] = createSignal(!!queryName); const [createLoading, setCreateLoading] = createSignal(false); @@ -69,14 +71,14 @@ function Nwc() { refetch(); } - setFormName(""); + setSearchParams({ name: "" }); setDialogOpen(false); - const callbackUriScheme = getCallbackQueryParam(); + const callbackUriScheme = searchParams.callbackUri; if (callbackUriScheme) { const fullURI = profile.nwc_uri.replace( "nostr+walletconnect://", - `${getCallbackQueryParam()}://` + `${callbackUriScheme}://` ); setCallbackUri(fullURI); setCallbackDialogOpen(true); @@ -91,7 +93,8 @@ function Nwc() { function openCallbackUri() { if (callbackUri()) { - window.open(callbackUri() as string, "_blank"); + Browser.open({ url: callbackUri() as string }); + setSearchParams({ callbackUri: "" }); setCallbackDialogOpen(false); } } @@ -115,17 +118,12 @@ function Nwc() { } function openInNostrClient(uri: string) { - window.open(uri, "_blank"); + Browser.open({ url: uri }); } function openInPrimal(uri: string) { const connectString = uri.replace("nostr+walletconnect", "primal"); - window.open(connectString, "_blank"); - } - - function getCallbackQueryParam() { - const urlParams = new URLSearchParams(window.location.search); - return urlParams.get("callbackUri"); + Browser.open({ url: connectString }); } return ( diff --git a/vite.config.ts b/vite.config.ts index b0ae087..3fc4bbc 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -65,7 +65,8 @@ export default defineConfig({ "@capacitor/filesystem", "@capacitor/toast", "@mutinywallet/barcode-scanner", - "@capacitor/app" + "@capacitor/app", + "@capacitor/browser" ], // This is necessary because otherwise `vite dev` can't find the wasm exclude: ["@mutinywallet/mutiny-wasm", "@mutinywallet/waila-wasm"]