1
1
import 'package:collection/collection.dart' ;
2
+ import 'package:meta/meta.dart' ;
2
3
3
4
final class SyncStatus {
4
5
/// true if currently connected.
@@ -18,6 +19,12 @@ final class SyncStatus {
18
19
/// This is only true when [connected] is also true.
19
20
final bool downloading;
20
21
22
+ /// A realtime progress report on how many operations have been downloaded and
23
+ /// how many are necessary in total to complete the next sync iteration.
24
+ ///
25
+ /// This field is only set when [downloading] is also true.
26
+ final SyncDownloadProgress ? downloadProgress;
27
+
21
28
/// true if uploading changes
22
29
final bool uploading;
23
30
@@ -47,6 +54,7 @@ final class SyncStatus {
47
54
this .connecting = false ,
48
55
this .lastSyncedAt,
49
56
this .hasSynced,
57
+ this .downloadProgress,
50
58
this .downloading = false ,
51
59
this .uploading = false ,
52
60
this .downloadError,
@@ -202,3 +210,83 @@ class UploadQueueStats {
202
210
}
203
211
}
204
212
}
213
+
214
+ @internal
215
+ typedef OperationCounter = ({BucketPriority priority, int opCount});
216
+
217
+ @internal
218
+ final class InternalSyncDownloadProgress {
219
+ final List <OperationCounter > downloaded;
220
+ final List <OperationCounter > target;
221
+
222
+ final int _totalDownloaded;
223
+ final int _totalTarget;
224
+
225
+ InternalSyncDownloadProgress (this .downloaded, this .target)
226
+ : _totalDownloaded = downloaded.map ((e) => e.opCount).sum,
227
+ _totalTarget = target.map ((e) => e.opCount).sum;
228
+
229
+ static int sumInPriority (
230
+ List <OperationCounter > counters, BucketPriority priority) {
231
+ return counters
232
+ .where ((e) => e.priority >= priority)
233
+ .map ((e) => e.opCount)
234
+ .sum;
235
+ }
236
+ }
237
+
238
+ /// Provides realtime progress about how PowerSync is downloading rows.
239
+ ///
240
+ /// The reported progress always reflects the status towards the end of a
241
+ /// sync iteration (after which a consistent snapshot of all buckets is
242
+ /// available locally). Note that [downloaded] starts at `0` every time an
243
+ /// iteration begins.
244
+ /// This has an effect when iterations are interrupted. Consider this flow
245
+ /// as an example:
246
+ ///
247
+ /// 1. The client comes online for the first time and has to synchronize a
248
+ /// large amount of rows (say 100k). Here, [downloaded] starts at `0` and
249
+ /// [total] would be the `100,000` rows.
250
+ /// 2. The client makes some progress, so that [downloaded] is perhaps
251
+ /// `60,000`.
252
+ /// 3. The client briefly looses connectivity.
253
+ /// 4. Back online, a new sync iteration starts. This means that [downloaded]
254
+ /// is reset to `0`. However, since half of the target has already been
255
+ /// downloaded in the earlier iteration, [total] is now set to `40,000` to
256
+ /// reflect the remaining rows to download in the new iteration.
257
+ extension type SyncDownloadProgress ._(InternalSyncDownloadProgress _internal) {
258
+ /// The amount of operations that have been downloaded in the current sync
259
+ /// iteration.
260
+ ///
261
+ /// This number always starts at zero as [SyncStatus.downloading] changes
262
+ /// from `false` to `true` .
263
+ int get downloaded => _internal._totalDownloaded;
264
+
265
+ /// The total amount of operations expected for this sync operation.
266
+ int get total => _internal._totalTarget;
267
+
268
+ /// The fraction of [total] operations that have already been [downloaded] , as
269
+ /// a number between 0 and 1.
270
+ double get progress => _internal._totalDownloaded / _internal._totalTarget;
271
+
272
+ int downloadedFor (BucketPriority priority) {
273
+ return InternalSyncDownloadProgress .sumInPriority (
274
+ _internal.downloaded, priority);
275
+ }
276
+
277
+ int totalFor (BucketPriority priority) {
278
+ return InternalSyncDownloadProgress .sumInPriority (
279
+ _internal.target, priority);
280
+ }
281
+
282
+ double progressFor (BucketPriority priority) {
283
+ final downloaded = downloadedFor (priority);
284
+ final total = totalFor (priority);
285
+
286
+ if (total == 0 ) {
287
+ return 0 ;
288
+ }
289
+
290
+ return downloaded / total;
291
+ }
292
+ }
0 commit comments