From cea3ae15a67f5c55ab247e5ace36238f6cf6a4e4 Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Tue, 10 Sep 2024 21:08:45 +0300 Subject: [PATCH] wip --- lib/models/nutrition/log_powersync.dart | 68 +++++++++++++++++++++++++ lib/models/schema.dart | 40 ++++++++++++--- lib/models/todo_item.dart | 51 ------------------- lib/powersync.dart | 20 ++++---- 4 files changed, 113 insertions(+), 66 deletions(-) create mode 100644 lib/models/nutrition/log_powersync.dart delete mode 100644 lib/models/todo_item.dart diff --git a/lib/models/nutrition/log_powersync.dart b/lib/models/nutrition/log_powersync.dart new file mode 100644 index 00000000..25819152 --- /dev/null +++ b/lib/models/nutrition/log_powersync.dart @@ -0,0 +1,68 @@ +import 'package:powersync/sqlite3.dart' as sqlite; +import 'package:wger/models/schema.dart'; + +import '../../powersync.dart'; + +/// TodoItem represents a result row of a query on "todos". +/// +/// This class is immutable - methods on this class do not modify the instance +/// directly. Instead, watch or re-query the data to get the updated item. +/// confirm how the watch works. this seems like a weird pattern +class TodoItem { + final String id; + final String description; + final String? photoId; + final bool completed; + + TodoItem( + {required this.id, + required this.description, + required this.completed, + required this.photoId}); + + factory TodoItem.fromRow(sqlite.Row row) { + return TodoItem( + id: row['id'], + description: row['description'], + photoId: row['photo_id'], + completed: row['completed'] == 1); + } + + Future toggle() async { + if (completed) { + await db.execute( + 'UPDATE $logItemsTable SET completed = FALSE, completed_by = NULL, completed_at = NULL WHERE id = ?', + [id]); + } else { + await db.execute( + 'UPDATE $logItemsTable SET completed = TRUE, completed_by = ?, completed_at = datetime() WHERE id = ?', + [await getUserId(), id]); + } + } + + Future delete() async { + await db.execute('DELETE FROM $logItemsTable WHERE id = ?', [id]); + } + + static Future addPhoto(String photoId, String id) async { + await db.execute('UPDATE $logItemsTable SET photo_id = ? WHERE id = ?', [photoId, id]); + } +} +/* + static Stream> watchLists() { + // This query is automatically re-run when data in "lists" or "todos" is modified. + return db.watch('SELECT * FROM lists ORDER BY created_at, id').map((results) { + return results.map(TodoList.fromRow).toList(growable: false); + }); + } + + static Future create(String name) async { + final results = await db.execute(''' + INSERT INTO + lists(id, created_at, name, owner_id) + VALUES(uuid(), datetime(), ?, ?) + RETURNING * + ''', [name, await getUserId()]); + return TodoList.fromRow(results.first); + } + */ \ No newline at end of file diff --git a/lib/models/schema.dart b/lib/models/schema.dart index b4c03784..f8f68ea2 100644 --- a/lib/models/schema.dart +++ b/lib/models/schema.dart @@ -2,9 +2,41 @@ import 'package:powersync/powersync.dart'; const todosTable = 'todos'; const musclesTable = 'muscles'; +const logItemsTable = 'nutrition_logitem'; -// these are the same ones as in postgres, except for 'id' +/* +Postgres: +wger@localhost:wger> \d nutrition_logitem; ++----------------+--------------------------+--------------------------------------------+ +| Column | Type | Modifiers | +|----------------+--------------------------+--------------------------------------------| +| id | integer | not null generated by default as identity | +| datetime | timestamp with time zone | not null | +| comment | text | | +| amount | numeric(6,2) | not null | +| ingredient_id | integer | not null | +| plan_id | integer | not null | +| weight_unit_id | integer | | +| meal_id | integer | | ++----------------+--------------------------+--------------------------------------------+ +*/ +// these are the same ones as in postgres, except for 'id', because that is implied Schema schema = const Schema([ + Table( + logItemsTable, + [ + Column.text('datetime'), + Column.text('comment'), + Column.integer('amount'), + Column.integer('ingredient_id'), + Column.integer('plan_id'), + Column.integer('weight_unit_id'), + Column.integer('meal_id'), + ], + indexes: [ + // Index('plan', [IndexedColumn('plan_id')]) + ], + ), Table( todosTable, [ @@ -31,11 +63,7 @@ Schema schema = const Schema([ ), Table( 'muscles', - [ - Column.text('name'), - Column.text('name_en'), - Column.text('is_front'), - ], + [Column.text('name'), Column.text('name_en'), Column.text('is_front')], ), ]); diff --git a/lib/models/todo_item.dart b/lib/models/todo_item.dart deleted file mode 100644 index 1b6e6e4e..00000000 --- a/lib/models/todo_item.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:powersync/sqlite3.dart' as sqlite; -import 'package:wger/models/schema.dart'; -import 'package:wger/powersync.dart'; - -/// TodoItem represents a result row of a query on "todos". -/// -/// This class is immutable - methods on this class do not modify the instance -/// directly. Instead, watch or re-query the data to get the updated item. -/// confirm how the watch works. this seems like a weird pattern -class TodoItem { - final String id; - final String description; - final String? photoId; - final bool completed; - - TodoItem({ - required this.id, - required this.description, - required this.completed, - required this.photoId, - }); - - factory TodoItem.fromRow(sqlite.Row row) { - return TodoItem( - id: row['id'], - description: row['description'], - photoId: row['photo_id'], - completed: row['completed'] == 1, - ); - } - - Future toggle() async { - if (completed) { - await db.execute( - 'UPDATE $todosTable SET completed = FALSE, completed_by = NULL, completed_at = NULL WHERE id = ?', - [id]); - } else { - await db.execute( - 'UPDATE $todosTable SET completed = TRUE, completed_by = ?, completed_at = datetime() WHERE id = ?', - [await getUserId(), id]); - } - } - - Future delete() async { - await db.execute('DELETE FROM $todosTable WHERE id = ?', [id]); - } - - static Future addPhoto(String photoId, String id) async { - await db.execute('UPDATE $todosTable SET photo_id = ? WHERE id = ?', [photoId, id]); - } -} diff --git a/lib/powersync.dart b/lib/powersync.dart index bdfcdfc9..df457aa5 100644 --- a/lib/powersync.dart +++ b/lib/powersync.dart @@ -52,7 +52,7 @@ class DjangoConnector extends PowerSyncBackendConnector { } try { - for (var op in transaction.crud) { + for (final op in transaction.crud) { final record = { 'table': op.table, 'data': {'id': op.id, ...?op.opData}, @@ -86,9 +86,14 @@ late final PowerSyncDatabase db; // Hacky flag to ensure the database is only initialized once, better to do this with listeners bool _dbInitialized = false; +/// id of the user currently logged in +Future getUserId() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('id'); +} + Future isLoggedIn() async { - final prefs = await SharedPreferences.getInstance(); // Initialize SharedPreferences - final userId = prefs.getString('id'); + final userId = await getUserId(); return userId != null; } @@ -113,6 +118,9 @@ Future openDatabase() async { // Otherwise, connect once logged in. currentConnector = DjangoConnector(db); db.connect(connector: currentConnector); + + // TODO: should we respond to login state changing? like here: + // https://www.powersync.com/blog/flutter-tutorial-building-an-offline-first-chat-app-with-supabase-and-powersync#implement-auth-methods } } @@ -120,9 +128,3 @@ Future openDatabase() async { Future logout() async { await db.disconnectAndClear(); } - -/// id of the user currently logged in -Future getUserId() async { - final prefs = await SharedPreferences.getInstance(); - return prefs.getString('id'); -}