170 lines
5.3 KiB
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);
|
|
});
|