159 lines
4.5 KiB
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;
|
|
}
|