Skip to content

Commit ff1c4bb

Browse files
committed
More polish
1 parent ec3fd2b commit ff1c4bb

File tree

7 files changed

+30
-21
lines changed

7 files changed

+30
-21
lines changed

demos/local-only-todolist/README.md

+5-7
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ It does the following:
4040

4141
## Configure your PowerSync Instance
4242

43-
Create a new PowerSync instance, connecting to the database of the Supabase project.
43+
Create a new PowerSync instance, connecting to the database of the Supabase project. See instructions [here](https://docs.powersync.com/integration-guides/supabase-+-powersync#connect-powersync-to-your-supabase).
4444

4545
Then deploy the following sync rules:
4646

@@ -60,7 +60,7 @@ Insert the credentials of your Supabase and PowerSync projects into `lib/app_con
6060

6161
## Sign in to the app
6262

63-
Reload the app and sign up or sign in. Once successfully signed in, existing and new data should seamlessly sync with Supabase.
63+
Restart the app and sign up or sign in. Once successfully signed in, existing and new data should seamlessly sync with Supabase.
6464

6565

6666
# How this works
@@ -79,26 +79,24 @@ The downside to this approach is that app queries would need to continuously dif
7979

8080
## Recommended implementation
8181

82-
To keep app queries consistent between the two states, we utilize the [viewName](https://pub.dev/documentation/powersync/latest/powersync/Table/viewName.html) property, which allows overriding the default name of the view that is used in queries.
82+
To keep app queries consistent between the two states, we utilize the [viewName](https://pub.dev/documentation/powersync/latest/powersync/Table/viewName.html) property, which allows overriding the default name of schema views (each table automatically has a corresponding view, defaulting to the table name, which is used in queries).
8383

8484
This looks as follows in the local-only state:
8585

8686
![diagram-1](./assets/local-only-readme-1.png)
8787

88-
The local-only tables (`local_lists` and `local_todos`) have their view names overriden to `listsAlias` and `todosAlias`, and these names are used in queries (e.g. `PowerSync.getAll("SELECT * FROM listsAlias");`). The `lists` and `todos` tables are not used in this state, but will become relevant in the next step.
88+
The local-only tables (`local_lists` and `local_todos`) have their view names overriden to `lists` and `todos`, and these names are used in queries (e.g. `PowerSync.getAll("SELECT * FROM lists");`). The `lists` and `todos` tables, which are the sync-enabled tables without the `localOnly` flag, are not used at this stage, as indicated by their `inactive_synced_` view names.
8989

9090
When the user registers / signs in:
9191

9292
![diagram-2](./assets/local-only-readme-2.png)
9393

94-
The _synced_ tables (`lists` and `todos`) now have their view names overriden to `listsAlias` and `todosAlias`. Note that `updateSchema` must be run to update the view name. See the [schema](./lib/models/schema.dart) for details about this. The app query `PowerSync.getAll("SELECT * FROM listsAlias")` now reads data from the `lists` table.
94+
The _synced_ tables (`lists` and `todos`) now have their view names set to `lists` and `todos`. Note that `updateSchema` must be run to update the view name. See the [schema](./lib/models/schema.dart) for details about this. The app query `PowerSync.getAll("SELECT * FROM lists")` now reads data from the `lists` table.
9595

9696
Finally, copy data from the local-only tables to the synced tables, and delete data from the local-only tables to reduce database size:
9797

9898
![diagram-3](./assets/local-only-readme-3.png)
9999

100-
101-
102100
At this point, being signed in no longer determines which schema should be used, as the user's session expiring and explicitly signing out trigger different behaviors. If the session expires, the user can continue interacting with their data. However, if the user explicitly logs out, all data is cleared, effectively resetting the app. To manage this, an additional local storage mechanism is used to track which schema is currently in use, as seen [here](./lib/models/sync_mode.dart). Note that any other local storage solution would work as long as it's not using the PowerSync database (chicken and egg problem).
103101

104102

Loading
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Enter your Supabase and PowerSync project details.
22
class AppConfig {
3-
static const String supabaseUrl = 'https://foo.supabase.co';
4-
static const String supabaseAnonKey = 'foo';
5-
static const String powersyncUrl = 'https://foo.powersync.journeyapps.com';
6-
}
3+
static const String supabaseUrl = 'https://beaywnuieweznatavpeg.supabase.co';
4+
static const String supabaseAnonKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJlYXl3bnVpZXdlem5hdGF2cGVnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MjU0NTk3OTAsImV4cCI6MjA0MTAzNTc5MH0.sb5_RBESMWbfnq_xaZy00T8Mia9lzY0-BBc7ZovS5t0';
5+
static const String powersyncUrl = 'https://660fe3a331d70135abf2c626.powersync.journeyapps.com';
6+
}

demos/local-only-todolist/lib/models/schema.dart

+11-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import 'package:powersync_flutter_local_only_demo/models/sync_mode.dart';
44
/// This schema design supports a local-only to sync-enabled workflow by managing data
55
/// across two versions of each table: one for local-only use without syncing before a user registers,
66
/// the other for sync-enabled use after the user registers/signs in.
7+
///
8+
/// This is done by utilizing the viewName property to override the default view name
9+
/// of a table.
710
///
811
/// See the README for details.
912
///
@@ -16,16 +19,22 @@ const listsTable = 'lists';
1619
Schema makeSchema({synced = bool}) {
1720
String syncedName(String table) {
1821
if (synced) {
22+
// results in lists, todos
1923
return table;
2024
} else {
25+
// in the local-only mode of the demo
26+
// these tables are not used
2127
return "inactive_synced_$table";
2228
}
2329
}
2430

2531
String localName(String table) {
2632
if (synced) {
33+
// in the sync-enabled mode of the demo
34+
// these tables are not used
2735
return "inactive_local_$table";
2836
} else {
37+
// results in lists, todos
2938
return table;
3039
}
3140
}
@@ -68,10 +77,10 @@ switchToSyncedSchema(PowerSyncDatabase db, String userId) async {
6877
await setSyncEnabled(true);
6978

7079
await db.writeTransaction((tx) async {
71-
// Copy local-only data to the sync-enabled tables/views.
80+
// Copy local-only data to the sync-enabled views.
7281
// This records each operation in the upload queue.
7382
await tx.execute(
74-
'INSERT INTO lists(id, name, created_at, owner_id) SELECT id, name, created_at, ? FROM inactive_local_lists',
83+
'INSERT INTO $listsTable(id, name, created_at, owner_id) SELECT id, name, created_at, ? FROM inactive_local_$listsTable',
7584
[userId]);
7685

7786
await tx.execute(

demos/local-only-todolist/lib/models/todo_list.dart

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'package:powersync_flutter_local_only_demo/models/schema.dart';
2+
13
import 'package:powersync/powersync.dart';
24
import 'package:powersync/sqlite3_common.dart' as sqlite;
35

@@ -39,7 +41,7 @@ class TodoList {
3941
static Stream<List<TodoList>> watchLists() {
4042
// This query is automatically re-run when data in "lists" or "todos" is modified.
4143
return db
42-
.watch('SELECT * FROM lists ORDER BY created_at, id')
44+
.watch('SELECT * FROM $listsTable ORDER BY created_at, id')
4345
.map((results) {
4446
return results.map(TodoList.fromRow).toList(growable: false);
4547
});
@@ -51,8 +53,8 @@ class TodoList {
5153
return db.watch('''
5254
SELECT
5355
*,
54-
(SELECT count() FROM todos WHERE list_id = lists.id AND completed = TRUE) as completed_count,
55-
(SELECT count() FROM todos WHERE list_id = lists.id AND completed = FALSE) as pending_count
56+
(SELECT count() FROM $todosTable WHERE list_id = lists.id AND completed = TRUE) as completed_count,
57+
(SELECT count() FROM $todosTable WHERE list_id = lists.id AND completed = FALSE) as pending_count
5658
FROM lists
5759
ORDER BY created_at
5860
''').map((results) {
@@ -68,7 +70,7 @@ class TodoList {
6870
static Future<TodoList> create(String name) async {
6971
final results = await db.execute('''
7072
INSERT INTO
71-
lists(id, created_at, name, owner_id)
73+
$listsTable(id, created_at, name, owner_id)
7274
VALUES(uuid(), datetime(), ?, ?)
7375
RETURNING *
7476
''', [name, getUserId()]);
@@ -78,28 +80,28 @@ class TodoList {
7880
/// Watch items within this list.
7981
Stream<List<TodoItem>> watchItems() {
8082
return db.watch(
81-
'SELECT * FROM todos WHERE list_id = ? ORDER BY created_at DESC, id',
83+
'SELECT * FROM $todosTable WHERE list_id = ? ORDER BY created_at DESC, id',
8284
parameters: [id]).map((event) {
8385
return event.map(TodoItem.fromRow).toList(growable: false);
8486
});
8587
}
8688

8789
/// Delete this list.
8890
Future<void> delete() async {
89-
await db.execute('DELETE FROM lists WHERE id = ?', [id]);
91+
await db.execute('DELETE FROM $listsTable WHERE id = ?', [id]);
9092
}
9193

9294
/// Find list item.
9395
static Future<TodoList> find(id) async {
94-
final results = await db.get('SELECT * FROM lists WHERE id = ?', [id]);
96+
final results = await db.get('SELECT * FROM $listsTable WHERE id = ?', [id]);
9597
return TodoList.fromRow(results);
9698
}
9799

98100
/// Add a new todo item to this list.
99101
Future<TodoItem> add(String description) async {
100102
final results = await db.execute('''
101103
INSERT INTO
102-
todos(id, created_at, completed, list_id, description, created_by)
104+
$todosTable(id, created_at, completed, list_id, description, created_by)
103105
VALUES(uuid(), datetime(), FALSE, ?, ?, ?)
104106
RETURNING *
105107
''', [id, description, getUserId()]);

0 commit comments

Comments
 (0)