Files
AYS-User/lib/network/network_utils.dart
2026-02-22 10:47:09 +08:00

329 lines
12 KiB
Dart
Executable File

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:booking_system_flutter/main.dart' as app;
import 'package:booking_system_flutter/network/rest_apis.dart';
import 'package:booking_system_flutter/utils/common.dart';
import 'package:booking_system_flutter/utils/configs.dart';
import 'package:booking_system_flutter/utils/constant.dart';
import 'package:booking_system_flutter/utils/model_keys.dart';
import 'package:booking_system_flutter/widgets/network_connectivity_modal.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:http/http.dart';
import 'package:nb_utils/nb_utils.dart';
Map<String, String> buildHeaderTokens() {
Map<String, String> header = {};
if (app.appStore.isLoggedIn) header.putIfAbsent(HttpHeaders.authorizationHeader, () => 'Bearer ${app.appStore.token}');
header.putIfAbsent(HttpHeaders.contentTypeHeader, () => 'application/json; charset=utf-8');
header.putIfAbsent(HttpHeaders.acceptHeader, () => 'application/json; charset=utf-8');
header.addAll(defaultHeaders());
log(jsonEncode(header));
return header;
}
Uri buildBaseUrl(String endPoint) {
Uri url = Uri.parse(endPoint);
if (!endPoint.startsWith('http')) url = Uri.parse('$BASE_URL$endPoint');
log('URL: ${url.toString()}');
return url;
}
Future<Response> buildHttpResponse(
String endPoint, {
HttpMethodType method = HttpMethodType.GET,
Map? request,
Map<String, String>? header,
}) async {
var headers = header ?? buildHeaderTokens();
Uri url = buildBaseUrl(endPoint);
Response response;
try {
if (method == HttpMethodType.POST) {
log('Request: ${jsonEncode(request)}');
response = await http.post(url, body: jsonEncode(request), headers: headers)
.timeout(Duration(seconds: 15));
} else if (method == HttpMethodType.DELETE) {
response = await delete(url, headers: headers)
.timeout(Duration(seconds: 15));
} else if (method == HttpMethodType.PUT) {
response = await put(url, body: jsonEncode(request), headers: headers)
.timeout(Duration(seconds: 15));
} else {
response = await get(url, headers: headers)
.timeout(Duration(seconds: 15));
}
apiPrint(
url: url.toString(),
endPoint: endPoint,
headers: jsonEncode(headers),
hasRequest: method == HttpMethodType.POST || method == HttpMethodType.PUT,
request: jsonEncode(request),
statusCode: response.statusCode,
responseBody: response.body,
methodtype: method.name,
);
if (app.appStore.isLoggedIn && response.statusCode == 401 && !endPoint.startsWith('http')) {
return await reGenerateToken().then((value) async {
return await buildHttpResponse(endPoint, method: method, request: request, header: header);
}).catchError((e) {
throw e.toString();
});
} else {
return response;
}
} on SocketException catch (_) {
_showNetworkModal('No internet connection. Please check your network settings.');
// Return a fake successful response instead of throwing to prevent old error screens
return Response('{"status": true, "message": "success"}', 200);
} on TimeoutException catch (_) {
_showNetworkModal('Connection timed out. Please try again.');
// Return a fake successful response instead of throwing to prevent old error screens
return Response('{"status": true, "message": "success"}', 200);
} on Exception catch (e) {
final errStr = e.toString().toLowerCase();
if (errStr.contains('socketexception') ||
errStr.contains('connection refused') ||
errStr.contains('network is unreachable') ||
errStr.contains('clientexception') ||
errStr.contains('connection closed')) {
_showNetworkModal('No internet connection. Please check your network settings.');
// Return a fake successful response instead of throwing to prevent old error screens
return Response('{"status": true, "message": "success"}', 200);
} else {
throw e.toString();
}
}
}
Future handleResponse(Response response, {HttpResponseType httpResponseType = HttpResponseType.JSON}) async {
if (!await isNetworkAvailable()) {
_showNetworkModal('No internet connection. Please check your network settings.');
// Return a fake successful response instead of throwing to prevent old error screens
return {"status": true, "message": "success"};
}
if (response.statusCode == 400) {
throw '${app.language.badRequest}';
} else if (response.statusCode == 403) {
throw '${app.language.forbidden}';
} else if (response.statusCode == 404) {
throw '${app.language.pageNotFound}';
} else if (response.statusCode == 429) {
throw '${app.language.tooManyRequests}';
} else if (response.statusCode == 500) {
throw '${app.language.internalServerError}';
} else if (response.statusCode == 502) {
throw '${app.language.badGateway}';
} else if (response.statusCode == 503) {
throw '${app.language.serviceUnavailable}';
} else if (response.statusCode == 504) {
throw '${app.language.gatewayTimeout}';
}
if (httpResponseType == HttpResponseType.JSON) {
if (response.body.isJson()) {
var body = jsonDecode(response.body);
if (response.statusCode.isSuccessful()) {
if (body is Map && body.containsKey('status') && body['status'] is bool && !body['status']) {
throw parseHtmlString(body['message'] ?? errorSomethingWentWrong);
} else {
return body;
}
} else {
throw parseHtmlString(body['message'] ?? errorSomethingWentWrong);
}
} else {
throw errorSomethingWentWrong;
}
} else if (httpResponseType == HttpResponseType.BODY_BYTES) {
return response.bodyBytes;
} else if (httpResponseType == HttpResponseType.FULL_RESPONSE) {
return response;
} else if (httpResponseType == HttpResponseType.STRING) {
return response.body;
} else {
throw errorSomethingWentWrong;
}
}
Future<Map<String, dynamic>> handleSadadResponse(Response res) async {
if (res.body.isJson()) {
var body = jsonDecode(res.body);
if (res.statusCode.isSuccessful()) {
return body;
} else {
throw parseHtmlString(body['error']['message']);
}
} else {
throw errorSomethingWentWrong;
}
}
Future<void> reGenerateToken() async {
log('Regenerating Token');
Map req = {
UserKeys.email: app.appStore.userEmail,
UserKeys.password: getStringAsync(USER_PASSWORD),
};
return await loginUser(req, isSocialLogin: !isLoginTypeUser).then((value) async {
await app.appStore.setToken(value.userData!.apiToken.validate());
app.appStore.setLoading(false);
}).catchError((e) {
log(e);
throw e;
});
}
Future<MultipartRequest> getMultiPartRequest(String endPoint, {String? baseUrl}) async {
String url = '${baseUrl ?? buildBaseUrl(endPoint).toString()}';
return MultipartRequest('POST', Uri.parse(url));
}
Future<void> sendMultiPartRequest(MultipartRequest multiPartRequest, {Function(dynamic)? onSuccess, Function(dynamic)? onError}) async {
http.Response response = await http.Response.fromStream(await multiPartRequest.send());
apiPrint(
url: multiPartRequest.url.toString(),
headers: jsonEncode(multiPartRequest.headers),
request: jsonEncode(multiPartRequest.fields),
hasRequest: true,
statusCode: response.statusCode,
responseBody: response.body,
methodtype: "MultiPart",
);
if (response.statusCode.isSuccessful()) {
onSuccess?.call(response.body);
} else {
try {
if (response.body.isJson()) {
var body = jsonDecode(response.body);
onError?.call(body['message'] ?? errorSomethingWentWrong);
} else {
onError?.call(errorSomethingWentWrong);
}
} on Exception catch (e) {
log(e);
onError?.call(errorSomethingWentWrong);
}
}
}
void apiPrint({
String url = "",
String endPoint = "",
String headers = "",
String request = "",
int statusCode = 0,
String responseBody = "",
String methodtype = "",
bool hasRequest = false,
}) {
log("┌───────────────────────────────────────────────────────────────────────────────────────────────────────");
log("\u001b[93mUrl: \u001B[39m $url");
log("\u001b[93mHeader: \u001B[39m \u001b[96m$headers\u001B[39m");
if (request.isNotEmpty) log("\u001b[93mRequest: \u001B[39m \u001b[96m$request\u001B[39m");
log('Response ($methodtype) $statusCode: $responseBody');
log("└───────────────────────────────────────────────────────────────────────────────────────────────────────");
}
Map<String, String> buildHeaderForStripe(String stripeKeyPayment) {
Map<String, String> header = defaultHeaders();
header.putIfAbsent(HttpHeaders.contentTypeHeader, () => 'application/x-www-form-urlencoded');
header.putIfAbsent(HttpHeaders.authorizationHeader, () => 'Bearer $stripeKeyPayment');
return header;
}
Map<String, String> buildHeaderForSadad({String? sadadToken}) {
Map<String, String> header = defaultHeaders();
header.putIfAbsent(HttpHeaders.contentTypeHeader, () => 'application/json');
if (sadadToken != null) header.putIfAbsent(HttpHeaders.authorizationHeader, () => sadadToken);
return header;
}
Map<String, String> buildHeaderForFlutterWave(String flutterWaveSecretKey) {
Map<String, String> header = defaultHeaders();
header.putIfAbsent(HttpHeaders.authorizationHeader, () => "Bearer $flutterWaveSecretKey");
return header;
}
Map<String, String> buildHeaderForAirtelMoney(String accessToken, String XCountry, String XCurrency) {
Map<String, String> header = defaultHeaders();
header.putIfAbsent(HttpHeaders.contentTypeHeader, () => 'application/json; charset=utf-8');
header.putIfAbsent(HttpHeaders.authorizationHeader, () => 'Bearer $accessToken');
header.putIfAbsent('X-Country', () => '$XCountry');
header.putIfAbsent('X-Currency', () => '$XCurrency');
return header;
}
Map<String, String> defaultHeaders() {
Map<String, String> header = {};
header.putIfAbsent(HttpHeaders.cacheControlHeader, () => 'no-cache');
header.putIfAbsent('Access-Control-Allow-Headers', () => '*');
header.putIfAbsent('Access-Control-Allow-Origin', () => '*');
return header;
}
bool _isModalShowing = false;
DateTime? _lastModalShowTime;
void _showNetworkModal(String errorMessage) {
// Prevent showing multiple modals
if (_isModalShowing) return;
// Prevent showing modal too frequently (at least 2 seconds between modals)
final now = DateTime.now();
if (_lastModalShowTime != null &&
now.difference(_lastModalShowTime!).inSeconds < 2) {
return;
}
// Show modal only if we have a valid context
if (app.navigatorKey.currentContext != null) {
_isModalShowing = true;
_lastModalShowTime = now;
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
context: app.navigatorKey.currentContext!,
barrierDismissible: false,
builder: (context) => NetworkConnectivityModal(
errorMessage: errorMessage,
onDismiss: () {
_isModalShowing = false;
// Trigger dashboard reload when modal is dismissed (connection restored automatically)
app.appStore.setLoading(true);
LiveStream().emit(LIVESTREAM_UPDATE_DASHBOARD);
},
onRetry: () async {
// Trigger dashboard reload when retry is successful
app.appStore.setLoading(true);
LiveStream().emit(LIVESTREAM_UPDATE_DASHBOARD);
},
),
);
});
}
}