mirror of
https://github.com/flutter/samples.git
synced 2026-05-15 03:20:01 +00:00
Compass app (#2446)
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/activity/activity.dart';
|
||||
import '../../../utils/result.dart';
|
||||
|
||||
/// Data source for activities.
|
||||
abstract class ActivityRepository {
|
||||
/// Get activities by [Destination] ref.
|
||||
Future<Result<List<Activity>>> getByDestination(String ref);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/activity/activity.dart';
|
||||
import '../../../utils/result.dart';
|
||||
import '../../services/local/local_data_service.dart';
|
||||
import 'activity_repository.dart';
|
||||
|
||||
/// Local implementation of ActivityRepository
|
||||
/// Uses data from assets folder
|
||||
class ActivityRepositoryLocal implements ActivityRepository {
|
||||
ActivityRepositoryLocal({
|
||||
required LocalDataService localDataService,
|
||||
}) : _localDataService = localDataService;
|
||||
|
||||
final LocalDataService _localDataService;
|
||||
|
||||
@override
|
||||
Future<Result<List<Activity>>> getByDestination(String ref) async {
|
||||
try {
|
||||
final activities = (await _localDataService.getActivities())
|
||||
.where((activity) => activity.destinationRef == ref)
|
||||
.toList();
|
||||
|
||||
return Result.ok(activities);
|
||||
} on Exception catch (error) {
|
||||
return Result.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/activity/activity.dart';
|
||||
import '../../../utils/result.dart';
|
||||
import '../../services/api/api_client.dart';
|
||||
import 'activity_repository.dart';
|
||||
|
||||
/// Remote data source for [Activity].
|
||||
/// Implements basic local caching.
|
||||
/// See: https://docs.flutter.dev/get-started/fwe/local-caching
|
||||
class ActivityRepositoryRemote implements ActivityRepository {
|
||||
ActivityRepositoryRemote({
|
||||
required ApiClient apiClient,
|
||||
}) : _apiClient = apiClient;
|
||||
|
||||
final ApiClient _apiClient;
|
||||
|
||||
final Map<String, List<Activity>> _cachedData = {};
|
||||
|
||||
@override
|
||||
Future<Result<List<Activity>>> getByDestination(String ref) async {
|
||||
if (!_cachedData.containsKey(ref)) {
|
||||
// No cached data, request activities
|
||||
final result = await _apiClient.getActivityByDestination(ref);
|
||||
if (result is Ok) {
|
||||
_cachedData[ref] = result.asOk.value;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
// Return cached data if available
|
||||
return Result.ok(_cachedData[ref]!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../../../utils/result.dart';
|
||||
|
||||
abstract class AuthRepository extends ChangeNotifier {
|
||||
/// Returns true when the user is logged in
|
||||
/// Returns [Future] because it will load a stored auth state the first time.
|
||||
Future<bool> get isAuthenticated;
|
||||
|
||||
/// Perform login
|
||||
Future<Result<void>> login({
|
||||
required String email,
|
||||
required String password,
|
||||
});
|
||||
|
||||
/// Perform logout
|
||||
Future<Result<void>> logout();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../utils/result.dart';
|
||||
import 'auth_repository.dart';
|
||||
|
||||
class AuthRepositoryDev extends AuthRepository {
|
||||
/// User is always authenticated in dev scenarios
|
||||
@override
|
||||
Future<bool> get isAuthenticated => Future.value(true);
|
||||
|
||||
/// Login is always successful in dev scenarios
|
||||
@override
|
||||
Future<Result<void>> login({
|
||||
required String email,
|
||||
required String password,
|
||||
}) async {
|
||||
return Result.ok(null);
|
||||
}
|
||||
|
||||
/// Logout is always successful in dev scenarios
|
||||
@override
|
||||
Future<Result<void>> logout() async {
|
||||
return Result.ok(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import '../../../utils/result.dart';
|
||||
import '../../services/api/api_client.dart';
|
||||
import '../../services/api/auth_api_client.dart';
|
||||
import '../../services/api/model/login_request/login_request.dart';
|
||||
import '../../services/api/model/login_response/login_response.dart';
|
||||
import '../../services/shared_preferences_service.dart';
|
||||
import 'auth_repository.dart';
|
||||
|
||||
class AuthRepositoryRemote extends AuthRepository {
|
||||
AuthRepositoryRemote({
|
||||
required ApiClient apiClient,
|
||||
required AuthApiClient authApiClient,
|
||||
required SharedPreferencesService sharedPreferencesService,
|
||||
}) : _apiClient = apiClient,
|
||||
_authApiClient = authApiClient,
|
||||
_sharedPreferencesService = sharedPreferencesService {
|
||||
_apiClient.authHeaderProvider = _authHeaderProvider;
|
||||
}
|
||||
|
||||
final AuthApiClient _authApiClient;
|
||||
final ApiClient _apiClient;
|
||||
final SharedPreferencesService _sharedPreferencesService;
|
||||
|
||||
bool? _isAuthenticated;
|
||||
String? _authToken;
|
||||
final _log = Logger('AuthRepositoryRemote');
|
||||
|
||||
/// Fetch token from shared preferences
|
||||
Future<void> _fetch() async {
|
||||
final result = await _sharedPreferencesService.fetchToken();
|
||||
switch (result) {
|
||||
case Ok<String?>():
|
||||
_authToken = result.value;
|
||||
_isAuthenticated = result.value != null;
|
||||
case Error<String?>():
|
||||
_log.severe(
|
||||
'Failed to fech Token from SharedPreferences',
|
||||
result.error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> get isAuthenticated async {
|
||||
// Status is cached
|
||||
if (_isAuthenticated != null) {
|
||||
return _isAuthenticated!;
|
||||
}
|
||||
// No status cached, fetch from storage
|
||||
await _fetch();
|
||||
return _isAuthenticated ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<void>> login({
|
||||
required String email,
|
||||
required String password,
|
||||
}) async {
|
||||
try {
|
||||
final result = await _authApiClient.login(
|
||||
LoginRequest(
|
||||
email: email,
|
||||
password: password,
|
||||
),
|
||||
);
|
||||
switch (result) {
|
||||
case Ok<LoginResponse>():
|
||||
_log.info('User logged int');
|
||||
// Set auth status
|
||||
_isAuthenticated = true;
|
||||
_authToken = result.value.token;
|
||||
// Store in Shared preferences
|
||||
return await _sharedPreferencesService.saveToken(result.value.token);
|
||||
case Error<LoginResponse>():
|
||||
_log.warning('Error logging in: ${result.error}');
|
||||
return Result.error(result.error);
|
||||
}
|
||||
} finally {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<void>> logout() async {
|
||||
_log.info('User logged out');
|
||||
try {
|
||||
// Clear stored auth token
|
||||
final result = await _sharedPreferencesService.saveToken(null);
|
||||
if (result is Error<void>) {
|
||||
_log.severe('Failed to clear stored auth token');
|
||||
}
|
||||
|
||||
// Clear token in ApiClient
|
||||
_authToken = null;
|
||||
|
||||
// Clear authenticated status
|
||||
_isAuthenticated = false;
|
||||
return result;
|
||||
} finally {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
String? _authHeaderProvider() =>
|
||||
_authToken != null ? 'Bearer $_authToken' : null;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/booking/booking.dart';
|
||||
import '../../../domain/models/booking/booking_summary.dart';
|
||||
import '../../../utils/result.dart';
|
||||
|
||||
abstract class BookingRepository {
|
||||
/// Returns the list of [BookingSummary] for the current user.
|
||||
Future<Result<List<BookingSummary>>> getBookingsList();
|
||||
|
||||
/// Returns a full [Booking] given the id.
|
||||
Future<Result<Booking>> getBooking(int id);
|
||||
|
||||
/// Creates a new [Booking].
|
||||
Future<Result<void>> createBooking(Booking booking);
|
||||
|
||||
/// Delete booking
|
||||
Future<Result<void>> delete(int id);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import '../../../domain/models/booking/booking.dart';
|
||||
import '../../../domain/models/booking/booking_summary.dart';
|
||||
import '../../../utils/result.dart';
|
||||
|
||||
import '../../services/local/local_data_service.dart';
|
||||
import 'booking_repository.dart';
|
||||
|
||||
class BookingRepositoryLocal implements BookingRepository {
|
||||
BookingRepositoryLocal({
|
||||
required LocalDataService localDataService,
|
||||
}) : _localDataService = localDataService;
|
||||
|
||||
// Only create default booking once
|
||||
bool _isInitialized = false;
|
||||
// Used to generate IDs for bookings
|
||||
int _sequentialId = 0;
|
||||
|
||||
final _bookings = List<Booking>.empty(growable: true);
|
||||
final LocalDataService _localDataService;
|
||||
|
||||
@override
|
||||
Future<Result<void>> createBooking(Booking booking) async {
|
||||
// Bookings created come without id, we need to assign one
|
||||
final bookingWithId = booking.copyWith(id: _sequentialId++);
|
||||
_bookings.add(bookingWithId);
|
||||
return Result.ok(null);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<Booking>> getBooking(int id) async {
|
||||
final booking = _bookings.firstWhereOrNull((booking) => booking.id == id);
|
||||
if (booking == null) {
|
||||
return Result.error(Exception('Booking not found'));
|
||||
}
|
||||
return Result.ok(booking);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<List<BookingSummary>>> getBookingsList() async {
|
||||
// Initialize the repository with a default booking
|
||||
if (!_isInitialized) {
|
||||
await _createDefaultBooking();
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
return Result.ok(_createSummaries());
|
||||
}
|
||||
|
||||
List<BookingSummary> _createSummaries() {
|
||||
return _bookings
|
||||
.map(
|
||||
(booking) => BookingSummary(
|
||||
id: booking.id!,
|
||||
name:
|
||||
'${booking.destination.name}, ${booking.destination.continent}',
|
||||
startDate: booking.startDate,
|
||||
endDate: booking.endDate,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<void> _createDefaultBooking() async {
|
||||
// create a default booking the first time
|
||||
if (_bookings.isEmpty) {
|
||||
final destination = (await _localDataService.getDestinations()).first;
|
||||
final activities = (await _localDataService.getActivities())
|
||||
.where((activity) => activity.destinationRef == destination.ref)
|
||||
.take(4)
|
||||
.toList();
|
||||
|
||||
_bookings.add(
|
||||
Booking(
|
||||
id: _sequentialId++,
|
||||
startDate: DateTime(2024, 1, 1),
|
||||
endDate: DateTime(2024, 2, 1),
|
||||
destination: destination,
|
||||
activity: activities,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<void>> delete(int id) async {
|
||||
_bookings.removeWhere((booking) => booking.id == id);
|
||||
return Result.ok(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/activity/activity.dart';
|
||||
import '../../../domain/models/booking/booking.dart';
|
||||
import '../../../domain/models/booking/booking_summary.dart';
|
||||
import '../../../domain/models/destination/destination.dart';
|
||||
import '../../../utils/result.dart';
|
||||
import '../../services/api/api_client.dart';
|
||||
import '../../services/api/model/booking/booking_api_model.dart';
|
||||
import 'booking_repository.dart';
|
||||
|
||||
class BookingRepositoryRemote implements BookingRepository {
|
||||
BookingRepositoryRemote({
|
||||
required ApiClient apiClient,
|
||||
}) : _apiClient = apiClient;
|
||||
|
||||
final ApiClient _apiClient;
|
||||
|
||||
List<Destination>? _cachedDestinations;
|
||||
|
||||
@override
|
||||
Future<Result<void>> createBooking(Booking booking) async {
|
||||
try {
|
||||
final BookingApiModel bookingApiModel = BookingApiModel(
|
||||
startDate: booking.startDate,
|
||||
endDate: booking.endDate,
|
||||
name: '${booking.destination.name}, ${booking.destination.continent}',
|
||||
destinationRef: booking.destination.ref,
|
||||
activitiesRef:
|
||||
booking.activity.map((activity) => activity.ref).toList(),
|
||||
);
|
||||
return _apiClient.postBooking(bookingApiModel);
|
||||
} on Exception catch (e) {
|
||||
return Result.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<Booking>> getBooking(int id) async {
|
||||
try {
|
||||
// Get booking by ID from server
|
||||
final resultBooking = await _apiClient.getBooking(id);
|
||||
if (resultBooking is Error<BookingApiModel>) {
|
||||
return Result.error(resultBooking.error);
|
||||
}
|
||||
final booking = resultBooking.asOk.value;
|
||||
|
||||
// Load destinations if not loaded yet
|
||||
if (_cachedDestinations == null) {
|
||||
final resultDestination = await _apiClient.getDestinations();
|
||||
if (resultDestination is Error<List<Destination>>) {
|
||||
return Result.error(resultDestination.error);
|
||||
}
|
||||
_cachedDestinations = resultDestination.asOk.value;
|
||||
}
|
||||
|
||||
// Get destination for booking
|
||||
final destination = _cachedDestinations!.firstWhere(
|
||||
(destination) => destination.ref == booking.destinationRef);
|
||||
|
||||
final resultActivities =
|
||||
await _apiClient.getActivityByDestination(destination.ref);
|
||||
|
||||
if (resultActivities is Error<List<Activity>>) {
|
||||
return Result.error(resultActivities.error);
|
||||
}
|
||||
final activities = resultActivities.asOk.value
|
||||
.where((activity) => booking.activitiesRef.contains(activity.ref))
|
||||
.toList();
|
||||
|
||||
return Result.ok(
|
||||
Booking(
|
||||
id: booking.id,
|
||||
startDate: booking.startDate,
|
||||
endDate: booking.endDate,
|
||||
destination: destination,
|
||||
activity: activities,
|
||||
),
|
||||
);
|
||||
} on Exception catch (e) {
|
||||
return Result.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<List<BookingSummary>>> getBookingsList() async {
|
||||
try {
|
||||
final result = await _apiClient.getBookings();
|
||||
if (result is Error<List<BookingApiModel>>) {
|
||||
return Result.error(result.error);
|
||||
}
|
||||
final bookingsApi = result.asOk.value;
|
||||
return Result.ok(bookingsApi
|
||||
.map(
|
||||
(bookingApi) => BookingSummary(
|
||||
id: bookingApi.id!,
|
||||
name: bookingApi.name,
|
||||
startDate: bookingApi.startDate,
|
||||
endDate: bookingApi.endDate,
|
||||
),
|
||||
)
|
||||
.toList());
|
||||
} on Exception catch (e) {
|
||||
return Result.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<void>> delete(int id) async {
|
||||
try {
|
||||
return _apiClient.deleteBooking(id);
|
||||
} on Exception catch (e) {
|
||||
return Result.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/continent/continent.dart';
|
||||
import '../../../utils/result.dart';
|
||||
|
||||
/// Data source with all possible continents.
|
||||
abstract class ContinentRepository {
|
||||
/// Get complete list of continents.
|
||||
Future<Result<List<Continent>>> getContinents();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/continent/continent.dart';
|
||||
import '../../../utils/result.dart';
|
||||
import '../../services/local/local_data_service.dart';
|
||||
import 'continent_repository.dart';
|
||||
|
||||
/// Local data source with all possible continents.
|
||||
class ContinentRepositoryLocal implements ContinentRepository {
|
||||
ContinentRepositoryLocal({
|
||||
required LocalDataService localDataService,
|
||||
}) : _localDataService = localDataService;
|
||||
|
||||
final LocalDataService _localDataService;
|
||||
|
||||
@override
|
||||
Future<Result<List<Continent>>> getContinents() async {
|
||||
return Future.value(Result.ok(_localDataService.getContinents()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/continent/continent.dart';
|
||||
import '../../../utils/result.dart';
|
||||
import '../../services/api/api_client.dart';
|
||||
import 'continent_repository.dart';
|
||||
|
||||
/// Remote data source for [Continent].
|
||||
/// Implements basic local caching.
|
||||
/// See: https://docs.flutter.dev/get-started/fwe/local-caching
|
||||
class ContinentRepositoryRemote implements ContinentRepository {
|
||||
ContinentRepositoryRemote({
|
||||
required ApiClient apiClient,
|
||||
}) : _apiClient = apiClient;
|
||||
|
||||
final ApiClient _apiClient;
|
||||
|
||||
List<Continent>? _cachedData;
|
||||
|
||||
@override
|
||||
Future<Result<List<Continent>>> getContinents() async {
|
||||
if (_cachedData == null) {
|
||||
// No cached data, request continents
|
||||
final result = await _apiClient.getContinents();
|
||||
if (result is Ok) {
|
||||
// Store value if result Ok
|
||||
_cachedData = result.asOk.value;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
// Return cached data if available
|
||||
return Result.ok(_cachedData!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/destination/destination.dart';
|
||||
import '../../../utils/result.dart';
|
||||
|
||||
/// Data source with all possible destinations
|
||||
abstract class DestinationRepository {
|
||||
/// Get complete list of destinations
|
||||
Future<Result<List<Destination>>> getDestinations();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/destination/destination.dart';
|
||||
import '../../../utils/result.dart';
|
||||
import '../../services/local/local_data_service.dart';
|
||||
import 'destination_repository.dart';
|
||||
|
||||
/// Local implementation of DestinationRepository
|
||||
/// Uses data from assets folder
|
||||
class DestinationRepositoryLocal implements DestinationRepository {
|
||||
DestinationRepositoryLocal({
|
||||
required LocalDataService localDataService,
|
||||
}) : _localDataService = localDataService;
|
||||
|
||||
final LocalDataService _localDataService;
|
||||
|
||||
/// Obtain list of destinations from local assets
|
||||
@override
|
||||
Future<Result<List<Destination>>> getDestinations() async {
|
||||
try {
|
||||
return Result.ok(await _localDataService.getDestinations());
|
||||
} on Exception catch (error) {
|
||||
return Result.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/destination/destination.dart';
|
||||
import '../../../utils/result.dart';
|
||||
import '../../services/api/api_client.dart';
|
||||
import 'destination_repository.dart';
|
||||
|
||||
/// Remote data source for [Destination].
|
||||
/// Implements basic local caching.
|
||||
/// See: https://docs.flutter.dev/get-started/fwe/local-caching
|
||||
class DestinationRepositoryRemote implements DestinationRepository {
|
||||
DestinationRepositoryRemote({
|
||||
required ApiClient apiClient,
|
||||
}) : _apiClient = apiClient;
|
||||
|
||||
final ApiClient _apiClient;
|
||||
|
||||
List<Destination>? _cachedData;
|
||||
|
||||
@override
|
||||
Future<Result<List<Destination>>> getDestinations() async {
|
||||
if (_cachedData == null) {
|
||||
// No cached data, request destinations
|
||||
final result = await _apiClient.getDestinations();
|
||||
if (result is Ok) {
|
||||
// Store value if result Ok
|
||||
_cachedData = result.asOk.value;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
// Return cached data if available
|
||||
return Result.ok(_cachedData!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/itinerary_config/itinerary_config.dart';
|
||||
import '../../../utils/result.dart';
|
||||
|
||||
/// Data source for the current [ItineraryConfig]
|
||||
abstract class ItineraryConfigRepository {
|
||||
/// Get current [ItineraryConfig], may be empty if no configuration started.
|
||||
/// Method is async to support writing to database, file, etc.
|
||||
Future<Result<ItineraryConfig>> getItineraryConfig();
|
||||
|
||||
/// Sets [ItineraryConfig], overrides the previous one stored.
|
||||
/// Returns Result.Ok if set is successful.
|
||||
Future<Result<void>> setItineraryConfig(ItineraryConfig itineraryConfig);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import '../../../domain/models/itinerary_config/itinerary_config.dart';
|
||||
import '../../../utils/result.dart';
|
||||
import 'itinerary_config_repository.dart';
|
||||
|
||||
/// In-memory implementation of [ItineraryConfigRepository].
|
||||
class ItineraryConfigRepositoryMemory implements ItineraryConfigRepository {
|
||||
ItineraryConfig? _itineraryConfig;
|
||||
|
||||
@override
|
||||
Future<Result<ItineraryConfig>> getItineraryConfig() async {
|
||||
return Result.ok(_itineraryConfig ?? const ItineraryConfig());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<bool>> setItineraryConfig(
|
||||
ItineraryConfig itineraryConfig,
|
||||
) async {
|
||||
_itineraryConfig = itineraryConfig;
|
||||
return Result.ok(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/user/user.dart';
|
||||
import '../../../utils/result.dart';
|
||||
|
||||
/// Data source for user related data
|
||||
abstract class UserRepository {
|
||||
/// Get current user
|
||||
Future<Result<User>> getUser();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/user/user.dart';
|
||||
import '../../../utils/result.dart';
|
||||
import '../../services/local/local_data_service.dart';
|
||||
import 'user_repository.dart';
|
||||
|
||||
class UserRepositoryLocal implements UserRepository {
|
||||
UserRepositoryLocal({
|
||||
required LocalDataService localDataService,
|
||||
}) : _localDataService = localDataService;
|
||||
|
||||
final LocalDataService _localDataService;
|
||||
|
||||
@override
|
||||
Future<Result<User>> getUser() async {
|
||||
return Result.ok(_localDataService.getUser());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2024 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import '../../../domain/models/user/user.dart';
|
||||
import '../../../utils/result.dart';
|
||||
import '../../services/api/api_client.dart';
|
||||
import '../../services/api/model/user/user_api_model.dart';
|
||||
import 'user_repository.dart';
|
||||
|
||||
class UserRepositoryRemote implements UserRepository {
|
||||
UserRepositoryRemote({
|
||||
required ApiClient apiClient,
|
||||
}) : _apiClient = apiClient;
|
||||
|
||||
final ApiClient _apiClient;
|
||||
|
||||
User? _cachedData;
|
||||
|
||||
@override
|
||||
Future<Result<User>> getUser() async {
|
||||
if (_cachedData != null) {
|
||||
return Future.value(Result.ok(_cachedData!));
|
||||
}
|
||||
|
||||
final result = await _apiClient.getUser();
|
||||
switch (result) {
|
||||
case Ok<UserApiModel>():
|
||||
final user = User(
|
||||
name: result.value.name,
|
||||
picture: result.value.picture,
|
||||
);
|
||||
_cachedData = user;
|
||||
return Result.ok(user);
|
||||
case Error<UserApiModel>():
|
||||
return Result.error(result.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user