From bbd89205523e5a3accd3f2e646aab1c6d377eb40 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Fri, 27 Oct 2023 14:19:38 -0500 Subject: [PATCH] Periodically check if we have inflight payments --- package.json | 2 +- public/sw.ts | 74 ++++++++++++++++++++++++++++++++++++++++++++ src/routes/index.tsx | 27 ++++++++++++++++ vite.config.ts | 6 +++- 4 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 public/sw.ts diff --git a/package.json b/package.json index b1116e4b..bb7ba868 100644 --- a/package.json +++ b/package.json @@ -48,8 +48,8 @@ "@capacitor/clipboard": "^5.0.6", "@capacitor/core": "^5.2.2", "@capacitor/filesystem": "^5.1.4", - "@capacitor/share": "^5.0.6", "@capacitor/haptics": "^5.0.6", + "@capacitor/share": "^5.0.6", "@capacitor/toast": "^5.0.6", "@kobalte/core": "^0.9.8", "@kobalte/tailwindcss": "^0.5.0", diff --git a/public/sw.ts b/public/sw.ts new file mode 100644 index 00000000..53d0099f --- /dev/null +++ b/public/sw.ts @@ -0,0 +1,74 @@ +self.addEventListener("periodicsync", (event) => { + if (event.tag === "check-inflight-payments") { + event.waitUntil(checkPaymentsInFlight()); + } +}); + +async function checkPaymentsInFlight() { + console.log('checkPaymentsInFlight'); + const db = await openDatabase(); + + const transaction = db.transaction('wallet_store', 'readonly'); + const store = transaction.objectStore('wallet_store'); + + // Get keys prefixed with "payment_outbound" + const keys = await getAllKeysWithPrefix(store, 'payment_outbound'); + + for (let key of keys) { + const payment = await get(store, key); + console.log(payment.status); + if (payment && payment.status === "InFlight") { + showNotification(); + break; + } + } + transaction.commit(); +} + +function openDatabase(): Promise { + return new Promise((resolve, reject) => { + const request = indexedDB.open('wallet'); + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); +} + +function getAllKeysWithPrefix(store: IDBObjectStore, prefix: string): Promise { + return new Promise((resolve, reject) => { + const keys: string[] = []; + const cursorRequest = store.openKeyCursor(); + + cursorRequest.onsuccess = function(event) { + const cursor = (event.target as IDBRequest).result as IDBCursor; + if (cursor) { + if (cursor.key.toString().startsWith(prefix)) { + keys.push(cursor.key.toString()); + } + cursor.continue(); + } else { + resolve(keys); + } + }; + + cursorRequest.onerror = function() { + reject(cursorRequest.error); + }; + }); +} + +function get(store: IDBObjectStore, key: string): Promise { + return new Promise((resolve, reject) => { + const request = store.get(key); + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); +} + +function showNotification() { + // todo make pretty + self.registration.showNotification('Payment Alert', { + body: 'There are payments with status InFlight.', + // icon: '/path/to/icon.png', // You can specify an icon if you have one + // badge: '/path/to/badge.png' // You can specify a badge if you have one + }); +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index ddcb63ed..eefd19fb 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -24,10 +24,37 @@ import { useI18n } from "~/i18n/context"; import { FeedbackLink } from "~/routes/Feedback"; import { useMegaStore } from "~/state/megaStore"; +async function registerPeriodicInFlightCheck() { + const registration = await navigator.serviceWorker.ready; + try { + // Request notification permission from the user + Notification.requestPermission().then(async (permission) => { + if (permission === "granted") { + console.log("Notification permission granted."); + + await registration.periodicSync.register( + "check-inflight-payments", + { + minInterval: 60 * 60 * 1000 // every hour + } + ); + } else { + console.error("Notification permission denied."); + } + }); + } catch (e) { + console.warn("Periodic Sync could not be registered!"); + console.error(e); + } +} + export default function App() { const i18n = useI18n(); const [state, _actions] = useMegaStore(); + // run registerPeriodicInFlightCheck in background + const _ = registerPeriodicInFlightCheck(); + return ( diff --git a/vite.config.ts b/vite.config.ts index a939b96b..c6ef7e92 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -13,9 +13,13 @@ const commitHash = process.env.VITE_COMMIT_HASH ?? child.execSync("git rev-parse const pwaOptions: Partial = { base: "/", + injectRegister: 'inline', + filename: 'sw.ts', + strategies: 'injectManifest', registerType: "autoUpdate", devOptions: { - enabled: false + enabled: true, + type: "module" }, includeAssets: ["favicon.ico", "robots.txt"], manifest: manifest