feat: split legacy/payment apps via APP_MODE flag
This commit is contained in:
55
.vscode/launch.json
vendored
55
.vscode/launch.json
vendored
@@ -2,39 +2,82 @@
|
|||||||
// Use IntelliSense to learn about possible attributes.
|
// Use IntelliSense to learn about possible attributes.
|
||||||
// Hover to view descriptions of existing attributes.
|
// Hover to view descriptions of existing attributes.
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
//
|
||||||
|
// Configurations are split between (Legacy) and (Payment) variants.
|
||||||
|
// (Legacy) is the default and matches historical behavior; (Payment)
|
||||||
|
// boots straight into the Treezor wallet flow via APP_MODE=payment.
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "SF Development",
|
"name": "SF Development (Legacy)",
|
||||||
"cwd": "apps/mobile_app",
|
"cwd": "apps/mobile_app",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"args": [
|
"args": [
|
||||||
"--flavor",
|
"--flavor",
|
||||||
"development",
|
"development",
|
||||||
"--dart-define-from-file=config/development.json"
|
"--dart-define-from-file=config/development.json",
|
||||||
|
"--dart-define=APP_MODE=legacy"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "SF Staging",
|
"name": "SF Development (Payment)",
|
||||||
|
"cwd": "apps/mobile_app",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"development",
|
||||||
|
"--dart-define-from-file=config/development.json",
|
||||||
|
"--dart-define=APP_MODE=payment"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SF Staging (Legacy)",
|
||||||
"cwd": "apps/mobile_app",
|
"cwd": "apps/mobile_app",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"args": [
|
"args": [
|
||||||
"--flavor",
|
"--flavor",
|
||||||
"staging",
|
"staging",
|
||||||
"--dart-define-from-file=config/staging.json"
|
"--dart-define-from-file=config/staging.json",
|
||||||
|
"--dart-define=APP_MODE=legacy"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "SF Production",
|
"name": "SF Staging (Payment)",
|
||||||
|
"cwd": "apps/mobile_app",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"staging",
|
||||||
|
"--dart-define-from-file=config/staging.json",
|
||||||
|
"--dart-define=APP_MODE=payment"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SF Production (Legacy)",
|
||||||
"cwd": "apps/mobile_app",
|
"cwd": "apps/mobile_app",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"args": [
|
"args": [
|
||||||
"--flavor",
|
"--flavor",
|
||||||
"production",
|
"production",
|
||||||
"--dart-define-from-file=config/production.json"
|
"--dart-define-from-file=config/production.json",
|
||||||
|
"--dart-define=APP_MODE=legacy"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SF Production (Payment)",
|
||||||
|
"cwd": "apps/mobile_app",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"production",
|
||||||
|
"--dart-define-from-file=config/production.json",
|
||||||
|
"--dart-define=APP_MODE=payment"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
20
apps/mobile_app/lib/core/config/app_mode.dart
Normal file
20
apps/mobile_app/lib/core/config/app_mode.dart
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/// Compile-time constant that controls which app the splash screen
|
||||||
|
/// navigates to when the app starts.
|
||||||
|
///
|
||||||
|
/// Set via `--dart-define=APP_MODE=payment` (or `legacy`) at launch time.
|
||||||
|
/// Defaults to `legacy` to preserve historical behavior when no flag is
|
||||||
|
/// passed (e.g. `flutter run` from CLI without arguments).
|
||||||
|
///
|
||||||
|
/// Used only for local development to switch between the legacy app
|
||||||
|
/// (watch/device control) and the payment app (Treezor wallet) without
|
||||||
|
/// needing separate flavors or entry points.
|
||||||
|
const String appMode = String.fromEnvironment(
|
||||||
|
'APP_MODE',
|
||||||
|
defaultValue: 'legacy',
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Whether the app should boot into the payment (Treezor wallet) flow.
|
||||||
|
bool get isPaymentMode => appMode == 'payment';
|
||||||
|
|
||||||
|
/// Whether the app should boot into the legacy (watch/device) flow.
|
||||||
|
bool get isLegacyMode => appMode == 'legacy';
|
||||||
@@ -7,6 +7,7 @@ import 'package:design_system/design_system.dart';
|
|||||||
import 'package:sca_treezor/sca_treezor.dart';
|
import 'package:sca_treezor/sca_treezor.dart';
|
||||||
import 'package:sf_app_platform/config/env/environment_enum.dart';
|
import 'package:sf_app_platform/config/env/environment_enum.dart';
|
||||||
import 'package:sf_app_platform/config/env/questia_env_config.dart';
|
import 'package:sf_app_platform/config/env/questia_env_config.dart';
|
||||||
|
import 'package:sf_app_platform/core/config/app_mode.dart';
|
||||||
import 'package:sf_app_platform/navigation/app_router.dart';
|
import 'package:sf_app_platform/navigation/app_router.dart';
|
||||||
import 'package:sf_app_platform/save_family_app.dart';
|
import 'package:sf_app_platform/save_family_app.dart';
|
||||||
import 'package:navigation/navigation.dart';
|
import 'package:navigation/navigation.dart';
|
||||||
@@ -30,9 +31,11 @@ Future<void> initApp(EnvironmentEnum env) async {
|
|||||||
await configureDependencies(
|
await configureDependencies(
|
||||||
QuestiaEnvConfig(),
|
QuestiaEnvConfig(),
|
||||||
log: env.isDevelopment || kDebugMode,
|
log: env.isDevelopment || kDebugMode,
|
||||||
onTokenExpired: () => appRouter.go(
|
// Treezor-specific detection (message + 500) runs in both modes;
|
||||||
AppRoutes.legacyLogin,
|
// only the destination route differs based on the active app mode.
|
||||||
), //change to payments app to AppRoutes.scaTreezor
|
onTokenExpired: isPaymentMode
|
||||||
|
? () => appRouter.go(AppRoutes.scaTreezor)
|
||||||
|
: () => appRouter.go(AppRoutes.legacyLogin),
|
||||||
onUnauthorized: () async {
|
onUnauthorized: () async {
|
||||||
final currentLocation =
|
final currentLocation =
|
||||||
appRouter.routerDelegate.currentConfiguration.uri.path;
|
appRouter.routerDelegate.currentConfiguration.uri.path;
|
||||||
@@ -41,7 +44,7 @@ Future<void> initApp(EnvironmentEnum env) async {
|
|||||||
await GetIt.I<TreezorWalletConnectionService>().logout();
|
await GetIt.I<TreezorWalletConnectionService>().logout();
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
await clearSessionData();
|
await clearSessionData();
|
||||||
appRouter.go(AppRoutes.legacyLogin);
|
appRouter.go(isPaymentMode ? AppRoutes.login : AppRoutes.legacyLogin);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,33 @@ import 'package:notifications/notifications.dart';
|
|||||||
import 'package:payments/payments.dart';
|
import 'package:payments/payments.dart';
|
||||||
import 'package:profile/profile.dart';
|
import 'package:profile/profile.dart';
|
||||||
import 'package:settings/settings.dart';
|
import 'package:settings/settings.dart';
|
||||||
|
import 'package:sf_app_platform/core/config/app_mode.dart';
|
||||||
import 'package:splash/splash.dart';
|
import 'package:splash/splash.dart';
|
||||||
|
|
||||||
final GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>();
|
final GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
late final GoRouter appRouter;
|
late final GoRouter appRouter;
|
||||||
|
|
||||||
|
/// Maps the splash's session check result to the destination route based
|
||||||
|
/// on the active [appMode]. Set `--dart-define=APP_MODE=payment` (or use
|
||||||
|
/// the `(Payment)` launch configurations) to boot into the payment app.
|
||||||
|
const _legacySplashRouteMap = <InitialRoute, String>{
|
||||||
|
InitialRoute.onboarding: AppRoutes.legacyOnboarding,
|
||||||
|
InitialRoute.login: AppRoutes.legacyLogin,
|
||||||
|
InitialRoute.home: AppRoutes.controlPanel,
|
||||||
|
};
|
||||||
|
|
||||||
|
const _paymentSplashRouteMap = <InitialRoute, String>{
|
||||||
|
InitialRoute.onboarding: AppRoutes.onboarding,
|
||||||
|
InitialRoute.login: AppRoutes.login,
|
||||||
|
InitialRoute.home: AppRoutes.dashboardHome,
|
||||||
|
};
|
||||||
|
|
||||||
void configureAppRouter() {
|
void configureAppRouter() {
|
||||||
|
final splashRouteMap = isPaymentMode
|
||||||
|
? _paymentSplashRouteMap
|
||||||
|
: _legacySplashRouteMap;
|
||||||
|
|
||||||
appRouter = GoRouter(
|
appRouter = GoRouter(
|
||||||
navigatorKey: rootNavigatorKey,
|
navigatorKey: rootNavigatorKey,
|
||||||
initialLocation: AppRoutes.splash,
|
initialLocation: AppRoutes.splash,
|
||||||
@@ -32,7 +52,7 @@ void configureAppRouter() {
|
|||||||
GoRoute(
|
GoRoute(
|
||||||
path: AppRoutes.splash,
|
path: AppRoutes.splash,
|
||||||
name: 'splash',
|
name: 'splash',
|
||||||
pageBuilder: SplashBuilder().buildPage,
|
pageBuilder: SplashBuilder(routeMap: splashRouteMap).buildPage,
|
||||||
),
|
),
|
||||||
StatefulShellRoute.indexedStack(
|
StatefulShellRoute.indexedStack(
|
||||||
builder: (context, state, navShell) {
|
builder: (context, state, navShell) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:auth/auth.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:sf_app_platform/core/config/app_mode.dart';
|
||||||
import 'package:sf_app_platform/navigation/app_router.dart';
|
import 'package:sf_app_platform/navigation/app_router.dart';
|
||||||
import 'package:navigation/navigation.dart';
|
import 'package:navigation/navigation.dart';
|
||||||
import 'package:sf_app_platform/providers/app_state_provider.dart';
|
import 'package:sf_app_platform/providers/app_state_provider.dart';
|
||||||
@@ -24,48 +25,58 @@ class SaveFamilyApp extends ConsumerStatefulWidget {
|
|||||||
|
|
||||||
class SaveFamilyAppState extends ConsumerState<SaveFamilyApp>
|
class SaveFamilyAppState extends ConsumerState<SaveFamilyApp>
|
||||||
with WidgetsBindingObserver {
|
with WidgetsBindingObserver {
|
||||||
late final WalletHeartbeatService walletHeartbeat;
|
WalletHeartbeatService? _walletHeartbeat;
|
||||||
late final LegacyHeartbeatService legacyHeartbeat;
|
LegacyHeartbeatService? _legacyHeartbeat;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
walletHeartbeat = WalletHeartbeatService(
|
|
||||||
repository: ref.read(treezorRepositoryProvider),
|
if (isPaymentMode) {
|
||||||
sessionLocal: SessionLocalDatasourceImpl(),
|
_walletHeartbeat = WalletHeartbeatService(
|
||||||
onError: () => appRouter.go(
|
repository: ref.read(treezorRepositoryProvider),
|
||||||
AppRoutes.legacyLogin,
|
sessionLocal: SessionLocalDatasourceImpl(),
|
||||||
), //change to payments app to AppRoutes.scaTreezor
|
onError: () => appRouter.go(AppRoutes.scaTreezor),
|
||||||
);
|
);
|
||||||
legacyHeartbeat = LegacyHeartbeatService(
|
}
|
||||||
repository: GetIt.I<QuestiaRepository>(),
|
|
||||||
onUnauthorized: () {
|
if (isLegacyMode) {
|
||||||
clearSessionData();
|
_legacyHeartbeat = LegacyHeartbeatService(
|
||||||
appRouter.go(AppRoutes.legacyLogin);
|
repository: GetIt.I<QuestiaRepository>(),
|
||||||
},
|
onUnauthorized: () {
|
||||||
);
|
clearSessionData();
|
||||||
|
appRouter.go(AppRoutes.legacyLogin);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
appRouter.routerDelegate.addListener(_onRouteChanged);
|
||||||
|
}
|
||||||
|
|
||||||
onBeforeSessionCleared = () {
|
onBeforeSessionCleared = () {
|
||||||
walletHeartbeat.stop();
|
_walletHeartbeat?.stop();
|
||||||
legacyHeartbeat.stop();
|
_legacyHeartbeat?.stop();
|
||||||
};
|
};
|
||||||
appRouter.routerDelegate.addListener(_onRouteChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onRouteChanged() {
|
void _onRouteChanged() {
|
||||||
|
final heartbeat = _legacyHeartbeat;
|
||||||
|
if (heartbeat == null) return;
|
||||||
|
|
||||||
final location = appRouter.routerDelegate.currentConfiguration.uri.path;
|
final location = appRouter.routerDelegate.currentConfiguration.uri.path;
|
||||||
if (location.startsWith(AppRoutes.legacyDashboard)) {
|
if (location.startsWith(AppRoutes.legacyDashboard)) {
|
||||||
legacyHeartbeat.start();
|
heartbeat.start();
|
||||||
} else {
|
} else {
|
||||||
legacyHeartbeat.stop();
|
heartbeat.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
appRouter.routerDelegate.removeListener(_onRouteChanged);
|
if (isLegacyMode) {
|
||||||
walletHeartbeat.stop();
|
appRouter.routerDelegate.removeListener(_onRouteChanged);
|
||||||
legacyHeartbeat.stop();
|
}
|
||||||
|
_walletHeartbeat?.stop();
|
||||||
|
_legacyHeartbeat?.stop();
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
@@ -75,12 +86,14 @@ class SaveFamilyAppState extends ConsumerState<SaveFamilyApp>
|
|||||||
debugPrint('State: $state');
|
debugPrint('State: $state');
|
||||||
ref.read(appLifecycleStateProvider.notifier).setState(state);
|
ref.read(appLifecycleStateProvider.notifier).setState(state);
|
||||||
if (state == AppLifecycleState.resumed) {
|
if (state == AppLifecycleState.resumed) {
|
||||||
// walletHeartbeat.start();
|
_walletHeartbeat?.start();
|
||||||
_onRouteChanged();
|
if (isLegacyMode) {
|
||||||
|
_onRouteChanged();
|
||||||
|
}
|
||||||
ref.read(permissionsProvider.notifier).checkPermissions();
|
ref.read(permissionsProvider.notifier).checkPermissions();
|
||||||
} else if (state == AppLifecycleState.paused) {
|
} else if (state == AppLifecycleState.paused) {
|
||||||
// walletHeartbeat.stop();
|
_walletHeartbeat?.stop();
|
||||||
legacyHeartbeat.stop();
|
_legacyHeartbeat?.stop();
|
||||||
}
|
}
|
||||||
super.didChangeAppLifecycleState(state);
|
super.didChangeAppLifecycleState(state);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
export 'src/domain/initial_route.dart';
|
||||||
export 'src/splash_builder.dart';
|
export 'src/splash_builder.dart';
|
||||||
|
|||||||
@@ -10,11 +10,17 @@ class SplashScreen extends StatefulWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.navigationContract,
|
required this.navigationContract,
|
||||||
required this.checkSessionUseCase,
|
required this.checkSessionUseCase,
|
||||||
|
required this.routeMap,
|
||||||
});
|
});
|
||||||
|
|
||||||
final NavigationContract navigationContract;
|
final NavigationContract navigationContract;
|
||||||
final CheckSessionUseCase checkSessionUseCase;
|
final CheckSessionUseCase checkSessionUseCase;
|
||||||
|
|
||||||
|
/// Maps each [InitialRoute] to its destination path. Provided by the
|
||||||
|
/// app layer so the splash module stays agnostic of which app
|
||||||
|
/// (legacy vs payment) is being launched.
|
||||||
|
final Map<InitialRoute, String> routeMap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SplashScreen> createState() => _SplashScreenState();
|
State<SplashScreen> createState() => _SplashScreenState();
|
||||||
}
|
}
|
||||||
@@ -52,11 +58,9 @@ class _SplashScreenState extends State<SplashScreen>
|
|||||||
void _navigateIfReady() {
|
void _navigateIfReady() {
|
||||||
if (!_animationDone || _route == null || !mounted) return;
|
if (!_animationDone || _route == null || !mounted) return;
|
||||||
|
|
||||||
final destination = switch (_route!) {
|
final destination = widget.routeMap[_route!];
|
||||||
InitialRoute.onboarding => AppRoutes.legacyOnboarding,
|
if (destination == null) return;
|
||||||
InitialRoute.login => AppRoutes.legacyLogin,
|
|
||||||
InitialRoute.home => AppRoutes.controlPanel,
|
|
||||||
};
|
|
||||||
widget.navigationContract.goTo(destination);
|
widget.navigationContract.goTo(destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,16 @@ import 'package:navigation/navigation_contract.dart';
|
|||||||
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
||||||
import 'package:sf_shared/sf_shared.dart';
|
import 'package:sf_shared/sf_shared.dart';
|
||||||
import 'package:splash/src/domain/check_session_use_case_impl.dart';
|
import 'package:splash/src/domain/check_session_use_case_impl.dart';
|
||||||
|
import 'package:splash/src/domain/initial_route.dart';
|
||||||
import 'package:splash/src/presentation/splash_screen.dart';
|
import 'package:splash/src/presentation/splash_screen.dart';
|
||||||
|
|
||||||
class SplashBuilder {
|
class SplashBuilder {
|
||||||
const SplashBuilder();
|
const SplashBuilder({required this.routeMap});
|
||||||
|
|
||||||
|
/// Maps each [InitialRoute] to its destination path. The app layer
|
||||||
|
/// provides this map so the splash module stays agnostic of which
|
||||||
|
/// app (legacy vs payment) is being launched.
|
||||||
|
final Map<InitialRoute, String> routeMap;
|
||||||
|
|
||||||
Page<void> buildPage(BuildContext context, GoRouterState state) {
|
Page<void> buildPage(BuildContext context, GoRouterState state) {
|
||||||
final navigationContract = GetIt.I<NavigationContract>();
|
final navigationContract = GetIt.I<NavigationContract>();
|
||||||
@@ -20,6 +26,7 @@ class SplashBuilder {
|
|||||||
child: SplashScreen(
|
child: SplashScreen(
|
||||||
navigationContract: navigationContract,
|
navigationContract: navigationContract,
|
||||||
checkSessionUseCase: checkSessionUseCase,
|
checkSessionUseCase: checkSessionUseCase,
|
||||||
|
routeMap: routeMap,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ Future<void> configureDependencies(
|
|||||||
cookieJar: cookieJar,
|
cookieJar: cookieJar,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (onTokenExpired != null && onUnauthorized != null) {
|
if (onUnauthorized != null) {
|
||||||
dio.interceptors.add(
|
dio.interceptors.add(
|
||||||
TreezorTokenInterceptor(
|
TreezorTokenInterceptor(
|
||||||
onTokenExpired: onTokenExpired,
|
|
||||||
onUnauthorized: onUnauthorized,
|
onUnauthorized: onUnauthorized,
|
||||||
|
onTokenExpired: onTokenExpired,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,32 +4,53 @@ import 'package:dio/dio.dart';
|
|||||||
|
|
||||||
class TreezorTokenInterceptor extends Interceptor {
|
class TreezorTokenInterceptor extends Interceptor {
|
||||||
TreezorTokenInterceptor({
|
TreezorTokenInterceptor({
|
||||||
required void Function() onTokenExpired,
|
|
||||||
required void Function() onUnauthorized,
|
required void Function() onUnauthorized,
|
||||||
}) : _onTokenExpired = onTokenExpired,
|
void Function()? onTokenExpired,
|
||||||
_onUnauthorized = onUnauthorized;
|
}) : _onUnauthorized = onUnauthorized,
|
||||||
|
_onTokenExpired = onTokenExpired;
|
||||||
|
|
||||||
// ignore: unused_field
|
/// Called when the backend signals a session expiration that should
|
||||||
final void Function() _onTokenExpired;
|
/// trigger a re-login flow. The destination route is decided by the
|
||||||
|
/// caller (e.g. SCA screen in payment mode, login screen in legacy
|
||||||
|
/// mode). When `null`, the Treezor-specific detection (message + 500)
|
||||||
|
/// is skipped entirely and only generic 401 responses are handled.
|
||||||
|
final void Function()? _onTokenExpired;
|
||||||
|
|
||||||
|
/// Called on any 401 response. Active in both apps.
|
||||||
final void Function() _onUnauthorized;
|
final void Function() _onUnauthorized;
|
||||||
|
|
||||||
bool _handling = false;
|
bool _handling = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||||
if (!_handling) {
|
if (!_handling) {
|
||||||
// final message = _extractApiMessage(err.response?.data);
|
// Treezor-specific handling: only enabled when running in payment mode
|
||||||
// if (message != null && message.contains('Treezor Token Expired')) {
|
// (i.e. when an `onTokenExpired` callback was provided).
|
||||||
// _handling = true;
|
if (_onTokenExpired != null) {
|
||||||
// _onTokenExpired();
|
final message = _extractApiMessage(err.response?.data);
|
||||||
// Future.delayed(const Duration(seconds: 2), () => _handling = false);
|
if (message != null && message.contains('Treezor Token Expired')) {
|
||||||
// }
|
_handling = true;
|
||||||
// else if (err.response?.statusCode == 500) {
|
_onTokenExpired();
|
||||||
// _handling = true;
|
Future.delayed(
|
||||||
// _onTokenExpired();
|
const Duration(seconds: 2),
|
||||||
// Future.delayed(const Duration(seconds: 2), () => _handling = false);
|
() => _handling = false,
|
||||||
// }
|
);
|
||||||
// else
|
} else if (err.response?.statusCode == 500) {
|
||||||
if (err.response?.statusCode == 401) {
|
_handling = true;
|
||||||
|
_onTokenExpired();
|
||||||
|
Future.delayed(
|
||||||
|
const Duration(seconds: 2),
|
||||||
|
() => _handling = false,
|
||||||
|
);
|
||||||
|
} else if (err.response?.statusCode == 401) {
|
||||||
|
_handling = true;
|
||||||
|
_onUnauthorized();
|
||||||
|
Future.delayed(
|
||||||
|
const Duration(seconds: 2),
|
||||||
|
() => _handling = false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (err.response?.statusCode == 401) {
|
||||||
_handling = true;
|
_handling = true;
|
||||||
_onUnauthorized();
|
_onUnauthorized();
|
||||||
Future.delayed(const Duration(seconds: 2), () => _handling = false);
|
Future.delayed(const Duration(seconds: 2), () => _handling = false);
|
||||||
@@ -39,7 +60,6 @@ class TreezorTokenInterceptor extends Interceptor {
|
|||||||
handler.next(err);
|
handler.next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: unused_element
|
|
||||||
String? _extractApiMessage(Object? data) {
|
String? _extractApiMessage(Object? data) {
|
||||||
if (data == null) return null;
|
if (data == null) return null;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user