feat(wip): implement trip persistence through a local repository. Include loaded trips in the start page UI
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user