@breeztech/breez-sdk-spark 0.7.21 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/breez-sdk-spark.tgz +0 -0
- package/bundler/breez_sdk_spark_wasm.d.ts +581 -533
- package/bundler/breez_sdk_spark_wasm_bg.js +74 -56
- package/bundler/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/bundler/breez_sdk_spark_wasm_bg.wasm.d.ts +4 -2
- package/bundler/storage/index.js +521 -215
- package/deno/breez_sdk_spark_wasm.d.ts +581 -533
- package/deno/breez_sdk_spark_wasm.js +72 -54
- package/deno/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/deno/breez_sdk_spark_wasm_bg.wasm.d.ts +4 -2
- package/nodejs/breez_sdk_spark_wasm.d.ts +581 -533
- package/nodejs/breez_sdk_spark_wasm.js +74 -56
- package/nodejs/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/nodejs/breez_sdk_spark_wasm_bg.wasm.d.ts +4 -2
- package/nodejs/storage/index.cjs +160 -182
- package/nodejs/storage/migrations.cjs +27 -3
- package/package.json +1 -1
- package/web/breez_sdk_spark_wasm.d.ts +585 -535
- package/web/breez_sdk_spark_wasm.js +72 -54
- package/web/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/web/breez_sdk_spark_wasm_bg.wasm.d.ts +4 -2
- package/web/storage/index.js +521 -215
package/bundler/storage/index.js
CHANGED
|
@@ -148,6 +148,8 @@ class MigrationManager {
|
|
|
148
148
|
{
|
|
149
149
|
name: "Add sync tables",
|
|
150
150
|
upgrade: (db, transaction) => {
|
|
151
|
+
// sync_revision: tracks the last committed revision (from server-acknowledged
|
|
152
|
+
// or server-received records). Does NOT include pending outgoing revisions.
|
|
151
153
|
if (!db.objectStoreNames.contains("sync_revision")) {
|
|
152
154
|
const syncRevisionStore = db.createObjectStore("sync_revision", {
|
|
153
155
|
keyPath: "id",
|
|
@@ -178,15 +180,17 @@ class MigrationManager {
|
|
|
178
180
|
if (!db.objectStoreNames.contains("sync_state")) {
|
|
179
181
|
db.createObjectStore("sync_state", { keyPath: ["type", "dataId"] });
|
|
180
182
|
}
|
|
181
|
-
}
|
|
183
|
+
},
|
|
182
184
|
},
|
|
183
185
|
{
|
|
184
186
|
name: "Create lnurl_receive_metadata store",
|
|
185
187
|
upgrade: (db) => {
|
|
186
188
|
if (!db.objectStoreNames.contains("lnurl_receive_metadata")) {
|
|
187
|
-
db.createObjectStore("lnurl_receive_metadata", {
|
|
189
|
+
db.createObjectStore("lnurl_receive_metadata", {
|
|
190
|
+
keyPath: "paymentHash",
|
|
191
|
+
});
|
|
188
192
|
}
|
|
189
|
-
}
|
|
193
|
+
},
|
|
190
194
|
},
|
|
191
195
|
{
|
|
192
196
|
// Delete all unclaimed deposits to clear old claim_error JSON format.
|
|
@@ -197,7 +201,7 @@ class MigrationManager {
|
|
|
197
201
|
const store = transaction.objectStore("unclaimed_deposits");
|
|
198
202
|
store.clear();
|
|
199
203
|
}
|
|
200
|
-
}
|
|
204
|
+
},
|
|
201
205
|
},
|
|
202
206
|
{
|
|
203
207
|
name: "Clear sync tables for BreezSigner backward compatibility",
|
|
@@ -236,6 +240,97 @@ class MigrationManager {
|
|
|
236
240
|
settings.delete("sync_initial_complete");
|
|
237
241
|
}
|
|
238
242
|
}
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
name: "Create parentPaymentId index for related payments lookup",
|
|
246
|
+
upgrade: (db, transaction) => {
|
|
247
|
+
if (db.objectStoreNames.contains("payment_metadata")) {
|
|
248
|
+
const metadataStore = transaction.objectStore("payment_metadata");
|
|
249
|
+
if (!metadataStore.indexNames.contains("parentPaymentId")) {
|
|
250
|
+
metadataStore.createIndex("parentPaymentId", "parentPaymentId", { unique: false });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: "Add tx_type to token payments and trigger token re-sync",
|
|
257
|
+
upgrade: (db, transaction) => {
|
|
258
|
+
// Update all existing token payments to have a default txType
|
|
259
|
+
if (db.objectStoreNames.contains("payments")) {
|
|
260
|
+
const paymentStore = transaction.objectStore("payments");
|
|
261
|
+
const getAllRequest = paymentStore.getAll();
|
|
262
|
+
|
|
263
|
+
getAllRequest.onsuccess = () => {
|
|
264
|
+
const payments = getAllRequest.result;
|
|
265
|
+
|
|
266
|
+
payments.forEach((payment) => {
|
|
267
|
+
// Parse details if it's a string
|
|
268
|
+
let details = null;
|
|
269
|
+
if (payment.details && typeof payment.details === "string") {
|
|
270
|
+
try {
|
|
271
|
+
details = JSON.parse(payment.details);
|
|
272
|
+
} catch (e) {
|
|
273
|
+
return; // Skip this payment if parsing fails
|
|
274
|
+
}
|
|
275
|
+
} else {
|
|
276
|
+
details = payment.details;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Add default txType to token payments
|
|
280
|
+
if (details && details.type === "token" && !details.txType) {
|
|
281
|
+
details.txType = "transfer";
|
|
282
|
+
payment.details = JSON.stringify(details);
|
|
283
|
+
paymentStore.put(payment);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Reset sync cache to trigger token re-sync
|
|
290
|
+
if (db.objectStoreNames.contains("settings")) {
|
|
291
|
+
const settingsStore = transaction.objectStore("settings");
|
|
292
|
+
const getRequest = settingsStore.get("sync_offset");
|
|
293
|
+
|
|
294
|
+
getRequest.onsuccess = () => {
|
|
295
|
+
const syncCache = getRequest.result;
|
|
296
|
+
if (syncCache && syncCache.value) {
|
|
297
|
+
try {
|
|
298
|
+
const syncInfo = JSON.parse(syncCache.value);
|
|
299
|
+
// Reset only the token sync position, keep the bitcoin offset
|
|
300
|
+
syncInfo.last_synced_final_token_payment_id = null;
|
|
301
|
+
settingsStore.put({
|
|
302
|
+
key: "sync_offset",
|
|
303
|
+
value: JSON.stringify(syncInfo),
|
|
304
|
+
});
|
|
305
|
+
} catch (e) {
|
|
306
|
+
// If parsing fails, just continue
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
name: "Clear sync tables to force re-sync",
|
|
315
|
+
upgrade: (db, transaction) => {
|
|
316
|
+
if (db.objectStoreNames.contains("sync_outgoing")) {
|
|
317
|
+
transaction.objectStore("sync_outgoing").clear();
|
|
318
|
+
}
|
|
319
|
+
if (db.objectStoreNames.contains("sync_incoming")) {
|
|
320
|
+
transaction.objectStore("sync_incoming").clear();
|
|
321
|
+
}
|
|
322
|
+
if (db.objectStoreNames.contains("sync_state")) {
|
|
323
|
+
transaction.objectStore("sync_state").clear();
|
|
324
|
+
}
|
|
325
|
+
if (db.objectStoreNames.contains("sync_revision")) {
|
|
326
|
+
const syncRevision = transaction.objectStore("sync_revision");
|
|
327
|
+
syncRevision.clear();
|
|
328
|
+
syncRevision.put({ id: 1, revision: "0" });
|
|
329
|
+
}
|
|
330
|
+
if (db.objectStoreNames.contains("settings")) {
|
|
331
|
+
transaction.objectStore("settings").delete("sync_initial_complete");
|
|
332
|
+
}
|
|
333
|
+
}
|
|
239
334
|
}
|
|
240
335
|
];
|
|
241
336
|
}
|
|
@@ -260,7 +355,7 @@ class IndexedDBStorage {
|
|
|
260
355
|
this.db = null;
|
|
261
356
|
this.migrationManager = null;
|
|
262
357
|
this.logger = logger;
|
|
263
|
-
this.dbVersion =
|
|
358
|
+
this.dbVersion = 10; // Current schema version
|
|
264
359
|
}
|
|
265
360
|
|
|
266
361
|
/**
|
|
@@ -418,6 +513,59 @@ class IndexedDBStorage {
|
|
|
418
513
|
|
|
419
514
|
// ===== Payment Operations =====
|
|
420
515
|
|
|
516
|
+
/**
|
|
517
|
+
* Gets the set of payment IDs that are related payments (have a parentPaymentId).
|
|
518
|
+
* Uses the parentPaymentId index for efficient lookup.
|
|
519
|
+
* @param {IDBObjectStore} metadataStore - The payment_metadata object store
|
|
520
|
+
* @returns {Promise<Set<string>>} Set of payment IDs that are related payments
|
|
521
|
+
*/
|
|
522
|
+
_getRelatedPaymentIds(metadataStore) {
|
|
523
|
+
return new Promise((resolve) => {
|
|
524
|
+
const relatedPaymentIds = new Set();
|
|
525
|
+
|
|
526
|
+
// Check if the parentPaymentId index exists (added in migration)
|
|
527
|
+
if (!metadataStore.indexNames.contains("parentPaymentId")) {
|
|
528
|
+
// Index doesn't exist yet, fall back to scanning all metadata
|
|
529
|
+
const cursorRequest = metadataStore.openCursor();
|
|
530
|
+
cursorRequest.onsuccess = (event) => {
|
|
531
|
+
const cursor = event.target.result;
|
|
532
|
+
if (cursor) {
|
|
533
|
+
if (cursor.value.parentPaymentId) {
|
|
534
|
+
relatedPaymentIds.add(cursor.value.paymentId);
|
|
535
|
+
}
|
|
536
|
+
cursor.continue();
|
|
537
|
+
} else {
|
|
538
|
+
resolve(relatedPaymentIds);
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
cursorRequest.onerror = () => resolve(new Set());
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Use the parentPaymentId index to find all metadata entries with a parent
|
|
546
|
+
const index = metadataStore.index("parentPaymentId");
|
|
547
|
+
const cursorRequest = index.openCursor();
|
|
548
|
+
|
|
549
|
+
cursorRequest.onsuccess = (event) => {
|
|
550
|
+
const cursor = event.target.result;
|
|
551
|
+
if (cursor) {
|
|
552
|
+
// Only add if parentPaymentId is truthy (not null/undefined)
|
|
553
|
+
if (cursor.value.parentPaymentId) {
|
|
554
|
+
relatedPaymentIds.add(cursor.value.paymentId);
|
|
555
|
+
}
|
|
556
|
+
cursor.continue();
|
|
557
|
+
} else {
|
|
558
|
+
resolve(relatedPaymentIds);
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
cursorRequest.onerror = () => {
|
|
563
|
+
// If index lookup fails, return empty set and fall back to per-payment lookup
|
|
564
|
+
resolve(new Set());
|
|
565
|
+
};
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
421
569
|
async listPayments(request) {
|
|
422
570
|
if (!this.db) {
|
|
423
571
|
throw new StorageError("Database not initialized");
|
|
@@ -427,15 +575,18 @@ class IndexedDBStorage {
|
|
|
427
575
|
const actualOffset = request.offset !== null ? request.offset : 0;
|
|
428
576
|
const actualLimit = request.limit !== null ? request.limit : 4294967295; // u32::MAX
|
|
429
577
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
const lnurlReceiveMetadataStore = transaction.objectStore("lnurl_receive_metadata");
|
|
578
|
+
const transaction = this.db.transaction(
|
|
579
|
+
["payments", "payment_metadata", "lnurl_receive_metadata"],
|
|
580
|
+
"readonly"
|
|
581
|
+
);
|
|
582
|
+
const paymentStore = transaction.objectStore("payments");
|
|
583
|
+
const metadataStore = transaction.objectStore("payment_metadata");
|
|
584
|
+
const lnurlReceiveMetadataStore = transaction.objectStore("lnurl_receive_metadata");
|
|
438
585
|
|
|
586
|
+
// Build set of related payment IDs upfront for O(1) filtering
|
|
587
|
+
const relatedPaymentIds = await this._getRelatedPaymentIds(metadataStore);
|
|
588
|
+
|
|
589
|
+
return new Promise((resolve, reject) => {
|
|
439
590
|
const payments = [];
|
|
440
591
|
let count = 0;
|
|
441
592
|
let skipped = 0;
|
|
@@ -458,16 +609,23 @@ class IndexedDBStorage {
|
|
|
458
609
|
|
|
459
610
|
const payment = cursor.value;
|
|
460
611
|
|
|
612
|
+
// Skip related payments (those with a parentPaymentId)
|
|
613
|
+
if (relatedPaymentIds.has(payment.id)) {
|
|
614
|
+
cursor.continue();
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
|
|
461
618
|
if (skipped < actualOffset) {
|
|
462
619
|
skipped++;
|
|
463
620
|
cursor.continue();
|
|
464
621
|
return;
|
|
465
622
|
}
|
|
466
623
|
|
|
467
|
-
// Get metadata for this payment
|
|
624
|
+
// Get metadata for this payment (now only for non-related payments)
|
|
468
625
|
const metadataRequest = metadataStore.get(payment.id);
|
|
469
626
|
metadataRequest.onsuccess = () => {
|
|
470
627
|
const metadata = metadataRequest.result;
|
|
628
|
+
|
|
471
629
|
const paymentWithMetadata = this._mergePaymentMetadata(
|
|
472
630
|
payment,
|
|
473
631
|
metadata
|
|
@@ -478,9 +636,12 @@ class IndexedDBStorage {
|
|
|
478
636
|
cursor.continue();
|
|
479
637
|
return;
|
|
480
638
|
}
|
|
481
|
-
|
|
639
|
+
|
|
482
640
|
// Fetch lnurl receive metadata if it's a lightning payment
|
|
483
|
-
this._fetchLnurlReceiveMetadata(
|
|
641
|
+
this._fetchLnurlReceiveMetadata(
|
|
642
|
+
paymentWithMetadata,
|
|
643
|
+
lnurlReceiveMetadataStore
|
|
644
|
+
)
|
|
484
645
|
.then((mergedPayment) => {
|
|
485
646
|
payments.push(mergedPayment);
|
|
486
647
|
count++;
|
|
@@ -560,7 +721,9 @@ class IndexedDBStorage {
|
|
|
560
721
|
);
|
|
561
722
|
const paymentStore = transaction.objectStore("payments");
|
|
562
723
|
const metadataStore = transaction.objectStore("payment_metadata");
|
|
563
|
-
const lnurlReceiveMetadataStore = transaction.objectStore(
|
|
724
|
+
const lnurlReceiveMetadataStore = transaction.objectStore(
|
|
725
|
+
"lnurl_receive_metadata"
|
|
726
|
+
);
|
|
564
727
|
|
|
565
728
|
const paymentRequest = paymentStore.get(id);
|
|
566
729
|
|
|
@@ -579,9 +742,12 @@ class IndexedDBStorage {
|
|
|
579
742
|
payment,
|
|
580
743
|
metadata
|
|
581
744
|
);
|
|
582
|
-
|
|
745
|
+
|
|
583
746
|
// Fetch lnurl receive metadata if it's a lightning payment
|
|
584
|
-
this._fetchLnurlReceiveMetadata(
|
|
747
|
+
this._fetchLnurlReceiveMetadata(
|
|
748
|
+
paymentWithMetadata,
|
|
749
|
+
lnurlReceiveMetadataStore
|
|
750
|
+
)
|
|
585
751
|
.then(resolve)
|
|
586
752
|
.catch(() => {
|
|
587
753
|
// Continue without lnurl receive metadata if fetch fails
|
|
@@ -620,7 +786,9 @@ class IndexedDBStorage {
|
|
|
620
786
|
const paymentStore = transaction.objectStore("payments");
|
|
621
787
|
const invoiceIndex = paymentStore.index("invoice");
|
|
622
788
|
const metadataStore = transaction.objectStore("payment_metadata");
|
|
623
|
-
const lnurlReceiveMetadataStore = transaction.objectStore(
|
|
789
|
+
const lnurlReceiveMetadataStore = transaction.objectStore(
|
|
790
|
+
"lnurl_receive_metadata"
|
|
791
|
+
);
|
|
624
792
|
|
|
625
793
|
const paymentRequest = invoiceIndex.get(invoice);
|
|
626
794
|
|
|
@@ -639,9 +807,12 @@ class IndexedDBStorage {
|
|
|
639
807
|
payment,
|
|
640
808
|
metadata
|
|
641
809
|
);
|
|
642
|
-
|
|
810
|
+
|
|
643
811
|
// Fetch lnurl receive metadata if it's a lightning payment
|
|
644
|
-
this._fetchLnurlReceiveMetadata(
|
|
812
|
+
this._fetchLnurlReceiveMetadata(
|
|
813
|
+
paymentWithMetadata,
|
|
814
|
+
lnurlReceiveMetadataStore
|
|
815
|
+
)
|
|
645
816
|
.then(resolve)
|
|
646
817
|
.catch(() => {
|
|
647
818
|
// Continue without lnurl receive metadata if fetch fails
|
|
@@ -667,7 +838,173 @@ class IndexedDBStorage {
|
|
|
667
838
|
});
|
|
668
839
|
}
|
|
669
840
|
|
|
670
|
-
|
|
841
|
+
/**
|
|
842
|
+
* Checks if any related payments exist (payments with a parentPaymentId).
|
|
843
|
+
* Uses the parentPaymentId index for efficient lookup.
|
|
844
|
+
* @param {IDBObjectStore} metadataStore - The payment_metadata object store
|
|
845
|
+
* @returns {Promise<boolean>} True if any related payments exist
|
|
846
|
+
*/
|
|
847
|
+
_hasRelatedPayments(metadataStore) {
|
|
848
|
+
return new Promise((resolve) => {
|
|
849
|
+
// Check if the parentPaymentId index exists (added in migration)
|
|
850
|
+
if (!metadataStore.indexNames.contains("parentPaymentId")) {
|
|
851
|
+
// Index doesn't exist yet, fall back to scanning all metadata
|
|
852
|
+
const cursorRequest = metadataStore.openCursor();
|
|
853
|
+
cursorRequest.onsuccess = (event) => {
|
|
854
|
+
const cursor = event.target.result;
|
|
855
|
+
if (cursor) {
|
|
856
|
+
if (cursor.value.parentPaymentId) {
|
|
857
|
+
resolve(true);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
cursor.continue();
|
|
861
|
+
} else {
|
|
862
|
+
resolve(false);
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
cursorRequest.onerror = () => resolve(true); // Assume there might be related payments on error
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
const index = metadataStore.index("parentPaymentId");
|
|
870
|
+
const cursorRequest = index.openCursor();
|
|
871
|
+
|
|
872
|
+
cursorRequest.onsuccess = (event) => {
|
|
873
|
+
const cursor = event.target.result;
|
|
874
|
+
if (cursor && cursor.value.parentPaymentId) {
|
|
875
|
+
// Found at least one related payment
|
|
876
|
+
resolve(true);
|
|
877
|
+
} else if (cursor) {
|
|
878
|
+
// Entry with null parentPaymentId, continue searching
|
|
879
|
+
cursor.continue();
|
|
880
|
+
} else {
|
|
881
|
+
// No more entries
|
|
882
|
+
resolve(false);
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
cursorRequest.onerror = () => {
|
|
887
|
+
// If index lookup fails, assume there might be related payments
|
|
888
|
+
resolve(true);
|
|
889
|
+
};
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Gets payments that have any of the specified parent payment IDs.
|
|
895
|
+
* @param {string[]} parentPaymentIds - Array of parent payment IDs
|
|
896
|
+
* @returns {Promise<Object>} Map of parentPaymentId -> array of RelatedPayment objects
|
|
897
|
+
*/
|
|
898
|
+
async getPaymentsByParentIds(parentPaymentIds) {
|
|
899
|
+
if (!this.db) {
|
|
900
|
+
throw new StorageError("Database not initialized");
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
if (!parentPaymentIds || parentPaymentIds.length === 0) {
|
|
904
|
+
return {};
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
const transaction = this.db.transaction(
|
|
908
|
+
["payments", "payment_metadata", "lnurl_receive_metadata"],
|
|
909
|
+
"readonly"
|
|
910
|
+
);
|
|
911
|
+
const metadataStore = transaction.objectStore("payment_metadata");
|
|
912
|
+
|
|
913
|
+
// Early exit if no related payments exist
|
|
914
|
+
const hasRelated = await this._hasRelatedPayments(metadataStore);
|
|
915
|
+
if (!hasRelated) {
|
|
916
|
+
return {};
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
const parentIdSet = new Set(parentPaymentIds);
|
|
920
|
+
const paymentStore = transaction.objectStore("payments");
|
|
921
|
+
const lnurlReceiveMetadataStore = transaction.objectStore("lnurl_receive_metadata");
|
|
922
|
+
|
|
923
|
+
return new Promise((resolve, reject) => {
|
|
924
|
+
const result = {};
|
|
925
|
+
const fetchedMetadata = [];
|
|
926
|
+
|
|
927
|
+
// Query all metadata records and filter by parentPaymentId
|
|
928
|
+
const cursorRequest = metadataStore.openCursor();
|
|
929
|
+
|
|
930
|
+
cursorRequest.onsuccess = (event) => {
|
|
931
|
+
const cursor = event.target.result;
|
|
932
|
+
if (!cursor) {
|
|
933
|
+
// All metadata processed, now fetch payment details
|
|
934
|
+
if (fetchedMetadata.length === 0) {
|
|
935
|
+
resolve(result);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
let processed = 0;
|
|
940
|
+
for (const metadata of fetchedMetadata) {
|
|
941
|
+
const parentId = metadata.parentPaymentId;
|
|
942
|
+
const paymentRequest = paymentStore.get(metadata.paymentId);
|
|
943
|
+
paymentRequest.onsuccess = () => {
|
|
944
|
+
const payment = paymentRequest.result;
|
|
945
|
+
if (payment) {
|
|
946
|
+
const paymentWithMetadata = this._mergePaymentMetadata(payment, metadata);
|
|
947
|
+
|
|
948
|
+
if (!result[parentId]) {
|
|
949
|
+
result[parentId] = [];
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// Fetch lnurl receive metadata if applicable
|
|
953
|
+
this._fetchLnurlReceiveMetadata(paymentWithMetadata, lnurlReceiveMetadataStore)
|
|
954
|
+
.then((mergedPayment) => {
|
|
955
|
+
result[parentId].push(mergedPayment);
|
|
956
|
+
})
|
|
957
|
+
.catch(() => {
|
|
958
|
+
result[parentId].push(paymentWithMetadata);
|
|
959
|
+
})
|
|
960
|
+
.finally(() => {
|
|
961
|
+
processed++;
|
|
962
|
+
if (processed === fetchedMetadata.length) {
|
|
963
|
+
// Sort each parent's children by timestamp
|
|
964
|
+
for (const parentId of Object.keys(result)) {
|
|
965
|
+
result[parentId].sort((a, b) => a.timestamp - b.timestamp);
|
|
966
|
+
}
|
|
967
|
+
resolve(result);
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
} else {
|
|
971
|
+
processed++;
|
|
972
|
+
if (processed === fetchedMetadata.length) {
|
|
973
|
+
resolve(result);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
paymentRequest.onerror = () => {
|
|
978
|
+
processed++;
|
|
979
|
+
if (processed === fetchedMetadata.length) {
|
|
980
|
+
resolve(result);
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
const metadata = cursor.value;
|
|
988
|
+
if (metadata.parentPaymentId && parentIdSet.has(metadata.parentPaymentId)) {
|
|
989
|
+
fetchedMetadata.push(metadata);
|
|
990
|
+
}
|
|
991
|
+
cursor.continue();
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
cursorRequest.onerror = () => {
|
|
995
|
+
reject(
|
|
996
|
+
new StorageError(
|
|
997
|
+
`Failed to get payments by parent ids: ${
|
|
998
|
+
cursorRequest.error?.message || "Unknown error"
|
|
999
|
+
}`,
|
|
1000
|
+
cursorRequest.error
|
|
1001
|
+
)
|
|
1002
|
+
);
|
|
1003
|
+
};
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
async insertPaymentMetadata(paymentId, metadata) {
|
|
671
1008
|
if (!this.db) {
|
|
672
1009
|
throw new StorageError("Database not initialized");
|
|
673
1010
|
}
|
|
@@ -676,30 +1013,47 @@ class IndexedDBStorage {
|
|
|
676
1013
|
const transaction = this.db.transaction("payment_metadata", "readwrite");
|
|
677
1014
|
const store = transaction.objectStore("payment_metadata");
|
|
678
1015
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
: null,
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
:
|
|
692
|
-
|
|
1016
|
+
// First get existing record to merge with
|
|
1017
|
+
const getRequest = store.get(paymentId);
|
|
1018
|
+
getRequest.onsuccess = () => {
|
|
1019
|
+
const existing = getRequest.result || {};
|
|
1020
|
+
|
|
1021
|
+
// Use COALESCE-like behavior: new value if non-null, otherwise keep existing
|
|
1022
|
+
const metadataToStore = {
|
|
1023
|
+
paymentId,
|
|
1024
|
+
parentPaymentId: metadata.parentPaymentId ?? existing.parentPaymentId ?? null,
|
|
1025
|
+
lnurlPayInfo: metadata.lnurlPayInfo
|
|
1026
|
+
? JSON.stringify(metadata.lnurlPayInfo)
|
|
1027
|
+
: existing.lnurlPayInfo ?? null,
|
|
1028
|
+
lnurlWithdrawInfo: metadata.lnurlWithdrawInfo
|
|
1029
|
+
? JSON.stringify(metadata.lnurlWithdrawInfo)
|
|
1030
|
+
: existing.lnurlWithdrawInfo ?? null,
|
|
1031
|
+
lnurlDescription: metadata.lnurlDescription ?? existing.lnurlDescription ?? null,
|
|
1032
|
+
conversionInfo: metadata.conversionInfo
|
|
1033
|
+
? JSON.stringify(metadata.conversionInfo)
|
|
1034
|
+
: existing.conversionInfo ?? null,
|
|
1035
|
+
};
|
|
693
1036
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
1037
|
+
const putRequest = store.put(metadataToStore);
|
|
1038
|
+
putRequest.onsuccess = () => resolve();
|
|
1039
|
+
putRequest.onerror = () => {
|
|
1040
|
+
reject(
|
|
1041
|
+
new StorageError(
|
|
1042
|
+
`Failed to set payment metadata for '${paymentId}': ${
|
|
1043
|
+
putRequest.error?.message || "Unknown error"
|
|
1044
|
+
}`,
|
|
1045
|
+
putRequest.error
|
|
1046
|
+
)
|
|
1047
|
+
);
|
|
1048
|
+
};
|
|
1049
|
+
};
|
|
1050
|
+
getRequest.onerror = () => {
|
|
697
1051
|
reject(
|
|
698
1052
|
new StorageError(
|
|
699
|
-
`Failed to
|
|
700
|
-
|
|
1053
|
+
`Failed to get existing payment metadata for '${paymentId}': ${
|
|
1054
|
+
getRequest.error?.message || "Unknown error"
|
|
701
1055
|
}`,
|
|
702
|
-
|
|
1056
|
+
getRequest.error
|
|
703
1057
|
)
|
|
704
1058
|
);
|
|
705
1059
|
};
|
|
@@ -909,9 +1263,9 @@ class IndexedDBStorage {
|
|
|
909
1263
|
request.onerror = () => {
|
|
910
1264
|
reject(
|
|
911
1265
|
new StorageError(
|
|
912
|
-
`Failed to add lnurl metadata for payment hash '${
|
|
913
|
-
|
|
914
|
-
}`,
|
|
1266
|
+
`Failed to add lnurl metadata for payment hash '${
|
|
1267
|
+
item.paymentHash
|
|
1268
|
+
}': ${request.error?.message || "Unknown error"}`,
|
|
915
1269
|
request.error
|
|
916
1270
|
)
|
|
917
1271
|
);
|
|
@@ -926,72 +1280,56 @@ class IndexedDBStorage {
|
|
|
926
1280
|
}
|
|
927
1281
|
|
|
928
1282
|
return new Promise((resolve, reject) => {
|
|
929
|
-
const transaction = this.db.transaction(
|
|
930
|
-
["sync_outgoing", "sync_revision"],
|
|
931
|
-
"readwrite"
|
|
932
|
-
);
|
|
1283
|
+
const transaction = this.db.transaction(["sync_outgoing"], "readwrite");
|
|
933
1284
|
|
|
934
|
-
//
|
|
935
|
-
const
|
|
936
|
-
const
|
|
1285
|
+
// This revision is a local queue id for pending rows, not a server revision.
|
|
1286
|
+
const outgoingStore = transaction.objectStore("sync_outgoing");
|
|
1287
|
+
const getAllOutgoingRequest = outgoingStore.getAll();
|
|
1288
|
+
|
|
1289
|
+
getAllOutgoingRequest.onsuccess = () => {
|
|
1290
|
+
const records = getAllOutgoingRequest.result;
|
|
1291
|
+
let maxOutgoingRevision = BigInt(0);
|
|
1292
|
+
for (const storeRecord of records) {
|
|
1293
|
+
const rev = BigInt(
|
|
1294
|
+
storeRecord.record.localRevision ?? storeRecord.record.revision
|
|
1295
|
+
);
|
|
1296
|
+
if (rev > maxOutgoingRevision) {
|
|
1297
|
+
maxOutgoingRevision = rev;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
const nextRevision = maxOutgoingRevision + BigInt(1);
|
|
937
1301
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
revision:
|
|
1302
|
+
const storeRecord = {
|
|
1303
|
+
type: record.id.type,
|
|
1304
|
+
dataId: record.id.dataId,
|
|
1305
|
+
revision: Number(nextRevision),
|
|
1306
|
+
record: {
|
|
1307
|
+
...record,
|
|
1308
|
+
localRevision: nextRevision,
|
|
1309
|
+
},
|
|
942
1310
|
};
|
|
943
|
-
const nextRevision = BigInt(revisionData.revision) + BigInt(1);
|
|
944
|
-
|
|
945
|
-
// Update the revision
|
|
946
|
-
const updateRequest = revisionStore.put({
|
|
947
|
-
id: 1,
|
|
948
|
-
revision: nextRevision.toString(),
|
|
949
|
-
});
|
|
950
1311
|
|
|
951
|
-
|
|
952
|
-
const outgoingStore = transaction.objectStore("sync_outgoing");
|
|
953
|
-
|
|
954
|
-
const storeRecord = {
|
|
955
|
-
type: record.id.type,
|
|
956
|
-
dataId: record.id.dataId,
|
|
957
|
-
revision: Number(nextRevision),
|
|
958
|
-
record: {
|
|
959
|
-
...record,
|
|
960
|
-
revision: nextRevision,
|
|
961
|
-
},
|
|
962
|
-
};
|
|
963
|
-
|
|
964
|
-
const addRequest = outgoingStore.add(storeRecord);
|
|
1312
|
+
const addRequest = outgoingStore.add(storeRecord);
|
|
965
1313
|
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
resolve(nextRevision);
|
|
970
|
-
};
|
|
971
|
-
};
|
|
972
|
-
|
|
973
|
-
addRequest.onerror = (event) => {
|
|
974
|
-
reject(
|
|
975
|
-
new StorageError(
|
|
976
|
-
`Failed to add outgoing change: ${event.target.error.message}`
|
|
977
|
-
)
|
|
978
|
-
);
|
|
1314
|
+
addRequest.onsuccess = () => {
|
|
1315
|
+
transaction.oncomplete = () => {
|
|
1316
|
+
resolve(nextRevision);
|
|
979
1317
|
};
|
|
980
1318
|
};
|
|
981
1319
|
|
|
982
|
-
|
|
1320
|
+
addRequest.onerror = (event) => {
|
|
983
1321
|
reject(
|
|
984
1322
|
new StorageError(
|
|
985
|
-
`Failed to
|
|
1323
|
+
`Failed to add outgoing change: ${event.target.error.message}`
|
|
986
1324
|
)
|
|
987
1325
|
);
|
|
988
1326
|
};
|
|
989
1327
|
};
|
|
990
1328
|
|
|
991
|
-
|
|
1329
|
+
getAllOutgoingRequest.onerror = (event) => {
|
|
992
1330
|
reject(
|
|
993
1331
|
new StorageError(
|
|
994
|
-
`Failed to get
|
|
1332
|
+
`Failed to get outgoing records: ${event.target.error.message}`
|
|
995
1333
|
)
|
|
996
1334
|
);
|
|
997
1335
|
};
|
|
@@ -1004,23 +1342,24 @@ class IndexedDBStorage {
|
|
|
1004
1342
|
});
|
|
1005
1343
|
}
|
|
1006
1344
|
|
|
1007
|
-
async syncCompleteOutgoingSync(record) {
|
|
1345
|
+
async syncCompleteOutgoingSync(record, localRevision) {
|
|
1008
1346
|
if (!this.db) {
|
|
1009
1347
|
throw new StorageError("Database not initialized");
|
|
1010
1348
|
}
|
|
1011
1349
|
|
|
1012
1350
|
return new Promise((resolve, reject) => {
|
|
1013
1351
|
const transaction = this.db.transaction(
|
|
1014
|
-
["sync_outgoing", "sync_state"],
|
|
1352
|
+
["sync_outgoing", "sync_state", "sync_revision"],
|
|
1015
1353
|
"readwrite"
|
|
1016
1354
|
);
|
|
1017
1355
|
const outgoingStore = transaction.objectStore("sync_outgoing");
|
|
1018
1356
|
const stateStore = transaction.objectStore("sync_state");
|
|
1357
|
+
const revisionStore = transaction.objectStore("sync_revision");
|
|
1019
1358
|
|
|
1020
1359
|
const deleteRequest = outgoingStore.delete([
|
|
1021
1360
|
record.id.type,
|
|
1022
1361
|
record.id.dataId,
|
|
1023
|
-
Number(
|
|
1362
|
+
Number(localRevision),
|
|
1024
1363
|
]);
|
|
1025
1364
|
|
|
1026
1365
|
deleteRequest.onsuccess = () => {
|
|
@@ -1030,7 +1369,25 @@ class IndexedDBStorage {
|
|
|
1030
1369
|
record: record,
|
|
1031
1370
|
};
|
|
1032
1371
|
stateStore.put(stateRecord);
|
|
1033
|
-
|
|
1372
|
+
|
|
1373
|
+
// Update sync_revision to track the highest known revision
|
|
1374
|
+
const getRevisionRequest = revisionStore.get(1);
|
|
1375
|
+
getRevisionRequest.onsuccess = () => {
|
|
1376
|
+
const current = getRevisionRequest.result || { id: 1, revision: "0" };
|
|
1377
|
+
const currentRevision = BigInt(current.revision);
|
|
1378
|
+
const recordRevision = BigInt(record.revision);
|
|
1379
|
+
if (recordRevision > currentRevision) {
|
|
1380
|
+
revisionStore.put({ id: 1, revision: recordRevision.toString() });
|
|
1381
|
+
}
|
|
1382
|
+
resolve();
|
|
1383
|
+
};
|
|
1384
|
+
getRevisionRequest.onerror = (event) => {
|
|
1385
|
+
reject(
|
|
1386
|
+
new StorageError(
|
|
1387
|
+
`Failed to update sync revision: ${event.target.error.message}`
|
|
1388
|
+
)
|
|
1389
|
+
);
|
|
1390
|
+
};
|
|
1034
1391
|
};
|
|
1035
1392
|
|
|
1036
1393
|
deleteRequest.onerror = (event) => {
|
|
@@ -1067,7 +1424,11 @@ class IndexedDBStorage {
|
|
|
1067
1424
|
const cursor = event.target.result;
|
|
1068
1425
|
if (cursor && count < limit) {
|
|
1069
1426
|
const storeRecord = cursor.value;
|
|
1070
|
-
const change =
|
|
1427
|
+
const change = {
|
|
1428
|
+
...storeRecord.record,
|
|
1429
|
+
localRevision:
|
|
1430
|
+
storeRecord.record.localRevision ?? storeRecord.record.revision,
|
|
1431
|
+
};
|
|
1071
1432
|
|
|
1072
1433
|
// Look up parent record if it exists
|
|
1073
1434
|
const stateRequest = stateStore.get([
|
|
@@ -1207,110 +1568,6 @@ class IndexedDBStorage {
|
|
|
1207
1568
|
});
|
|
1208
1569
|
}
|
|
1209
1570
|
|
|
1210
|
-
async syncRebasePendingOutgoingRecords(revision) {
|
|
1211
|
-
if (!this.db) {
|
|
1212
|
-
throw new StorageError("Database not initialized");
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
return new Promise((resolve, reject) => {
|
|
1216
|
-
const transaction = this.db.transaction(
|
|
1217
|
-
["sync_outgoing", "sync_revision"],
|
|
1218
|
-
"readwrite"
|
|
1219
|
-
);
|
|
1220
|
-
const outgoingStore = transaction.objectStore("sync_outgoing");
|
|
1221
|
-
const revisionStore = transaction.objectStore("sync_revision");
|
|
1222
|
-
|
|
1223
|
-
// Get the last revision from sync_revision table
|
|
1224
|
-
const getRevisionRequest = revisionStore.get(1);
|
|
1225
|
-
|
|
1226
|
-
getRevisionRequest.onsuccess = () => {
|
|
1227
|
-
const revisionData = getRevisionRequest.result || {
|
|
1228
|
-
id: 1,
|
|
1229
|
-
revision: "0",
|
|
1230
|
-
};
|
|
1231
|
-
const lastRevision = BigInt(revisionData.revision);
|
|
1232
|
-
|
|
1233
|
-
// Calculate the difference
|
|
1234
|
-
const diff = revision - lastRevision;
|
|
1235
|
-
|
|
1236
|
-
if (diff <= BigInt(0)) {
|
|
1237
|
-
// No rebase needed
|
|
1238
|
-
resolve();
|
|
1239
|
-
return;
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
// Get all records from sync_outgoing and update their revisions
|
|
1243
|
-
const getAllRequest = outgoingStore.getAll();
|
|
1244
|
-
|
|
1245
|
-
getAllRequest.onsuccess = () => {
|
|
1246
|
-
const records = getAllRequest.result;
|
|
1247
|
-
let updatesCompleted = 0;
|
|
1248
|
-
|
|
1249
|
-
if (records.length === 0) {
|
|
1250
|
-
resolve();
|
|
1251
|
-
return;
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
for (const storeRecord of records) {
|
|
1255
|
-
// Delete the old record
|
|
1256
|
-
const oldKey = [
|
|
1257
|
-
storeRecord.type,
|
|
1258
|
-
storeRecord.dataId,
|
|
1259
|
-
storeRecord.revision,
|
|
1260
|
-
];
|
|
1261
|
-
outgoingStore.delete(oldKey);
|
|
1262
|
-
|
|
1263
|
-
// Update revision in both the key and the nested record
|
|
1264
|
-
const newRevision = storeRecord.record.revision + diff;
|
|
1265
|
-
const updatedRecord = {
|
|
1266
|
-
type: storeRecord.type,
|
|
1267
|
-
dataId: storeRecord.dataId,
|
|
1268
|
-
revision: Number(newRevision),
|
|
1269
|
-
record: {
|
|
1270
|
-
...storeRecord.record,
|
|
1271
|
-
revision: newRevision,
|
|
1272
|
-
},
|
|
1273
|
-
};
|
|
1274
|
-
|
|
1275
|
-
// Add the updated record
|
|
1276
|
-
const putRequest = outgoingStore.put(updatedRecord);
|
|
1277
|
-
|
|
1278
|
-
putRequest.onsuccess = () => {
|
|
1279
|
-
updatesCompleted++;
|
|
1280
|
-
if (updatesCompleted === records.length) {
|
|
1281
|
-
resolve();
|
|
1282
|
-
}
|
|
1283
|
-
};
|
|
1284
|
-
|
|
1285
|
-
putRequest.onerror = (event) => {
|
|
1286
|
-
reject(
|
|
1287
|
-
new StorageError(
|
|
1288
|
-
`Failed to rebase outgoing record: ${event.target.error.message}`
|
|
1289
|
-
)
|
|
1290
|
-
);
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1293
|
-
};
|
|
1294
|
-
|
|
1295
|
-
getAllRequest.onerror = (event) => {
|
|
1296
|
-
reject(
|
|
1297
|
-
new StorageError(
|
|
1298
|
-
`Failed to get outgoing records for rebase: ${event.target.error.message}`
|
|
1299
|
-
)
|
|
1300
|
-
);
|
|
1301
|
-
};
|
|
1302
|
-
};
|
|
1303
|
-
|
|
1304
|
-
getRevisionRequest.onerror = (event) => {
|
|
1305
|
-
reject(
|
|
1306
|
-
new StorageError(
|
|
1307
|
-
`Failed to get last revision: ${event.target.error.message}`
|
|
1308
|
-
)
|
|
1309
|
-
);
|
|
1310
|
-
};
|
|
1311
|
-
});
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
1571
|
async syncGetIncomingRecords(limit) {
|
|
1315
1572
|
if (!this.db) {
|
|
1316
1573
|
throw new StorageError("Database not initialized");
|
|
@@ -1400,7 +1657,11 @@ class IndexedDBStorage {
|
|
|
1400
1657
|
const cursor = event.target.result;
|
|
1401
1658
|
if (cursor) {
|
|
1402
1659
|
const storeRecord = cursor.value;
|
|
1403
|
-
const change =
|
|
1660
|
+
const change = {
|
|
1661
|
+
...storeRecord.record,
|
|
1662
|
+
localRevision:
|
|
1663
|
+
storeRecord.record.localRevision ?? storeRecord.record.revision,
|
|
1664
|
+
};
|
|
1404
1665
|
|
|
1405
1666
|
// Get the parent record
|
|
1406
1667
|
const stateRequest = stateStore.get([
|
|
@@ -1446,8 +1707,9 @@ class IndexedDBStorage {
|
|
|
1446
1707
|
}
|
|
1447
1708
|
|
|
1448
1709
|
return new Promise((resolve, reject) => {
|
|
1449
|
-
const transaction = this.db.transaction(["sync_state"], "readwrite");
|
|
1710
|
+
const transaction = this.db.transaction(["sync_state", "sync_revision"], "readwrite");
|
|
1450
1711
|
const stateStore = transaction.objectStore("sync_state");
|
|
1712
|
+
const revisionStore = transaction.objectStore("sync_revision");
|
|
1451
1713
|
|
|
1452
1714
|
const storeRecord = {
|
|
1453
1715
|
type: record.id.type,
|
|
@@ -1458,7 +1720,24 @@ class IndexedDBStorage {
|
|
|
1458
1720
|
const request = stateStore.put(storeRecord);
|
|
1459
1721
|
|
|
1460
1722
|
request.onsuccess = () => {
|
|
1461
|
-
|
|
1723
|
+
// Update sync_revision to track the highest known revision
|
|
1724
|
+
const getRevisionRequest = revisionStore.get(1);
|
|
1725
|
+
getRevisionRequest.onsuccess = () => {
|
|
1726
|
+
const current = getRevisionRequest.result || { id: 1, revision: "0" };
|
|
1727
|
+
const currentRevision = BigInt(current.revision);
|
|
1728
|
+
const incomingRevision = BigInt(record.revision);
|
|
1729
|
+
if (incomingRevision > currentRevision) {
|
|
1730
|
+
revisionStore.put({ id: 1, revision: incomingRevision.toString() });
|
|
1731
|
+
}
|
|
1732
|
+
resolve();
|
|
1733
|
+
};
|
|
1734
|
+
getRevisionRequest.onerror = (event) => {
|
|
1735
|
+
reject(
|
|
1736
|
+
new StorageError(
|
|
1737
|
+
`Failed to update sync revision: ${event.target.error.message}`
|
|
1738
|
+
)
|
|
1739
|
+
);
|
|
1740
|
+
};
|
|
1462
1741
|
};
|
|
1463
1742
|
|
|
1464
1743
|
request.onerror = (event) => {
|
|
@@ -1502,7 +1781,10 @@ class IndexedDBStorage {
|
|
|
1502
1781
|
}
|
|
1503
1782
|
|
|
1504
1783
|
// Filter by payment details
|
|
1505
|
-
if (
|
|
1784
|
+
if (
|
|
1785
|
+
request.paymentDetailsFilter &&
|
|
1786
|
+
request.paymentDetailsFilter.length > 0
|
|
1787
|
+
) {
|
|
1506
1788
|
let details = null;
|
|
1507
1789
|
|
|
1508
1790
|
// Parse details if it's a string (stored in IndexedDB)
|
|
@@ -1533,7 +1815,9 @@ class IndexedDBStorage {
|
|
|
1533
1815
|
if (
|
|
1534
1816
|
details.type !== "spark" ||
|
|
1535
1817
|
!details.htlcDetails ||
|
|
1536
|
-
!paymentDetailsFilter.htlcStatus.includes(
|
|
1818
|
+
!paymentDetailsFilter.htlcStatus.includes(
|
|
1819
|
+
details.htlcDetails.status
|
|
1820
|
+
)
|
|
1537
1821
|
) {
|
|
1538
1822
|
continue;
|
|
1539
1823
|
}
|
|
@@ -1570,11 +1854,23 @@ class IndexedDBStorage {
|
|
|
1570
1854
|
continue;
|
|
1571
1855
|
}
|
|
1572
1856
|
}
|
|
1857
|
+
// Filter by token transaction type
|
|
1858
|
+
if (
|
|
1859
|
+
paymentDetailsFilter.type === "token" &&
|
|
1860
|
+
paymentDetailsFilter.txType != null
|
|
1861
|
+
) {
|
|
1862
|
+
if (
|
|
1863
|
+
details.type !== "token" ||
|
|
1864
|
+
details.txType !== paymentDetailsFilter.txType
|
|
1865
|
+
) {
|
|
1866
|
+
continue;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1573
1869
|
|
|
1574
1870
|
paymentDetailsFilterMatches = true;
|
|
1575
1871
|
break;
|
|
1576
1872
|
}
|
|
1577
|
-
|
|
1873
|
+
|
|
1578
1874
|
if (!paymentDetailsFilterMatches) {
|
|
1579
1875
|
return false;
|
|
1580
1876
|
}
|
|
@@ -1706,7 +2002,11 @@ class IndexedDBStorage {
|
|
|
1706
2002
|
|
|
1707
2003
|
_fetchLnurlReceiveMetadata(payment, lnurlReceiveMetadataStore) {
|
|
1708
2004
|
// Only fetch for lightning payments with a payment hash
|
|
1709
|
-
if (
|
|
2005
|
+
if (
|
|
2006
|
+
!payment.details ||
|
|
2007
|
+
payment.details.type !== "lightning" ||
|
|
2008
|
+
!payment.details.paymentHash
|
|
2009
|
+
) {
|
|
1710
2010
|
return Promise.resolve(payment);
|
|
1711
2011
|
}
|
|
1712
2012
|
|
|
@@ -1715,11 +2015,17 @@ class IndexedDBStorage {
|
|
|
1715
2015
|
}
|
|
1716
2016
|
|
|
1717
2017
|
return new Promise((resolve, reject) => {
|
|
1718
|
-
const lnurlReceiveRequest = lnurlReceiveMetadataStore.get(
|
|
1719
|
-
|
|
2018
|
+
const lnurlReceiveRequest = lnurlReceiveMetadataStore.get(
|
|
2019
|
+
payment.details.paymentHash
|
|
2020
|
+
);
|
|
2021
|
+
|
|
1720
2022
|
lnurlReceiveRequest.onsuccess = () => {
|
|
1721
2023
|
const lnurlReceiveMetadata = lnurlReceiveRequest.result;
|
|
1722
|
-
if (
|
|
2024
|
+
if (
|
|
2025
|
+
lnurlReceiveMetadata &&
|
|
2026
|
+
(lnurlReceiveMetadata.nostrZapRequest ||
|
|
2027
|
+
lnurlReceiveMetadata.senderComment)
|
|
2028
|
+
) {
|
|
1723
2029
|
payment.details.lnurlReceiveMetadata = {
|
|
1724
2030
|
nostrZapRequest: lnurlReceiveMetadata.nostrZapRequest || null,
|
|
1725
2031
|
nostrZapReceipt: lnurlReceiveMetadata.nostrZapReceipt || null,
|
|
@@ -1728,7 +2034,7 @@ class IndexedDBStorage {
|
|
|
1728
2034
|
}
|
|
1729
2035
|
resolve(payment);
|
|
1730
2036
|
};
|
|
1731
|
-
|
|
2037
|
+
|
|
1732
2038
|
lnurlReceiveRequest.onerror = () => {
|
|
1733
2039
|
// Continue without lnurlReceiveMetadata if fetch fails
|
|
1734
2040
|
reject(new Error("Failed to fetch lnurl receive metadata"));
|