84 lines
2.5 KiB
Dart
84 lines
2.5 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
/// Defines the contract for persisting trip payloads locally.
|
|
abstract class TripLocalDataSource {
|
|
/// Returns all saved trip JSON payloads, newest first.
|
|
Future<List<Map<String, dynamic>>> loadTrips();
|
|
|
|
/// Returns a single trip payload by uuid if present.
|
|
/// TODO - should directly return Trip?
|
|
Future<Map<String, dynamic>?> getTrip(String uuid);
|
|
|
|
/// Upserts the provided trip payload (also used for editing existing trips).
|
|
Future<void> upsertTrip(Map<String, dynamic> tripJson);
|
|
|
|
/// Removes the trip with the matching uuid.
|
|
Future<void> deleteTrip(String uuid);
|
|
}
|
|
|
|
class TripLocalDataSourceImpl implements TripLocalDataSource {
|
|
TripLocalDataSourceImpl({Future<SharedPreferences>? preferences})
|
|
: _prefsFuture = preferences ?? SharedPreferences.getInstance();
|
|
|
|
static const String _storageKey = 'savedTrips';
|
|
final Future<SharedPreferences> _prefsFuture;
|
|
|
|
@override
|
|
Future<List<Map<String, dynamic>>> loadTrips() async {
|
|
final prefs = await _prefsFuture;
|
|
final stored = prefs.getStringList(_storageKey);
|
|
if (stored == null) return [];
|
|
return stored.map(_decodeTrip).toList();
|
|
}
|
|
|
|
@override
|
|
Future<Map<String, dynamic>?> getTrip(String uuid) async {
|
|
final trips = await loadTrips();
|
|
for (final trip in trips) {
|
|
if (trip['uuid'] == uuid) {
|
|
return Map<String, dynamic>.from(trip);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Future<void> upsertTrip(Map<String, dynamic> tripJson) async {
|
|
final uuid = tripJson['uuid'];
|
|
if (uuid is! String || uuid.isEmpty) {
|
|
throw ArgumentError('Trip JSON must contain a uuid string');
|
|
}
|
|
|
|
final trips = await loadTrips();
|
|
trips.removeWhere((trip) => trip['uuid'] == uuid);
|
|
trips.insert(0, Map<String, dynamic>.from(tripJson));
|
|
await _persistTrips(trips);
|
|
}
|
|
|
|
@override
|
|
Future<void> deleteTrip(String uuid) async {
|
|
final trips = await loadTrips();
|
|
final updated = trips.where((trip) => trip['uuid'] != uuid).toList();
|
|
if (updated.length == trips.length) {
|
|
return;
|
|
}
|
|
await _persistTrips(updated);
|
|
}
|
|
|
|
Future<void> _persistTrips(List<Map<String, dynamic>> trips) async {
|
|
final prefs = await _prefsFuture;
|
|
final payload = trips.map(jsonEncode).toList();
|
|
await prefs.setStringList(_storageKey, payload);
|
|
}
|
|
|
|
Map<String, dynamic> _decodeTrip(String raw) {
|
|
final decoded = jsonDecode(raw);
|
|
if (decoded is! Map<String, dynamic>) {
|
|
throw const FormatException('Saved trip entry is not a JSON object');
|
|
}
|
|
return Map<String, dynamic>.from(decoded);
|
|
}
|
|
}
|