From 619eb76c71b0b7db29fa42976da5a9c3ff036028 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Sun, 29 Oct 2023 11:59:53 -0500 Subject: [PATCH] Background android app pending payments --- android/app/capacitor.build.gradle | 1 + android/app/src/main/AndroidManifest.xml | 2 + android/capacitor.settings.gradle | 3 + capacitor.config.ts | 12 ++- package.json | 3 +- pnpm-lock.yaml | 12 +++ src/runners/background.ts | 99 ++++++++++++++++++++++++ 7 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 src/runners/background.ts diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle index 67325db4..8f0d6a68 100644 --- a/android/app/capacitor.build.gradle +++ b/android/app/capacitor.build.gradle @@ -11,6 +11,7 @@ apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { implementation project(':capacitor-mlkit-barcode-scanning') implementation project(':capacitor-app') + implementation project(':capacitor-background-runner') implementation project(':capacitor-browser') implementation project(':capacitor-clipboard') implementation project(':capacitor-filesystem') diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a9e99d88..060fc852 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -60,4 +60,6 @@ + + diff --git a/android/capacitor.settings.gradle b/android/capacitor.settings.gradle index 93a5ac65..4be35e23 100644 --- a/android/capacitor.settings.gradle +++ b/android/capacitor.settings.gradle @@ -8,6 +8,9 @@ project(':capacitor-mlkit-barcode-scanning').projectDir = new File('../node_modu include ':capacitor-app' project(':capacitor-app').projectDir = new File('../node_modules/.pnpm/@capacitor+app@5.0.6_@capacitor+core@5.5.1/node_modules/@capacitor/app/android') +include ':capacitor-background-runner' +project(':capacitor-background-runner').projectDir = new File('../node_modules/.pnpm/@capacitor+background-runner@1.0.5_@capacitor+core@5.2.2/node_modules/@capacitor/background-runner/android') + include ':capacitor-browser' project(':capacitor-browser').projectDir = new File('../node_modules/.pnpm/@capacitor+browser@5.0.6_@capacitor+core@5.5.1/node_modules/@capacitor/browser/android') diff --git a/capacitor.config.ts b/capacitor.config.ts index ef03c3e0..50467207 100644 --- a/capacitor.config.ts +++ b/capacitor.config.ts @@ -7,7 +7,17 @@ const config: CapacitorConfig = { webDir: 'dist/public', server: { androidScheme: 'https' - } + }, + plugins: { + BackgroundRunner: { + label: 'com.mutinywallet.mutinywallet.background', + src: 'runners/background.ts', + event: 'checkPaymentsInFlight', + repeat: true, + interval: 60, + autoStart: true, + }, + }, }; export default config; diff --git a/package.json b/package.json index fd0cf16c..fd6fb09c 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "@capacitor-mlkit/barcode-scanning": "^5.3.0", "@capacitor/android": "^5.5.1", "@capacitor/app": "^5.0.6", - "@capacitor/browser": "5.0.6", + "@capacitor/background-runner": "1.0.5", + "@capacitor/browser": "5.0.6", "@capacitor/clipboard": "^5.0.6", "@capacitor/core": "^5.5.1", "@capacitor/filesystem": "^5.1.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 71be13e8..a487cddf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@capacitor/app': specifier: ^5.0.6 version: 5.0.6(@capacitor/core@5.5.1) + '@capacitor/background-runner': + specifier: 1.0.5 + version: 1.0.5(@capacitor/core@5.5.1) '@capacitor/browser': specifier: 5.0.6 version: 5.0.6(@capacitor/core@5.5.1) @@ -1537,6 +1540,15 @@ packages: - typescript dev: true + /@capacitor/background-runner@1.0.5(@capacitor/core@5.5.1): + resolution: {integrity: sha512-8WCPkhO42p3O5ENjf8FqvnbwMjNEPcFVxIwsp/Gi+CGWQlgGm7MbxlJAtGmGcNrglD1OqpYWWB0tvJzX2+I+sA==} + requiresBuild: true + peerDependencies: + '@capacitor/core': ^5.0.0 + dependencies: + '@capacitor/core': 5.5.1 + dev: false + /@capacitor/browser@5.0.6(@capacitor/core@5.5.1): resolution: {integrity: sha512-wEI7Na6PVzSP/00ud7pjbBwXwVG7HywCdy2fJT/hzF6yuHn4tDirbOvbr1JKd9LZqKs2Xn+TapV38JhBRhX6YA==} peerDependencies: diff --git a/src/runners/background.ts b/src/runners/background.ts new file mode 100644 index 00000000..3083716d --- /dev/null +++ b/src/runners/background.ts @@ -0,0 +1,99 @@ +import { CapacitorNotifications } from "@capacitor/background-runner"; + +addEventListener("checkPaymentsInFlight", async (resolve, reject, _args) => { + try { + await checkPaymentsInFlight(); + resolve(); + } catch (e) { + reject(e); + } +}); + +interface OutboundPayment { + status: string; +} + +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 (const key of keys) { + const payment = await get(store, key); + console.log(payment.status); + // fixme change back to InFlight + if (payment && payment.status === "Succeeded") { + 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() { + // generate random id + const id = Math.random() * 100_000_000; + + // send notification in 5 seconds + const scheduleDate = new Date(); + scheduleDate.setSeconds(scheduleDate.getSeconds() + 5); + + // todo make pretty + CapacitorNotifications.schedule([ + { + id, + title: "You have a payment in flight", + body: "Open Mutiny to make sure it completes.", + scheduleAt: scheduleDate + } + ]); +} + +export default checkPaymentsInFlight;