MotorSignal-App/lib/features/garage/garage_providers.dart

170 lines
5.3 KiB
Dart

import 'dart:convert';
import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../database/database.dart';
import '../../models/motorcycle_models.dart';
import '../../services/motorcycle/motorcycle_repository.dart';
final motorcycleRepositoryProvider = Provider<MotorcycleRepository>((ref) {
return HybridMotorcycleRepository();
});
final garageMotorcyclesStreamProvider = StreamProvider<List<GarageMotorcycle>>((ref) {
final db = ref.watch(databaseProvider);
return (db.select(db.garageMotorcycles)
..orderBy([(t) => OrderingTerm(expression: t.dateAdded, mode: OrderingMode.desc)]))
.watch();
});
final defaultMotorcycleProvider = Provider<GarageMotorcycle?>((ref) {
final bikesAsync = ref.watch(garageMotorcyclesStreamProvider);
final bikes = bikesAsync.value ?? [];
try {
return bikes.firstWhere((b) => b.isDefault);
} catch (_) {
return bikes.isNotEmpty ? bikes.first : null;
}
});
class MotorcycleStats {
final int rideCount;
final double totalDistanceMiles;
final double topSpeedMph;
final double? best0to60Ms;
final double? best0to100Ms;
final double maxLean;
final double maxAccelG;
final double maxBrakingG;
MotorcycleStats({
required this.rideCount,
required this.totalDistanceMiles,
required this.topSpeedMph,
this.best0to60Ms,
this.best0to100Ms,
required this.maxLean,
required this.maxAccelG,
required this.maxBrakingG,
});
}
final motorcyclePerformanceStatsProvider =
FutureProvider.family<MotorcycleStats, int>((ref, motorcycleId) async {
final db = ref.watch(databaseProvider);
final rides = await (db.select(db.rides)..where((t) => t.motorcycleId.equals(motorcycleId))).get();
if (rides.isEmpty) {
return MotorcycleStats(
rideCount: 0,
totalDistanceMiles: 0.0,
topSpeedMph: 0.0,
maxLean: 0.0,
maxAccelG: 0.0,
maxBrakingG: 0.0,
);
}
int count = rides.length;
double totalDistance = 0.0;
double topSpeed = 0.0;
double? best0to60;
double? best0to100;
double maxLean = 0.0;
double maxAccel = 0.0;
double maxBraking = 0.0;
for (var r in rides) {
totalDistance += r.distanceMiles;
if (r.topSpeedMph > topSpeed) topSpeed = r.topSpeedMph;
final lean = r.maxLeanLeft > r.maxLeanRight ? r.maxLeanLeft : r.maxLeanRight;
if (lean > maxLean) maxLean = lean;
if (r.maxAccelG > maxAccel) maxAccel = r.maxAccelG;
if (r.maxBrakingG > maxBraking) maxBraking = r.maxBrakingG;
if (r.best0to60Ms != null) {
if (best0to60 == null || r.best0to60Ms! < best0to60) {
best0to60 = r.best0to60Ms;
}
}
if (r.best0to100Ms != null) {
if (best0to100 == null || r.best0to100Ms! < best0to100) {
best0to100 = r.best0to100Ms;
}
}
}
return MotorcycleStats(
rideCount: count,
totalDistanceMiles: totalDistance,
topSpeedMph: topSpeed,
best0to60Ms: best0to60,
best0to100Ms: best0to100,
maxLean: maxLean,
maxAccelG: maxAccel,
maxBrakingG: maxBraking,
);
});
class GarageController {
final AppDatabase _db;
final MotorcycleRepository _repository;
GarageController(this._db, this._repository);
Future<int> addMotorcycle(MotorcycleSelection selection, {String? nickname}) async {
final spec = await _repository.getSpecifications(selection);
final specJson = spec != null ? json.encode(spec.toJson()) : null;
// Check existing default count
final existingBikes = await _db.select(_db.garageMotorcycles).get();
final isFirst = existingBikes.isEmpty;
return await _db.into(_db.garageMotorcycles).insert(
GarageMotorcyclesCompanion.insert(
year: selection.year,
makeId: selection.makeId,
makeName: selection.makeName,
modelId: selection.modelId,
modelName: selection.modelName,
variantId: Value(selection.variantId),
variantName: Value(selection.variantName),
nickname: Value(nickname),
isDefault: Value(isFirst),
dateAdded: DateTime.now(),
lastUpdated: DateTime.now(),
specificationJson: Value(specJson),
),
);
}
Future<void> setDefaultMotorcycle(int id) async {
await (_db.update(_db.garageMotorcycles)..where((t) => t.id.equals(id).not()))
.write(const GarageMotorcyclesCompanion(isDefault: Value(false)));
await (_db.update(_db.garageMotorcycles)..where((t) => t.id.equals(id)))
.write(const GarageMotorcyclesCompanion(isDefault: Value(true)));
}
Future<void> updateNickname(int id, String? nickname) async {
await (_db.update(_db.garageMotorcycles)..where((t) => t.id.equals(id)))
.write(GarageMotorcyclesCompanion(nickname: Value(nickname)));
}
Future<void> deleteMotorcycle(int id) async {
await (_db.delete(_db.garageMotorcycles)..where((t) => t.id.equals(id))).go();
// If default was deleted, set next bike as default if available
final remaining = await _db.select(_db.garageMotorcycles).get();
if (remaining.isNotEmpty && !remaining.any((b) => b.isDefault)) {
await setDefaultMotorcycle(remaining.first.id);
}
}
}
final garageControllerProvider = Provider<GarageController>((ref) {
final db = ref.watch(databaseProvider);
final repo = ref.watch(motorcycleRepositoryProvider);
return GarageController(db, repo);
});