Skip to content

Commit 5e087e2

Browse files
authored
Merge pull request #105 from powersync-ja/feature/sdk-common-watch-overloads
[Update] powersync-sdk-common: watch/onChange API overloads to support callbacks
2 parents 31f056f + 9bf5a76 commit 5e087e2

File tree

13 files changed

+451
-109
lines changed

13 files changed

+451
-109
lines changed

.changeset/clever-phones-hang.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@journeyapps/powersync-react": minor
3+
---
4+
5+
No longer using the async iterator version of the watch method for the usePowerSyncWatchedQuery hook, using the callback version instead.

.changeset/heavy-dogs-work.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"powersync-example": minor
3+
---
4+
5+
Updated/renamed implementation of `attachmentIds` to `onAttachmentIdsChange`, now implements a callback approach instead of AsyncGenerator.

.changeset/slow-years-pull.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@journeyapps/powersync-attachments": major
3+
---
4+
5+
Renamed abstract `attachmentIds` to `onAttachmentIdsChange` in `AbstractAttachmentQueue`, changed to use callback signature instead of an AsyncGenerator.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@journeyapps/powersync-sdk-common": minor
3+
---
4+
5+
Introduced overloaded versions of watch and onChange methods to support a callback approach to handle results and errors alongside the existing AsyncGenerator mechanism.

demos/react-native-supabase-todolist/library/powersync/PhotoAttachmentQueue.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,10 @@ export class PhotoAttachmentQueue extends AbstractAttachmentQueue {
1616
await super.init();
1717
}
1818

19-
async *attachmentIds(): AsyncIterable<string[]> {
20-
for await (const result of this.powersync.watch(
21-
`SELECT photo_id as id FROM ${TODO_TABLE} WHERE photo_id IS NOT NULL`,
22-
[]
23-
)) {
24-
yield result.rows?._array.map((r) => r.id) ?? [];
25-
}
19+
onAttachmentIdsChange(onUpdate: (ids: string[]) => void): void {
20+
this.powersync.watch(`SELECT photo_id as id FROM ${TODO_TABLE} WHERE photo_id IS NOT NULL`, [], {
21+
onResult: (result) => onUpdate(result.rows?._array.map((r) => r.id) ?? [])
22+
});
2623
}
2724

2825
async newAttachmentRecord(record?: Partial<AttachmentRecord>): Promise<AttachmentRecord> {

packages/powersync-attachments/README.md

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,18 @@ import { AbstractAttachmentQueue } from '@journeyapps/powersync-attachments';
6363
export class AttachmentQueue extends AbstractAttachmentQueue {}
6464
```
6565

66-
2. Implement `attachmentIds`, an `AsyncIterator` method to return an array of `string` values of IDs that relate to attachments in your app. We recommend using `PowerSync`'s `watch` query to return the all IDs of attachments in your app.
66+
2. Implement `onAttachmentIdsChange`, which takes in a callback to handle an array of `string` values of IDs that relate to attachments in your app. We recommend using `PowerSync`'s `watch` query to return the all IDs of attachments in your app.
6767

6868
In this example, we query all photos that have been captured as part of an inspection and map these to an array of `string` values.
6969

7070
```javascript
7171
import { AbstractAttachmentQueue } from '@journeyapps/powersync-attachments';
7272

7373
export class AttachmentQueue extends AbstractAttachmentQueue {
74-
async *attachmentIds() {
75-
for await (const result of this.powersync.watch(
76-
`SELECT photo_id as id FROM checklists WHERE photo_id IS NOT NULL`,
77-
[]
78-
)) {
79-
yield result.rows?._array.map((r) => r.id) ?? [];
80-
}
74+
onAttachmentIdsChange(onUpdate) {
75+
this.powersync.watch('SELECT photo_id as id FROM checklists WHERE photo_id IS NOT NULL', [], {
76+
onResult: (result) => onUpdate(result.rows?._array.map((r) => r.id) ?? [])
77+
});
8178
}
8279
}
8380
```

packages/powersync-attachments/src/AbstractAttachmentQueue.ts

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,20 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
5757
}
5858

5959
/**
60-
* Returns an async iterator that yields attachment IDs that need to be synced.
61-
* In most cases this will be a watch query
60+
* Takes in a callback that gets invoked with attachment IDs that need to be synced.
61+
* In most cases this will contain a watch query.
6262
*
63-
* Example:
64-
* for await (const result of powersync.watch('SELECT photo_id as id FROM todos WHERE photo_id IS NOT NULL', [])) {
65-
* yield result.rows?._array.map((r) => r.id) ?? [];
63+
* @example
64+
* ```javascript
65+
* onAttachmentIdsChange(onUpdate) {
66+
* this.powersync.watch('SELECT photo_id as id FROM todos WHERE photo_id IS NOT NULL', [], {
67+
* onResult: (result) => onUpdate(result.rows?._array.map((r) => r.id) ?? [])
68+
* });
6669
* }
70+
* ```
6771
*/
68-
abstract attachmentIds(): AsyncIterable<string[]>;
72+
abstract onAttachmentIdsChange(onUpdate: (ids: string[]) => void): void;
73+
6974

7075
/**
7176
* Create a new AttachmentRecord, this gets called when the attachment id is not found in the database.
@@ -106,7 +111,7 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
106111
}
107112

108113
async watchAttachmentIds() {
109-
for await (const ids of this.attachmentIds()) {
114+
this.onAttachmentIdsChange(async (ids) => {
110115
const _ids = `${ids.map((id) => `'${id}'`).join(',')}`;
111116
console.debug(`Queuing for sync, attachment IDs: [${_ids}]`);
112117

@@ -157,7 +162,7 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
157162
AND
158163
id NOT IN (${ids.map((id) => `'${id}'`).join(',')})`
159164
);
160-
}
165+
});
161166
}
162167

163168
async saveToQueue(record: Omit<AttachmentRecord, 'timestamp'>): Promise<AttachmentRecord> {
@@ -342,8 +347,8 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
342347
return false;
343348
}
344349

345-
async *idsToUpload(): AsyncIterable<string[]> {
346-
for await (const result of this.powersync.watch(
350+
idsToUpload(onResult: (ids: string[]) => void): void {
351+
this.powersync.watch(
347352
`SELECT id
348353
FROM ${this.table}
349354
WHERE
@@ -352,18 +357,17 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
352357
(state = ${AttachmentState.QUEUED_UPLOAD}
353358
OR
354359
state = ${AttachmentState.QUEUED_SYNC})`,
355-
[]
356-
)) {
357-
yield result.rows?._array.map((r) => r.id) || [];
358-
}
360+
[],
361+
{ onResult: (result) => onResult(result.rows?._array.map((r) => r.id) || []) }
362+
);
359363
}
360364

361-
async watchUploads() {
362-
for await (const ids of this.idsToUpload()) {
365+
watchUploads() {
366+
this.idsToUpload(async (ids) => {
363367
if (ids.length > 0) {
364368
await this.uploadRecords();
365369
}
366-
}
370+
})
367371
}
368372

369373
/**
@@ -409,26 +413,25 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
409413
return res.map((r) => r.id);
410414
}
411415

412-
async *idsToDownload(): AsyncIterable<string[]> {
413-
for await (const result of this.powersync.watch(
416+
idsToDownload(onResult: (ids: string[]) => void): void {
417+
this.powersync.watch(
414418
`SELECT id
415419
FROM ${this.table}
416420
WHERE
417421
state = ${AttachmentState.QUEUED_DOWNLOAD}
418422
OR
419423
state = ${AttachmentState.QUEUED_SYNC}`,
420-
[]
421-
)) {
422-
yield result.rows?._array.map((r) => r.id) || [];
423-
}
424+
[],
425+
{ onResult: result => onResult(result.rows?._array.map(r => r.id) || []) }
426+
)
424427
}
425428

426-
async watchDownloads() {
427-
for await (const ids of this.idsToDownload()) {
429+
watchDownloads() {
430+
this.idsToDownload(async (ids) => {
428431
ids.map((id) => this.downloadQueue.add(id));
429-
// No need to await this, the lock will ensure only loop is running at a time
432+
// No need to await this, the lock will ensure only one loop is running at a time
430433
this.downloadRecords();
431-
}
434+
})
432435
}
433436

434437
private async downloadRecords() {

packages/powersync-react/src/hooks/usePowerSyncWatchedQuery.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@ export const usePowerSyncWatchedQuery = <T = any>(
2424
// Abort any previous watches
2525
abortController.current?.abort();
2626
abortController.current = new AbortController();
27-
(async () => {
28-
for await (const result of powerSync.watch(sqlStatement, parameters, {
29-
...options,
30-
signal: abortController.current.signal
31-
})) {
32-
setData(result.rows?._array ?? []);
33-
}
34-
})();
27+
28+
powerSync.watch(sqlStatement, parameters, {
29+
onResult(results) {
30+
setData(results.rows?._array ?? []);
31+
},
32+
}, {
33+
...options,
34+
signal: abortController.current.signal
35+
});
3536

3637
return () => {
3738
abortController.current?.abort();

0 commit comments

Comments
 (0)