MIF_E31231623/android/wisata_app/lib/services/destination_service.dart

159 lines
4.5 KiB
Dart

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:wisata_app/config/api_config.dart';
import 'package:wisata_app/models/destination.dart';
class DestinationService {
const DestinationService({http.Client? client}) : _client = client;
final http.Client? _client;
static List<Destination> localDestinations = [];
static List<Destination> get destinations => localDestinations;
Future<List<Destination>> getAllDestinations() async {
final client = _client ?? http.Client();
try {
final items = <dynamic>[];
var currentPage = 1;
var lastPage = 1;
do {
final uri = ApiConfig.apiUri('/destinations').replace(
queryParameters: {
'per_page': '100',
'page': currentPage.toString(),
},
);
final response = await client.get(uri).timeout(
const Duration(seconds: 15),
);
if (response.statusCode < 200 || response.statusCode >= 300) {
throw DestinationServiceException(
'API destinasi gagal dimuat. HTTP ${response.statusCode}.',
);
}
final decoded = jsonDecode(response.body);
items.addAll(_extractItems(decoded));
lastPage = _lastPageFrom(decoded);
currentPage++;
} while (currentPage <= lastPage);
final destinations = items
.whereType<Map<String, dynamic>>()
.map(Destination.fromJson)
.where((destination) => destination.title.isNotEmpty)
.toList();
localDestinations = destinations;
return destinations;
} finally {
if (_client == null) client.close();
}
}
Future<List<Destination>> getDestinations() => getAllDestinations();
Future<Destination?> getDestinationDetail(String id) async {
try {
return await getDestinationById(id);
} on DestinationServiceException {
return _findCachedById(id);
}
}
Future<Destination> getDestinationById(String id) async {
final client = _client ?? http.Client();
try {
final response = await client
.get(ApiConfig.apiUri('/destinations/$id'))
.timeout(const Duration(seconds: 15));
if (response.statusCode < 200 || response.statusCode >= 300) {
throw DestinationServiceException(
'Detail destinasi gagal dimuat. HTTP ${response.statusCode}.',
);
}
final decoded = jsonDecode(response.body);
final data = decoded is Map<String, dynamic> ? decoded['data'] : decoded;
if (data is! Map<String, dynamic>) {
throw const DestinationServiceException(
'Format detail destinasi dari API tidak valid.',
);
}
final destination = Destination.fromJson(data);
final nextDestinations = [...localDestinations];
final index = nextDestinations.indexWhere((item) => item.id == id);
if (index >= 0) {
nextDestinations[index] = destination;
} else {
nextDestinations.add(destination);
}
localDestinations = nextDestinations;
return destination;
} finally {
if (_client == null) client.close();
}
}
List<Destination> get localDestinationsSnapshot => localDestinations;
Destination findById(String id) {
final destination = _findCachedById(id);
if (destination != null) return destination;
throw DestinationServiceException(
'Destinasi dengan ID $id tidak ditemukan.',
);
}
static Destination? _findCachedById(String id) {
for (final item in localDestinations) {
if (item.id == id) return item;
}
return null;
}
List<dynamic> _extractItems(Object? decoded) {
if (decoded is List) return decoded;
if (decoded is Map<String, dynamic>) {
final data = decoded['data'];
if (data is List) return data;
if (data is Map<String, dynamic>) {
final nestedData = data['data'];
if (nestedData is List) return nestedData;
}
}
throw const DestinationServiceException(
'Format daftar destinasi dari API tidak valid.',
);
}
int _lastPageFrom(Object? decoded) {
if (decoded is Map<String, dynamic>) {
final meta = decoded['meta'];
if (meta is Map<String, dynamic>) {
final lastPage = meta['last_page'];
if (lastPage is num) return lastPage.toInt();
if (lastPage is String) return int.tryParse(lastPage) ?? 1;
}
}
return 1;
}
}
class DestinationServiceException implements Exception {
const DestinationServiceException(this.message);
final String message;
@override
String toString() => message;
}