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> build() async { final db = await ref.watch(databaseProvider); return await db.getAllConnectedDevices(); } Future> addConnectedDevice( ConnectedDevicesCompanion device) async { final db = await ref.watch(databaseProvider); final res = await db.addConnectedDevice(device); if (res.isOk()) { ref.invalidateSelf(); } return res; } Future> 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((ref) { final database = AppDatabase(); ref.onDispose(() => database.close()); return database; }); /// Provider for all connected devices as a stream final connectedDevicesStreamProvider = StreamProvider>((ref) { final database = ref.watch(databaseProvider); return database.getAllConnectedDevicesStream(); }); /// Provider for all connected devices as a future final connectedDevicesProvider = FutureProvider>((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> 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> 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> 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> getAllConnectedDevicesStream() { return select(connectedDevices).watch(); } }