From d741823039f6d9304e5e00806d3500882ddc5374 Mon Sep 17 00:00:00 2001 From: SkateSpots Date: Wed, 28 Oct 2026 16:39:07 +0700 Subject: [PATCH] feat: Android back/app links and web app-shell cache --- AGENTS.md | 18 +++- package-lock.json | 10 ++ package.json | 5 +- public/.well-known/assetlinks.json | 10 ++ public/push-sw.js | 70 +++++++++++++- scripts/inject-deep-links.mjs | 44 +++++++++ src/components/DeepLinkHandler.tsx | 148 ++++++++++++++++++----------- src/lib/appServiceWorker.ts | 25 +++++ src/main.tsx | 3 + 9 files changed, 274 insertions(+), 59 deletions(-) create mode 100644 public/.well-known/assetlinks.json create mode 100755 scripts/inject-deep-links.mjs create mode 100644 src/lib/appServiceWorker.ts diff --git a/AGENTS.md b/AGENTS.md index 17f6c86..f712920 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2077,8 +2077,22 @@ session with **no other memory of this project**. So: used by **both** maps (`SpotMap`, `RouteDialog`). Deliberately NOT a service worker: a SW only helps the web build, while the Android app runs inside Capacitor's WebView where a SW never sees tile requests. Works on both. - Still open: offline **app shell** (SW) — unnecessary for native, deferred for - web. + **Web app shell (2026-10-28):** `public/push-sw.js` is also the app-shell + cache — hashed assets cache-first, navigations network-first with + `/index.html` fallback, old `skate-spots-shell-*` caches removed. It is + registered unconditionally on web by `lib/appServiceWorker.ts` (skipped on + native). The push handlers are unchanged; `/api/*` and cross-origin model + requests are never cached. +- **Android navigation + app links (2026-10-28):** `@capacitor/app` is now a + real dependency. `DeepLinkHandler` dynamically imports it and handles + `appUrlOpen` (HTTPS paths, `#/` HashRouter paths, and + `org.skatespots.app://` host routes) plus hardware `backButton` (close the top + Radix overlay → history back → minimise at the map). `scripts/inject-deep-links.mjs` + adds the verified `https://skate-spots.org` + custom-scheme intent filters to + the generated manifest; `public/.well-known/assetlinks.json` carries the + release cert SHA-256 `B7:52:F2:6A:…:12:0A` for package + `org.skatespots.app`. If the signing cert changes, update assetlinks and + redeploy; app-link verification happens at install. - **Photo redundancy (2026-09-28):** `useUploadFile` now uses **two** Blossom hosts (`blossom.primal.net`, `blossom.band`) instead of three. ⚠️ `BlossomUploader.upload` signs ONE auth event then does diff --git a/package-lock.json b/package-lock.json index 2b27f85..82fa2b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@bitcoinerlab/secp256k1": "^1.2.0", "@capacitor/android": "^8.4.2", + "@capacitor/app": "^8.1.2", "@capacitor/camera": "^8.2.1", "@capacitor/core": "^8.4.2", "@capacitor/geolocation": "^8.2.0", @@ -324,6 +325,15 @@ "@capacitor/core": "^8.5.0" } }, + "node_modules/@capacitor/app": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@capacitor/app/-/app-8.1.2.tgz", + "integrity": "sha512-uKBlsrXH0IdvUn69e/KKYbZ32A06pcrUdG8LWuZ/6y33YMXZXLEEdNSwl+jKS/SrsTWqnePs8EqSWaiQM3mUGA==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, "node_modules/@capacitor/camera": { "version": "8.2.4", "resolved": "https://registry.npmjs.org/@capacitor/camera/-/camera-8.2.4.tgz", diff --git a/package.json b/package.json index e1e3675..b1c45eb 100644 --- a/package.json +++ b/package.json @@ -22,10 +22,10 @@ "test": "npm i --silent && tsc --noEmit && tsc -p cvm-server/tsconfig.json && tsc -p ai-proxy/tsconfig.json && tsc -p push-service/tsconfig.json && eslint && node scripts/check-i18n.mjs && vitest run --reporter=dot --silent && vite build -l error && cp dist/index.html dist/404.html && echo 'All tests passed!'", "deploy": "npm i --silent && npm run build && npx -y nostr-deploy-cli deploy --skip-setup", "android:init": "npm run android:build:apk && npx cap add android && npx cap sync android && node scripts/inject-amber-queries.mjs && node scripts/inject-main-activity.mjs && node scripts/inject-network-security.mjs && node scripts/inject-signing-config.mjs", - "android:sync": "npm run android:build:apk && npx cap sync android && node scripts/sync-android-version.mjs && node scripts/inject-amber-queries.mjs && node scripts/inject-main-activity.mjs && node scripts/inject-network-security.mjs && node scripts/inject-signing-config.mjs", + "android:sync": "npm run android:build:apk && npx cap sync android && node scripts/sync-android-version.mjs && node scripts/inject-amber-queries.mjs && node scripts/inject-deep-links.mjs && node scripts/inject-main-activity.mjs && node scripts/inject-network-security.mjs && node scripts/inject-signing-config.mjs", "android:open": "npx cap open android", "android:version": "node scripts/sync-android-version.mjs", - "android:build": "npm run android:build:apk && npx cap sync android && node scripts/sync-android-version.mjs && node scripts/inject-amber-queries.mjs && node scripts/inject-main-activity.mjs && node scripts/inject-network-security.mjs && node scripts/inject-signing-config.mjs && cd android && ./gradlew assembleRelease", + "android:build": "npm run android:build:apk && npx cap sync android && node scripts/sync-android-version.mjs && node scripts/inject-amber-queries.mjs && node scripts/inject-deep-links.mjs && node scripts/inject-main-activity.mjs && node scripts/inject-network-security.mjs && node scripts/inject-signing-config.mjs && cd android && ./gradlew assembleRelease", "android:build:apk": "CAPACITOR_BUILD=1 vite build && sed -i 's|||g' dist/index.html && sed -i 's|