Files
abawo-bt-app/lib/database/database.dart

144 lines
4.8 KiB
Dart

import 'package:anyhow/anyhow.dart';
import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'database.g.dart';
@riverpod
class NConnectedDevices extends _$NConnectedDevices {
@override
Future<List<ConnectedDevice>> build() async {
final db = await ref.watch(databaseProvider);
return await db.getAllConnectedDevices();
}
Future<Result<int>> addConnectedDevice(
ConnectedDevicesCompanion device) async {
final db = await ref.watch(databaseProvider);
final res = await db.addConnectedDevice(device);
if (res.isOk()) {
ref.invalidateSelf();
}
return res;
}
Future<Result<void>> deleteConnectedDevice(int id) async {
final db = await ref.watch(databaseProvider);
final res = await db.deleteConnectedDevice(id);
if (res.isOk()) {
ref.invalidateSelf();
}
return res;
}
}
/// Provider for the [AppDatabase] instance
final databaseProvider = Provider<AppDatabase>((ref) {
final database = AppDatabase();
ref.onDispose(() => database.close());
return database;
});
/// Provider for all connected devices as a stream
final connectedDevicesStreamProvider =
StreamProvider<List<ConnectedDevice>>((ref) {
final database = ref.watch(databaseProvider);
return database.getAllConnectedDevicesStream();
});
/// Provider for all connected devices as a future
final connectedDevicesProvider = FutureProvider<List<ConnectedDevice>>((ref) {
final database = ref.watch(databaseProvider);
return database.getAllConnectedDevices();
});
class ConnectedDevices extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get deviceName => text()();
TextColumn get deviceAddress => text()();
TextColumn get deviceType => text()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get lastConnectedAt => dateTime().nullable()();
}
@DriftDatabase(tables: [ConnectedDevices])
class AppDatabase extends _$AppDatabase {
// After generating code, this class needs to define a `schemaVersion` getter
// and a constructor telling drift where the database should be stored.
// These are described in the getting started guide: https://drift.simonbinder.eu/setup/
AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());
@override
int get schemaVersion => 1;
static QueryExecutor _openConnection() {
return driftDatabase(
name: 'my_database',
native: const DriftNativeOptions(
// By default, `driftDatabase` from `package:drift_flutter` stores the
// database files in `getApplicationDocumentsDirectory()`.
databaseDirectory: getApplicationSupportDirectory,
),
// If you need web support, see https://drift.simonbinder.eu/platforms/web/
);
}
Future<List<ConnectedDevice>> getAllConnectedDevices() {
return select(connectedDevices).get();
}
/// Adds a new connected device to the database.
///
/// [device] is a [ConnectedDevicesCompanion] representing the device to be inserted.
///
/// Returns a [Result] indicating success or failure of the device insertion.
/// On successful insertion, returns [Ok]\(rowid\). On failure, returns an error with a descriptive message.
Future<Result<int>> addConnectedDevice(
ConnectedDevicesCompanion device) async {
try {
if (!device.deviceAddress.present) {
return bail('Device address is required to save a connected device.');
}
final exists = await (select(connectedDevices)
..where(
(tbl) => tbl.deviceAddress.equals(device.deviceAddress.value)))
.getSingleOrNull();
if (exists != null) {
return bail('Device ${device.deviceAddress.value} is already added.');
}
final rowid = await into(connectedDevices).insert(device);
return Ok(rowid);
} catch (e, st) {
return bail('Failed to add device: $e', st);
}
}
/// Deletes a connected device from the database by its ID.
///
/// [id] is the ID of the device to be deleted.
///
/// Returns a [Result] indicating success or failure of the deletion.
/// On successful deletion, returns [Ok](number of deleted rows). On failure, returns an error with a descriptive message.
Future<Result<void>> deleteConnectedDevice(int id) async {
try {
final count = await (delete(connectedDevices)
..where((tbl) => tbl.id.equals(id)))
.go();
if (count == 0) {
return bail('Device with id $id not found.');
}
return Ok(());
} catch (e, st) {
return bail('Failed to delete device with id $id: $e', st);
}
}
Stream<List<ConnectedDevice>> getAllConnectedDevicesStream() {
return select(connectedDevices).watch();
}
}