|
| 1 | +import 'package:powersync/sqlite3.dart' as sqlite; |
| 2 | + |
| 3 | +import './todo_item.dart'; |
| 4 | +import '../powersync.dart'; |
| 5 | + |
| 6 | +/// TodoList represents a result row of a query on "lists". |
| 7 | +/// |
| 8 | +/// This class is immutable - methods on this class do not modify the instance |
| 9 | +/// directly. Instead, watch or re-query the data to get the updated list. |
| 10 | +class TodoList { |
| 11 | + /// List id (UUID). |
| 12 | + final String id; |
| 13 | + |
| 14 | + /// Descriptive name. |
| 15 | + final String name; |
| 16 | + |
| 17 | + /// Number of completed todos in this list. |
| 18 | + final int? completedCount; |
| 19 | + |
| 20 | + /// Number of pending todos in this list. |
| 21 | + final int? pendingCount; |
| 22 | + |
| 23 | + TodoList( |
| 24 | + {required this.id, |
| 25 | + required this.name, |
| 26 | + this.completedCount, |
| 27 | + this.pendingCount}); |
| 28 | + |
| 29 | + factory TodoList.fromRow(sqlite.Row row) { |
| 30 | + return TodoList( |
| 31 | + id: row['id'], |
| 32 | + name: row['name'], |
| 33 | + completedCount: row['completed_count'], |
| 34 | + pendingCount: row['pending_count']); |
| 35 | + } |
| 36 | + |
| 37 | + /// Watch all lists. |
| 38 | + static Stream<List<TodoList>> watchLists() { |
| 39 | + // This query is automatically re-run when data in "lists" or "todos" is modified. |
| 40 | + return db |
| 41 | + .watch('SELECT * FROM lists ORDER BY created_at, id') |
| 42 | + .map((results) { |
| 43 | + return results.map(TodoList.fromRow).toList(growable: false); |
| 44 | + }); |
| 45 | + } |
| 46 | + |
| 47 | + /// Watch all lists, with [completedCount] and [pendingCount] populated. |
| 48 | + static Stream<List<TodoList>> watchListsWithStats() { |
| 49 | + // This query is automatically re-run when data in "lists" or "todos" is modified. |
| 50 | + return db.watch(''' |
| 51 | + SELECT |
| 52 | + *, |
| 53 | + (SELECT count() FROM todos WHERE list_id = lists.id AND completed = TRUE) as completed_count, |
| 54 | + (SELECT count() FROM todos WHERE list_id = lists.id AND completed = FALSE) as pending_count |
| 55 | + FROM lists |
| 56 | + ORDER BY created_at |
| 57 | + ''').map((results) { |
| 58 | + return results.map(TodoList.fromRow).toList(growable: false); |
| 59 | + }); |
| 60 | + } |
| 61 | + |
| 62 | + /// Create a new list |
| 63 | + static Future<TodoList> create(String name) async { |
| 64 | + final results = await db.execute(''' |
| 65 | + INSERT INTO |
| 66 | + lists(id, created_at, name, owner_id) |
| 67 | + VALUES(uuid(), datetime(), ?, ?) |
| 68 | + RETURNING * |
| 69 | + ''', [name, await getUserId()]); |
| 70 | + return TodoList.fromRow(results.first); |
| 71 | + } |
| 72 | + |
| 73 | + /// Watch items within this list. |
| 74 | + Stream<List<TodoItem>> watchItems() { |
| 75 | + return db.watch( |
| 76 | + 'SELECT * FROM todos WHERE list_id = ? ORDER BY created_at DESC, id', |
| 77 | + parameters: [id]).map((event) { |
| 78 | + return event.map(TodoItem.fromRow).toList(growable: false); |
| 79 | + }); |
| 80 | + } |
| 81 | + |
| 82 | + /// Delete this list. |
| 83 | + Future<void> delete() async { |
| 84 | + await db.execute('DELETE FROM lists WHERE id = ?', [id]); |
| 85 | + } |
| 86 | + |
| 87 | + /// Find list item. |
| 88 | + static Future<TodoList> find(id) async { |
| 89 | + final results = await db.get('SELECT * FROM lists WHERE id = ?', [id]); |
| 90 | + return TodoList.fromRow(results); |
| 91 | + } |
| 92 | + |
| 93 | + /// Add a new todo item to this list. |
| 94 | + Future<TodoItem> add(String description) async { |
| 95 | + final results = await db.execute(''' |
| 96 | + INSERT INTO |
| 97 | + todos(id, created_at, completed, list_id, description, created_by) |
| 98 | + VALUES(uuid(), datetime(), FALSE, ?, ?, ?) |
| 99 | + RETURNING * |
| 100 | + ''', [id, description, await getUserId()]); |
| 101 | + return TodoItem.fromRow(results.first); |
| 102 | + } |
| 103 | +} |
0 commit comments