mirror of https://github.com/AMT-Cheif/drift.git
149 lines
5.0 KiB
Dart
149 lines
5.0 KiB
Dart
import 'package:drift/drift.dart';
|
|
import 'package:flutter/material.dart' show Colors;
|
|
import 'package:riverpod/riverpod.dart';
|
|
|
|
import 'connection/connection.dart' as impl;
|
|
import 'tables.dart';
|
|
// Manually generated by `drift_dev schema steps` - this file makes writing
|
|
// migrations easier. See this for details:
|
|
// https://drift.simonbinder.eu/docs/advanced-features/migrations/#step-by-step
|
|
import 'schema_versions.dart';
|
|
|
|
// Generated by drift_dev when running `build_runner build`
|
|
part 'database.g.dart';
|
|
|
|
@DriftDatabase(tables: [TodoEntries, Categories], include: {'sql.drift'})
|
|
class AppDatabase extends _$AppDatabase {
|
|
AppDatabase() : super(impl.connect());
|
|
|
|
AppDatabase.forTesting(DatabaseConnection connection) : super(connection);
|
|
|
|
@override
|
|
int get schemaVersion => 3;
|
|
|
|
@override
|
|
MigrationStrategy get migration {
|
|
return MigrationStrategy(
|
|
onUpgrade: stepByStep(
|
|
from1To2: (m, schema) async {
|
|
// The todoEntries.dueDate column was added in version 2.
|
|
await m.addColumn(schema.todoEntries, schema.todoEntries.dueDate);
|
|
},
|
|
from2To3: (m, schema) async {
|
|
// New triggers were added in version 3:
|
|
await m.create(schema.todosDelete);
|
|
await m.create(schema.todosUpdate);
|
|
|
|
// Also, the `REFERENCES` constraint was added to
|
|
// [TodoEntries.category]. Run a table migration to rebuild all
|
|
// column constraints without loosing data.
|
|
await m.alterTable(TableMigration(schema.todoEntries));
|
|
},
|
|
),
|
|
beforeOpen: (details) async {
|
|
// Make sure that foreign keys are enabled
|
|
await customStatement('PRAGMA foreign_keys = ON');
|
|
|
|
if (details.wasCreated) {
|
|
// Create a bunch of default values so the app doesn't look too empty
|
|
// on the first start.
|
|
await batch((b) {
|
|
b.insert(
|
|
categories,
|
|
CategoriesCompanion.insert(name: 'Important', color: Colors.red),
|
|
);
|
|
|
|
b.insertAll(todoEntries, [
|
|
TodoEntriesCompanion.insert(description: 'Check out drift'),
|
|
TodoEntriesCompanion.insert(
|
|
description: 'Fix session invalidation bug',
|
|
category: const Value(1)),
|
|
TodoEntriesCompanion.insert(
|
|
description: 'Add favorite movies to home page'),
|
|
]);
|
|
});
|
|
}
|
|
|
|
// This follows the recommendation to validate that the database schema
|
|
// matches what drift expects (https://drift.simonbinder.eu/docs/advanced-features/migrations/#verifying-a-database-schema-at-runtime).
|
|
// It allows catching bugs in the migration logic early.
|
|
await impl.validateDatabaseSchema(this);
|
|
},
|
|
);
|
|
}
|
|
|
|
Future<List<TodoEntryWithCategory>> search(String query) {
|
|
return _search(query).map((row) {
|
|
return TodoEntryWithCategory(entry: row.todos, category: row.cat);
|
|
}).get();
|
|
}
|
|
|
|
Stream<List<CategoryWithCount>> categoriesWithCount() {
|
|
// the _categoriesWithCount method has been generated automatically based
|
|
// on the query declared in the @DriftDatabase annotation
|
|
return _categoriesWithCount().map((row) {
|
|
final hasId = row.id != null;
|
|
final category = hasId
|
|
? Category(id: row.id!, name: row.name!, color: row.color!)
|
|
: null;
|
|
|
|
return CategoryWithCount(category, row.amount);
|
|
}).watch();
|
|
}
|
|
|
|
/// Returns an auto-updating stream of all todo entries in a given category
|
|
/// id.
|
|
Stream<List<TodoEntryWithCategory>> entriesInCategory(int? categoryId) {
|
|
final query = select(todoEntries).join([
|
|
leftOuterJoin(categories, categories.id.equalsExp(todoEntries.category))
|
|
]);
|
|
|
|
if (categoryId != null) {
|
|
query.where(categories.id.equals(categoryId));
|
|
} else {
|
|
query.where(categories.id.isNull());
|
|
}
|
|
|
|
return query.map((row) {
|
|
return TodoEntryWithCategory(
|
|
entry: row.readTable(todoEntries),
|
|
category: row.readTableOrNull(categories),
|
|
);
|
|
}).watch();
|
|
}
|
|
|
|
Future<void> deleteCategory(Category category) {
|
|
return transaction(() async {
|
|
// First, move todo entries that might remain into the default category
|
|
await (todoEntries.update()
|
|
..where((todo) => todo.category.equals(category.id)))
|
|
.write(const TodoEntriesCompanion(category: Value(null)));
|
|
|
|
// Then, delete the category
|
|
await categories.deleteOne(category);
|
|
});
|
|
}
|
|
|
|
static final StateProvider<AppDatabase> provider = StateProvider((ref) {
|
|
final database = AppDatabase();
|
|
ref.onDispose(database.close);
|
|
|
|
return database;
|
|
});
|
|
}
|
|
|
|
class TodoEntryWithCategory {
|
|
final TodoEntry entry;
|
|
final Category? category;
|
|
|
|
TodoEntryWithCategory({required this.entry, this.category});
|
|
}
|
|
|
|
class CategoryWithCount {
|
|
// can be null, in which case we count how many entries don't have a category
|
|
final Category? category;
|
|
final int count; // amount of entries in this category
|
|
|
|
CategoryWithCount(this.category, this.count);
|
|
}
|