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;