Files
anyway/frontend/lib/data/datasources/trip_local_datasource.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);
}
}