-
Notifications
You must be signed in to change notification settings - Fork 27
WIP: Sync progress status #260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
83e90ee
27623d9
bd01bfd
cadd682
720a4e9
9d45ba3
6e66936
8a3dded
a2322df
0ecd906
86623f4
2be5919
c9d7755
ccf67ef
6c7bae4
8271c2a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:powersync/powersync.dart' hide Column; | ||
import 'package:powersync_django_todolist_demo/powersync.dart'; | ||
|
||
/// A widget that shows [child] after a complete sync on the database has | ||
/// completed and a progress bar before that. | ||
class GuardBySync extends StatelessWidget { | ||
final Widget child; | ||
|
||
/// When set, wait only for a complete sync within the [BucketPriority] | ||
/// instead of a full sync. | ||
final BucketPriority? priority; | ||
|
||
const GuardBySync({ | ||
super.key, | ||
required this.child, | ||
this.priority, | ||
}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return StreamBuilder<SyncStatus>( | ||
stream: db.statusStream, | ||
initialData: db.currentStatus, | ||
builder: (context, snapshot) { | ||
final status = snapshot.requireData; | ||
final (didSync, progress) = switch (priority) { | ||
null => ( | ||
status.hasSynced ?? false, | ||
status.downloadProgress?.untilCompletion | ||
), | ||
var priority? => ( | ||
status.statusForPriority(priority).hasSynced ?? false, | ||
status.downloadProgress?.untilPriority(priority) | ||
), | ||
}; | ||
|
||
if (didSync) { | ||
return child; | ||
} else { | ||
return Center( | ||
child: Column( | ||
children: [ | ||
const Text('Busy with sync...'), | ||
LinearProgressIndicator(value: progress?.fraction), | ||
if (progress case final progress?) | ||
Text('${progress.completed} out of ${progress.total}') | ||
], | ||
), | ||
); | ||
} | ||
}, | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:powersync/powersync.dart' hide Column; | ||
import 'package:powersync_flutter_demo/powersync.dart'; | ||
|
||
/// A widget that shows [child] after a complete sync on the database has | ||
/// completed and a progress bar before that. | ||
class GuardBySync extends StatelessWidget { | ||
final Widget child; | ||
|
||
/// When set, wait only for a complete sync within the [BucketPriority] | ||
/// instead of a full sync. | ||
final BucketPriority? priority; | ||
|
||
const GuardBySync({ | ||
super.key, | ||
required this.child, | ||
this.priority, | ||
}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return StreamBuilder<SyncStatus>( | ||
stream: db.statusStream, | ||
initialData: db.currentStatus, | ||
builder: (context, snapshot) { | ||
final status = snapshot.requireData; | ||
final (didSync, progress) = switch (priority) { | ||
null => (status.hasSynced ?? false, status.downloadProgress), | ||
var priority? => ( | ||
status.statusForPriority(priority).hasSynced ?? false, | ||
status.downloadProgress?.untilPriority(priority) | ||
), | ||
}; | ||
|
||
if (didSync) { | ||
return child; | ||
} else { | ||
return Center( | ||
child: Column( | ||
children: [ | ||
const Text('Busy with sync...'), | ||
LinearProgressIndicator(value: progress?.downloadedFraction), | ||
if (progress case final progress?) | ||
Text( | ||
'${progress.downloadedOperations} out of ${progress.totalOperations}') | ||
], | ||
), | ||
); | ||
} | ||
}, | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:powersync/powersync.dart'; | ||
import 'package:powersync_flutter_demo/powersync.dart'; | ||
import 'package:powersync_flutter_demo/widgets/guard_by_sync.dart'; | ||
|
||
import './list_item.dart'; | ||
import './list_item_dialog.dart'; | ||
|
@@ -46,29 +46,23 @@ final class ListsWidget extends StatelessWidget { | |
|
||
@override | ||
Widget build(BuildContext context) { | ||
return FutureBuilder( | ||
future: db.waitForFirstSync(priority: _listsPriority), | ||
builder: (context, snapshot) { | ||
if (snapshot.connectionState == ConnectionState.done) { | ||
return StreamBuilder( | ||
stream: TodoList.watchListsWithStats(), | ||
builder: (context, snapshot) { | ||
if (snapshot.data case final todoLists?) { | ||
return ListView( | ||
padding: const EdgeInsets.symmetric(vertical: 8.0), | ||
children: todoLists.map((list) { | ||
return ListItemWidget(list: list); | ||
}).toList(), | ||
); | ||
} else { | ||
return const CircularProgressIndicator(); | ||
} | ||
}, | ||
); | ||
} else { | ||
return const Text('Busy with sync...'); | ||
} | ||
}, | ||
return GuardBySync( | ||
priority: _listsPriority, | ||
child: StreamBuilder( | ||
stream: TodoList.watchListsWithStats(), | ||
builder: (context, snapshot) { | ||
if (snapshot.data case final todoLists?) { | ||
return ListView( | ||
padding: const EdgeInsets.symmetric(vertical: 8.0), | ||
children: todoLists.map((list) { | ||
return ListItemWidget(list: list); | ||
}).toList(), | ||
); | ||
} else { | ||
return const CircularProgressIndicator(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not important for right now, but is there a good way to combine the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I think we could definitely have some kind of query widget that takes SQL + parameters and then shows:
This might need some more thought put into it to combine it with e.g. drift, but the basic pattern is always the same (guard for first sync, guard for data on stream, show results). I'm not sure if an abstraction is worth it here or if it'll end up complacting the use-sites further, but it's worth trying out. |
||
} | ||
}, | ||
), | ||
); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a side-note, it could be useful to package some of these widgets in a separate lib. Doesn't have to cover all use cases - I'm thinking just making a couple of sync-related widgets available, to make it easier to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a good idea 👍 Given that we already have
package:powersync
as a Flutter-only library, I think it could reasonably live there (or at least be exported from there, we might want apackage:powersync_widgets
to share code between the main package and the SQLCipher one). It just can't be inpackage:powersync_core
so that we can keep using the package without Flutter.