-
-
Notifications
You must be signed in to change notification settings - Fork 244
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
508 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import 'dart:convert'; | ||
import 'package:http/http.dart' as http; | ||
import 'package:logging/logging.dart'; | ||
|
||
final log = Logger('powersync-test'); | ||
|
||
class ApiClient { | ||
final String baseUrl; | ||
|
||
ApiClient(this.baseUrl); | ||
|
||
Future<Map<String, dynamic>> authenticate(String username, String password) async { | ||
final response = await http.post( | ||
Uri.parse('$baseUrl/api/auth/'), | ||
headers: {'Content-Type': 'application/json'}, | ||
body: json.encode({'username': username, 'password': password}), | ||
); | ||
if (response.statusCode == 200) { | ||
return json.decode(response.body); | ||
} else { | ||
throw Exception('Failed to authenticate'); | ||
} | ||
} | ||
|
||
Future<Map<String, dynamic>> getToken(String userId) async { | ||
final response = await http.get( | ||
Uri.parse('$baseUrl/api/get_powersync_token/'), | ||
headers: {'Content-Type': 'application/json'}, | ||
); | ||
if (response.statusCode == 200) { | ||
return json.decode(response.body); | ||
} else { | ||
throw Exception('Failed to fetch token'); | ||
} | ||
} | ||
|
||
Future<void> upsert(Map<String, dynamic> record) async { | ||
await http.put( | ||
Uri.parse('$baseUrl/api/upload_data/'), | ||
headers: {'Content-Type': 'application/json'}, | ||
body: json.encode(record), | ||
); | ||
} | ||
|
||
Future<void> update(Map<String, dynamic> record) async { | ||
await http.patch( | ||
Uri.parse('$baseUrl/api/upload_data/'), | ||
headers: {'Content-Type': 'application/json'}, | ||
body: json.encode(record), | ||
); | ||
} | ||
|
||
Future<void> delete(Map<String, dynamic> record) async { | ||
await http.delete( | ||
Uri.parse('$baseUrl/api/upload_data/'), | ||
headers: {'Content-Type': 'application/json'}, | ||
body: json.encode(record), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
class AppConfig { | ||
static const String djangoUrl = 'http://192.168.2.223:6061'; | ||
static const String powersyncUrl = 'http://192.168.2.223:8080'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import 'package:powersync/powersync.dart'; | ||
|
||
const todosTable = 'todos'; | ||
|
||
// these are the same ones as in postgres, except for 'id' | ||
Schema schema = const Schema(([ | ||
Table(todosTable, [ | ||
Column.text('list_id'), | ||
Column.text('created_at'), | ||
Column.text('completed_at'), | ||
Column.text('description'), | ||
Column.integer('completed'), | ||
Column.text('created_by'), | ||
Column.text('completed_by'), | ||
], indexes: [ | ||
// Index to allow efficient lookup within a list | ||
Index('list', [IndexedColumn('list_id')]) | ||
]), | ||
Table('lists', | ||
[Column.text('created_at'), Column.text('name'), Column.text('owner_id')]) | ||
])); | ||
|
||
// post gres columns: | ||
// todos: | ||
// id | created_at | completed_at | description | completed | created_by | completed_by | list_id | ||
// lists: | ||
// id | created_at | name | owner_id | ||
|
||
// diagnostics app: | ||
/* | ||
new Schema([ | ||
new Table({ | ||
name: 'lists', // same as flutter | ||
columns: [ | ||
new Column({ name: 'created_at', type: ColumnType.TEXT }), | ||
new Column({ name: 'name', type: ColumnType.TEXT }), | ||
new Column({ name: 'owner_id', type: ColumnType.TEXT }) | ||
] | ||
}), | ||
new Table({ | ||
name: 'todos', // misses completed_at and completed_by, until these actually get populated with something | ||
columns: [ | ||
new Column({ name: 'created_at', type: ColumnType.TEXT }), | ||
new Column({ name: 'description', type: ColumnType.TEXT }), | ||
new Column({ name: 'completed', type: ColumnType.INTEGER }), | ||
new Column({ name: 'created_by', type: ColumnType.TEXT }), | ||
new Column({ name: 'list_id', type: ColumnType.TEXT }) | ||
] | ||
}) | ||
]) | ||
Column.text('completed_at'), | ||
Column.text('completed_by'), | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import 'package:wger/models/schema.dart'; | ||
|
||
import '../powersync.dart'; | ||
import 'package:powersync/sqlite3.dart' as sqlite; | ||
|
||
/// 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<void> 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<void> delete() async { | ||
await db.execute('DELETE FROM $todosTable WHERE id = ?', [id]); | ||
} | ||
|
||
static Future<void> addPhoto(String photoId, String id) async { | ||
await db.execute('UPDATE $todosTable SET photo_id = ? WHERE id = ?', [photoId, id]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import 'package:powersync/sqlite3.dart' as sqlite; | ||
|
||
import 'todo_item.dart'; | ||
import '../powersync.dart'; | ||
|
||
/// TodoList represents a result row of a query on "lists". | ||
/// | ||
/// 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 list. | ||
class TodoList { | ||
/// List id (UUID). | ||
final String id; | ||
|
||
/// Descriptive name. | ||
final String name; | ||
|
||
/// Number of completed todos in this list. | ||
final int? completedCount; | ||
|
||
/// Number of pending todos in this list. | ||
final int? pendingCount; | ||
|
||
TodoList({required this.id, required this.name, this.completedCount, this.pendingCount}); | ||
|
||
factory TodoList.fromRow(sqlite.Row row) { | ||
return TodoList( | ||
id: row['id'], | ||
name: row['name'], | ||
completedCount: row['completed_count'], | ||
pendingCount: row['pending_count']); | ||
} | ||
|
||
/// Watch all lists. | ||
static Stream<List<TodoList>> 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); | ||
}); | ||
} | ||
|
||
/// Watch all lists, with [completedCount] and [pendingCount] populated. | ||
static Stream<List<TodoList>> watchListsWithStats() { | ||
// This query is automatically re-run when data in "lists" or "todos" is modified. | ||
return db.watch(''' | ||
SELECT | ||
*, | ||
(SELECT count() FROM todos WHERE list_id = lists.id AND completed = TRUE) as completed_count, | ||
(SELECT count() FROM todos WHERE list_id = lists.id AND completed = FALSE) as pending_count | ||
FROM lists | ||
ORDER BY created_at | ||
''').map((results) { | ||
return results.map(TodoList.fromRow).toList(growable: false); | ||
}); | ||
} | ||
|
||
/// Create a new list | ||
static Future<TodoList> 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); | ||
} | ||
|
||
/// Watch items within this list. | ||
Stream<List<TodoItem>> watchItems() { | ||
return db.watch('SELECT * FROM todos WHERE list_id = ? ORDER BY created_at DESC, id', | ||
parameters: [id]).map((event) { | ||
return event.map(TodoItem.fromRow).toList(growable: false); | ||
}); | ||
} | ||
|
||
/// Delete this list. | ||
Future<void> delete() async { | ||
await db.execute('DELETE FROM lists WHERE id = ?', [id]); | ||
} | ||
|
||
/// Find list item. | ||
static Future<TodoList> find(id) async { | ||
final results = await db.get('SELECT * FROM lists WHERE id = ?', [id]); | ||
return TodoList.fromRow(results); | ||
} | ||
|
||
/// Add a new todo item to this list. | ||
Future<TodoItem> add(String description) async { | ||
final results = await db.execute(''' | ||
INSERT INTO | ||
todos(id, created_at, completed, list_id, description, created_by) | ||
VALUES(uuid(), datetime(), FALSE, ?, ?, ?) | ||
RETURNING * | ||
''', [id, description, await getUserId()]); | ||
return TodoItem.fromRow(results.first); | ||
} | ||
} |
Oops, something went wrong.