feat: integrate enakgame and voucher
Build & Deploy iOS to TestFlight / build-and-deploy (push) Waiting to run
Build & Deploy iOS to TestFlight / build-and-deploy (push) Waiting to run
This commit is contained in:
@@ -1,40 +1,55 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../../injection.dart';
|
||||
import '../../../presentation/router/app_router.dart';
|
||||
import '../../../presentation/router/app_router.gr.dart';
|
||||
import '../../constant/local_storage_key.dart';
|
||||
import '../errors/unauthorized_error.dart';
|
||||
|
||||
/// Token login ditolak server: HTTP `401`, atau `304` dengan `entity`
|
||||
/// `auth_handler` (§2.2). Belum ada refresh token, jadi customer harus login
|
||||
/// ulang.
|
||||
///
|
||||
/// Bukan `403`: `PIN_NOT_SET` dan `TRANSFER_BLOCKED` juga memakai `403`.
|
||||
bool isTokenRejected(Response response) {
|
||||
if (response.statusCode == 401) return true;
|
||||
|
||||
final body = response.data;
|
||||
if (body is! Map) return false;
|
||||
final errors = body['errors'];
|
||||
if (errors is! List || errors.isEmpty || errors.first is! Map) return false;
|
||||
|
||||
final first = errors.first as Map;
|
||||
return first['code']?.toString() == '304' &&
|
||||
first['entity'] == 'auth_handler';
|
||||
}
|
||||
|
||||
/// Menghapus sesi dan membuka halaman login bila token yang dipakai request
|
||||
/// ditolak. `validateStatus` menerima semua status, jadi pengecekan ada di
|
||||
/// [onResponse], bukan `onError`.
|
||||
class UnauthorizedInterceptor extends Interceptor {
|
||||
static final GlobalKey<NavigatorState> navigatorKey =
|
||||
GlobalKey<NavigatorState>();
|
||||
@override
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) async {
|
||||
if (err.response?.statusCode == 401 ||
|
||||
err.response?.statusCode == 403 ||
|
||||
err.response?.statusCode == 419) {
|
||||
await _handleTokenExpired();
|
||||
return super.onError(UnauthorizedError(err, null), handler);
|
||||
void onResponse(Response response, ResponseInterceptorHandler handler) async {
|
||||
try {
|
||||
if (isTokenRejected(response)) await _handleTokenRejected(response);
|
||||
} finally {
|
||||
handler.next(response);
|
||||
}
|
||||
super.onError(err, handler);
|
||||
}
|
||||
|
||||
Future<void> _handleTokenExpired() async {
|
||||
// Clear stored token
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
Future<void> _handleTokenRejected(Response response) async {
|
||||
final prefs = getIt<SharedPreferences>();
|
||||
final token = prefs.getString(LocalStorageKey.token);
|
||||
// Hanya token yang sedang dipakai: abaikan request tanpa login dan jawaban
|
||||
// basi dari token lama setelah customer login lagi.
|
||||
final sentAuthorization = response.requestOptions.headers['Authorization'];
|
||||
if (token == null || sentAuthorization != 'Bearer $token') return;
|
||||
|
||||
log('token rejected', name: 'UnauthorizedInterceptor');
|
||||
await prefs.remove(LocalStorageKey.token);
|
||||
await prefs.remove(LocalStorageKey.user);
|
||||
await prefs.clear(); // Optional: clear all user data
|
||||
log('handleTokenExpired');
|
||||
// Navigate to login page
|
||||
final context = navigatorKey.currentContext;
|
||||
if (context != null) {
|
||||
// Option 1: Navigate and remove all previous routes
|
||||
context.router.replaceAll([LoginRoute()]);
|
||||
}
|
||||
getIt<AppRouter>().replaceAll([LoginRoute()]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user