Skip to content

Commit 8e37523

Browse files
authored
Add hasSynced and waitForFirstSync (#112)
* Add hasSynced and waitForFirstSync * Export sqlite3_common * Add example code for hasSynced Bump version
1 parent b7f4511 commit 8e37523

File tree

8 files changed

+102
-11
lines changed

8 files changed

+102
-11
lines changed

demos/supabase-todolist/lib/models/todo_list.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:powersync/powersync.dart';
12
import 'package:powersync/sqlite3.dart' as sqlite;
23

34
import './todo_item.dart';
@@ -59,6 +60,10 @@ class TodoList {
5960
});
6061
}
6162

63+
static Stream<SyncStatus> watchSyncStatus() {
64+
return db.statusStream;
65+
}
66+
6267
/// Create a new list
6368
static Future<TodoList> create(String name) async {
6469
final results = await db.execute('''

demos/supabase-todolist/lib/widgets/lists_page.dart

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ class ListsWidget extends StatefulWidget {
5252

5353
class _ListsWidgetState extends State<ListsWidget> {
5454
List<TodoList> _data = [];
55+
bool hasSynced = false;
5556
StreamSubscription? _subscription;
57+
StreamSubscription? _syncStatusSubscription;
5658

5759
_ListsWidgetState();
5860

@@ -68,21 +70,32 @@ class _ListsWidgetState extends State<ListsWidget> {
6870
_data = data;
6971
});
7072
});
73+
_syncStatusSubscription = TodoList.watchSyncStatus().listen((status) {
74+
if (!context.mounted) {
75+
return;
76+
}
77+
setState(() {
78+
hasSynced = status.hasSynced ?? false;
79+
});
80+
});
7181
}
7282

7383
@override
7484
void dispose() {
7585
super.dispose();
7686
_subscription?.cancel();
87+
_syncStatusSubscription?.cancel();
7788
}
7889

7990
@override
8091
Widget build(BuildContext context) {
81-
return ListView(
82-
padding: const EdgeInsets.symmetric(vertical: 8.0),
83-
children: _data.map((list) {
84-
return ListItemWidget(list: list);
85-
}).toList(),
86-
);
92+
return !hasSynced
93+
? const Text("Busy with sync...")
94+
: ListView(
95+
padding: const EdgeInsets.symmetric(vertical: 8.0),
96+
children: _data.map((list) {
97+
return ListItemWidget(list: list);
98+
}).toList(),
99+
);
87100
}
88101
}

packages/powersync/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 1.5.1
2+
3+
- Adds a hasSynced flag to check if initial data has been synced.
4+
- Adds a waitForFirstSync method to check if the first full sync has completed.
5+
16
## 1.5.0
27

38
- Upgrade minimum Dart SDK constraint to `3.4.0`.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/// Re-exports [sqlite3_common](https://pub.dev/packages/sqlite3) to expose sqlite3_common without
2+
/// adding it as a direct dependency.
3+
library;
4+
5+
export 'package:sqlite_async/sqlite3_common.dart';

packages/powersync/lib/src/powersync_database.dart

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ class PowerSyncDatabase with SqliteQueries implements SqliteConnection {
153153
await database.initialize();
154154
await database.execute('SELECT powersync_init()');
155155
await updateSchema(schema);
156+
await _updateHasSynced();
156157
}
157158

158159
/// Replace the schema with a new version.
@@ -175,6 +176,37 @@ class PowerSyncDatabase with SqliteQueries implements SqliteConnection {
175176
return _initialized;
176177
}
177178

179+
Future<void> _updateHasSynced() async {
180+
const syncedSQL =
181+
'SELECT 1 FROM ps_buckets WHERE last_applied_op > 0 LIMIT 1';
182+
183+
// Query the database to see if any data has been synced.
184+
final result = await database.execute(syncedSQL);
185+
final hasSynced = result.rows.isNotEmpty;
186+
187+
if (hasSynced != currentStatus.hasSynced) {
188+
final status = SyncStatus(hasSynced: hasSynced);
189+
_setStatus(status);
190+
}
191+
}
192+
193+
///
194+
/// returns a [Future] which will resolve once the first full sync has completed.
195+
///
196+
Future<void> waitForFirstSync() async {
197+
if (currentStatus.hasSynced ?? false) {
198+
return;
199+
}
200+
final completer = Completer<void>();
201+
statusStream.listen((result) {
202+
if (result.hasSynced ?? false) {
203+
completer.complete();
204+
}
205+
});
206+
207+
return completer.future;
208+
}
209+
178210
@override
179211
bool get closed {
180212
return database.closed;
@@ -297,8 +329,9 @@ class PowerSyncDatabase with SqliteQueries implements SqliteConnection {
297329

298330
void _setStatus(SyncStatus status) {
299331
if (status != currentStatus) {
300-
currentStatus = status;
301-
_statusStreamController.add(status);
332+
currentStatus = status.copyWith(
333+
hasSynced: status.hasSynced ?? status.lastSyncedAt != null);
334+
_statusStreamController.add(currentStatus);
302335
}
303336
}
304337

packages/powersync/lib/src/streaming_sync.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ class StreamingSyncImplementation {
153153
/// To clear errors, use [_noError] instead of null.
154154
void _updateStatus(
155155
{DateTime? lastSyncedAt,
156+
bool? hasSynced,
156157
bool? connected,
157158
bool? connecting,
158159
bool? downloading,
@@ -164,6 +165,7 @@ class StreamingSyncImplementation {
164165
connected: c,
165166
connecting: !c && (connecting ?? lastStatus.connecting),
166167
lastSyncedAt: lastSyncedAt ?? lastStatus.lastSyncedAt,
168+
hasSynced: hasSynced ?? lastStatus.hasSynced,
167169
downloading: downloading ?? lastStatus.downloading,
168170
uploading: uploading ?? lastStatus.uploading,
169171
uploadError: uploadError == _noError

packages/powersync/lib/src/sync_status.dart

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ class SyncStatus {
2424
/// Currently this is reset to null after a restart.
2525
final DateTime? lastSyncedAt;
2626

27+
/// Indicates whether there has been at least one full sync, if any.
28+
/// Is null when unknown, for example when state is still being loaded from the database.
29+
final bool? hasSynced;
30+
2731
/// Error during uploading.
2832
///
2933
/// Cleared on the next successful upload.
@@ -38,6 +42,7 @@ class SyncStatus {
3842
{this.connected = false,
3943
this.connecting = false,
4044
this.lastSyncedAt,
45+
this.hasSynced,
4146
this.downloading = false,
4247
this.uploading = false,
4348
this.downloadError,
@@ -52,7 +57,30 @@ class SyncStatus {
5257
other.connecting == connecting &&
5358
other.downloadError == downloadError &&
5459
other.uploadError == uploadError &&
55-
other.lastSyncedAt == lastSyncedAt);
60+
other.lastSyncedAt == lastSyncedAt &&
61+
other.hasSynced == hasSynced);
62+
}
63+
64+
SyncStatus copyWith({
65+
bool? connected,
66+
bool? downloading,
67+
bool? uploading,
68+
bool? connecting,
69+
Object? uploadError,
70+
Object? downloadError,
71+
DateTime? lastSyncedAt,
72+
bool? hasSynced,
73+
}) {
74+
return SyncStatus(
75+
connected: connected ?? this.connected,
76+
downloading: downloading ?? this.downloading,
77+
uploading: uploading ?? this.uploading,
78+
connecting: connecting ?? this.connecting,
79+
uploadError: uploadError ?? this.uploadError,
80+
downloadError: downloadError ?? this.downloadError,
81+
lastSyncedAt: lastSyncedAt ?? this.lastSyncedAt,
82+
hasSynced: hasSynced ?? this.hasSynced,
83+
);
5684
}
5785

5886
/// Get the current [downloadError] or [uploadError].
@@ -68,7 +96,7 @@ class SyncStatus {
6896

6997
@override
7098
String toString() {
71-
return "SyncStatus<connected: $connected connecting: $connecting downloading: $downloading uploading: $uploading lastSyncedAt: $lastSyncedAt error: $anyError>";
99+
return "SyncStatus<connected: $connected connecting: $connecting downloading: $downloading uploading: $uploading lastSyncedAt: $lastSyncedAt, hasSynced: $hasSynced, error: $anyError>";
72100
}
73101
}
74102

packages/powersync/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: powersync
2-
version: 1.5.0
2+
version: 1.5.1
33
homepage: https://powersync.com
44
repository: https://github.com/powersync-ja/powersync.dart
55
description: PowerSync Flutter SDK - keep PostgreSQL databases in sync with on-device SQLite databases.

0 commit comments

Comments
 (0)