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>> loadTrips(); /// Returns a single trip payload by uuid if present. /// TODO - should directly return Trip? Future?> getTrip(String uuid); /// Upserts the provided trip payload (also used for editing existing trips). Future upsertTrip(Map tripJson); /// Removes the trip with the matching uuid. Future deleteTrip(String uuid); } class TripLocalDataSourceImpl implements TripLocalDataSource { TripLocalDataSourceImpl({Future? preferences}) : _prefsFuture = preferences ?? SharedPreferences.getInstance(); static const String _storageKey = 'savedTrips'; final Future _prefsFuture; @override Future>> loadTrips() async { final prefs = await _prefsFuture; final stored = prefs.getStringList(_storageKey); if (stored == null) return []; return stored.map(_decodeTrip).toList(); } @override Future?> getTrip(String uuid) async { final trips = await loadTrips(); for (final trip in trips) { if (trip['uuid'] == uuid) { return Map.from(trip); } } return null; } @override Future upsertTrip(Map 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.from(tripJson)); await _persistTrips(trips); } @override Future 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 _persistTrips(List> trips) async { final prefs = await _prefsFuture; final payload = trips.map(jsonEncode).toList(); await prefs.setStringList(_storageKey, payload); } Map _decodeTrip(String raw) { final decoded = jsonDecode(raw); if (decoded is! Map) { throw const FormatException('Saved trip entry is not a JSON object'); } return Map.from(decoded); } }