diff --git a/docs/lib/sample/app.zap b/docs/lib/sample/app.zap new file mode 100644 index 000000000..dbab86e5e --- /dev/null +++ b/docs/lib/sample/app.zap @@ -0,0 +1,74 @@ + + + +
+

Todo list

+

This offline todo-list app is implemented with drift on the web.

+
+ +
+{#await database from dbFuture} + {#if database.hasData} +
+ +
+ {#await each snapshot from database.data.items} + {#if snapshot.hasData} + {#for entry, i in snapshot.data} + + {#if i != snapshot.data.length - 1} +
+ {/if} + {/for} + {/if} + {/await} + {:else if database.hasError} + Sorry, we could not open the database on this browser! + {:else} + + {/if} +{/await} + +
+ + diff --git a/docs/lib/sample/database.dart b/docs/lib/sample/database.dart new file mode 100644 index 000000000..e12b26a81 --- /dev/null +++ b/docs/lib/sample/database.dart @@ -0,0 +1,105 @@ +import 'dart:async'; + +import 'package:drift/drift.dart'; +import 'package:drift/wasm.dart'; +import 'package:rxdart/rxdart.dart'; + +part 'database.g.dart'; + +enum TodoListFilter { all, active, completed } + +class TodoItems extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get description => text()(); + BoolColumn get completed => boolean().withDefault(const Constant(false))(); +} + +@DriftDatabase(tables: [TodoItems]) +class Database extends _$Database { + final BehaviorSubject _filterChanges = + BehaviorSubject.seeded(TodoListFilter.all); + + Database(super.e); + + @override + int get schemaVersion => 1; + + @override + MigrationStrategy get migration { + return MigrationStrategy( + onCreate: (m) async { + await m.createAll(); + + // Create some entries by default + await batch((b) { + b.insertAll(todoItems, [ + TodoItemsCompanion.insert(description: 'Migrate to drift'), + TodoItemsCompanion.insert(description: 'Support all platforms'), + TodoItemsCompanion.insert( + description: 'Solve local persistence issues'), + ]); + }); + }, + ); + } + + TodoListFilter get currentFilter => _filterChanges.value; + + set currentFilter(TodoListFilter value) { + _filterChanges.add(value); + } + + Stream> get items => _filterChanges.stream.switchMap(_items); + + Stream get uncompletedItems { + final all = countAll(); + final query = selectOnly(todoItems) + ..addColumns([all]) + ..where(todoItems.completed.not()); + + return query.map((row) => row.read(all)!).watchSingle(); + } + + Stream> _items(TodoListFilter filter) { + final query = todoItems.select(); + + switch (filter) { + case TodoListFilter.completed: + query.where((row) => row.completed); + case TodoListFilter.active: + query.where((row) => row.completed.not()); + case TodoListFilter.all: + break; + } + + return query.watch(); + } + + Future toggleCompleted(TodoItem item) async { + final statement = update(todoItems)..whereSamePrimaryKey(item); + await statement.write(TodoItemsCompanion.custom( + completed: todoItems.completed.not(), + )); + } +} + +Future connect() async { + final result = await WasmDatabase.open( + databaseName: 'todo_example', + sqlite3Uri: Uri.parse('/sqlite3.wasm'), + driftWorkerUri: Uri.parse('/drift_worker.dart.js'), + ); + + if (!result.chosenImplementation.fullySupported) { + print('Using ${result.chosenImplementation} due to unsupported browser ' + 'features: ${result.missingFeatures}'); + } + + return result; +} + +extension CompatibilityUI on WasmStorageImplementation { + bool get fullySupported => + this != WasmStorageImplementation.inMemory && + this != WasmStorageImplementation.unsafeIndexedDb; +} diff --git a/docs/lib/sample/entry.zap b/docs/lib/sample/entry.zap new file mode 100644 index 000000000..8dabb046b --- /dev/null +++ b/docs/lib/sample/entry.zap @@ -0,0 +1,18 @@ + + + diff --git a/docs/lib/sample/toolbar.zap b/docs/lib/sample/toolbar.zap new file mode 100644 index 000000000..e19998436 --- /dev/null +++ b/docs/lib/sample/toolbar.zap @@ -0,0 +1,41 @@ + + + + +{#await each entry from database.uncompletedItems} + {#if entry.hasData} + {entry.data} {entry.data == 1 ? 'item' : 'items'} left + {/if} +{/await} + + diff --git a/docs/pubspec.yaml b/docs/pubspec.yaml index 8f38b9645..45d2a7ddd 100644 --- a/docs/pubspec.yaml +++ b/docs/pubspec.yaml @@ -25,6 +25,10 @@ dependencies: rxdart: ^0.27.3 yaml: ^3.1.1 drift_dev: any + zap: ^0.2.0 + picocss: + hosted: https://simonbinder.eu + version: ^1.5.10 dev_dependencies: lints: ^2.0.0 @@ -42,6 +46,7 @@ dev_dependencies: source_span: ^1.9.1 test: ^1.18.0 sqlparser: + zap_dev: ^0.2.2 dependency_overrides: diff --git a/docs/web/web/compatibility.dart b/docs/web/web/compatibility.dart index 846527cc2..e5c7845e9 100644 --- a/docs/web/web/compatibility.dart +++ b/docs/web/web/compatibility.dart @@ -1,8 +1,13 @@ import 'dart:html'; import 'package:drift/wasm.dart'; +import 'package:drift_docs/site.dart' as i0; +import 'package:docsy/main.dart' as i1; void main() async { + i0.built_site_main(); + i1.built_site_main(); + final btn = querySelector('#drift-compat-btn')!; final results = querySelector('#drift-compat-results')!; diff --git a/docs/web/web/example/index.html b/docs/web/web/example/index.html new file mode 100644 index 000000000..52c1be82a --- /dev/null +++ b/docs/web/web/example/index.html @@ -0,0 +1,17 @@ + + + + + + + Drift web example + + + + + +
+ +
+ + diff --git a/docs/web/web/example/main.dart b/docs/web/web/example/main.dart new file mode 100644 index 000000000..ac6223056 --- /dev/null +++ b/docs/web/web/example/main.dart @@ -0,0 +1,9 @@ +import 'dart:html'; + +import 'package:drift_docs/sample/app.zap.dart'; + +void main() { + final main = document.querySelector('main.container')!; + + App().create(main); +} diff --git a/docs/web/web/example/style.scss b/docs/web/web/example/style.scss new file mode 100644 index 000000000..e7b258775 --- /dev/null +++ b/docs/web/web/example/style.scss @@ -0,0 +1,2 @@ +@use "package:picocss/pico"; +@use "package:drift_docs/sample/app.zap"; diff --git a/netlify.toml b/netlify.toml index 94d0e429c..e2de9174c 100644 --- a/netlify.toml +++ b/netlify.toml @@ -21,6 +21,12 @@ status = 301 force = true +[[headers]] + for = "/*" + [headers.values] + Cross-Origin-Opener-Policy = "same-origin" + Cross-Origin-Embedder-Policy = "require-corp" + [context.production] environment = { BUILD_RELEASE="release" }