12 کامیت‌ها 617e268148 ... 9e1f99d2b8

نویسنده SHA1 پیام تاریخ
  Yulian 9e1f99d2b8 before push 1 ماه پیش
  Yulian 1337ea3280 set pindah2 filter pake indexed stack 3 ماه پیش
  Yulian d2f634452b rapikan param history 3 ماه پیش
  Yulian ee83788aac beresin context di history 3 ماه پیش
  Yulian 00fdde5d93 beresin home & detail 3 ماه پیش
  Yulian a8974652fd beresin account 3 ماه پیش
  Yulian e715330edc beresin context di login & qr 3 ماه پیش
  Yulian c88361e6f5 fix context get & post 3 ماه پیش
  Yulian cb549169fb upgrade flutter to 3.35, rapikan pubspec yaml 3 ماه پیش
  Yulian 8f3b422739 fix: some hint & pemanggilan context 3 ماه پیش
  Yulian a5fab5f975 bersih2 MaterialStateProperty 3 ماه پیش
  Yulian 9e648bff34 warning beres 3 ماه پیش
61فایلهای تغییر یافته به همراه2178 افزوده شده و 1693 حذف شده
  1. 1 1
      android/app/build.gradle.kts
  2. 2 1
      android/settings.gradle.kts
  3. 5 4
      lib/app_router.dart
  4. 30 34
      lib/main.dart
  5. 58 57
      lib/src/api/api_auth_provider.dart
  6. 14 9
      lib/src/api/jwt_token.dart
  7. 3 6
      lib/src/bloc/login/login_bloc.dart
  8. 23 16
      lib/src/layouts/auth/change_code.dart
  9. 3 4
      lib/src/layouts/auth/end_session.dart
  10. 68 48
      lib/src/layouts/auth/login.dart
  11. 23 26
      lib/src/layouts/auth/qr.dart
  12. 40 37
      lib/src/layouts/components/auto_login.dart
  13. 6 4
      lib/src/layouts/components/camera.dart
  14. 13 16
      lib/src/layouts/components/error_page.dart
  15. 19 21
      lib/src/layouts/components/photo_chat.dart
  16. 4 7
      lib/src/layouts/components/responsive.dart
  17. 59 71
      lib/src/layouts/components/template.dart
  18. 46 40
      lib/src/layouts/functions/account.dart
  19. 13 11
      lib/src/layouts/functions/detail.dart
  20. 140 105
      lib/src/layouts/functions/history.dart
  21. 99 93
      lib/src/layouts/functions/home.dart
  22. 5 8
      lib/src/layouts/functions/message.dart
  23. 7 5
      lib/src/layouts/functions/request.dart
  24. 0 3
      lib/src/layouts/landing/landing_page.dart
  25. 44 23
      lib/src/layouts/mobile/app_mobile.dart
  26. 3 3
      lib/src/layouts/mobile/banner_detail.dart
  27. 12 13
      lib/src/layouts/mobile/history_detail.dart
  28. 7 7
      lib/src/layouts/mobile/history_detail_pending.dart
  29. 25 23
      lib/src/layouts/mobile/history_forum.dart
  30. 5 5
      lib/src/layouts/mobile/history_rating.dart
  31. 42 40
      lib/src/layouts/mobile/menu_account.dart
  32. 206 171
      lib/src/layouts/mobile/menu_history.dart
  33. 98 78
      lib/src/layouts/mobile/menu_home.dart
  34. 1 1
      lib/src/layouts/mobile/message_broadcast.dart
  35. 9 8
      lib/src/layouts/mobile/message_chat.dart
  36. 2 2
      lib/src/layouts/mobile/message_list.dart
  37. 3 3
      lib/src/layouts/mobile/password.dart
  38. 5 5
      lib/src/layouts/mobile/request_create.dart
  39. 7 7
      lib/src/layouts/mobile/request_select.dart
  40. 5 5
      lib/src/layouts/mobile/request_success.dart
  41. 3 3
      lib/src/layouts/web/banner_detail.dart
  42. 3 3
      lib/src/layouts/web/history_detail.dart
  43. 2 2
      lib/src/layouts/web/history_detail_pending.dart
  44. 4 4
      lib/src/layouts/web/history_forum.dart
  45. 205 206
      lib/src/layouts/web/menu_account.dart
  46. 133 129
      lib/src/layouts/web/menu_history.dart
  47. 12 12
      lib/src/layouts/web/menu_home.dart
  48. 4 4
      lib/src/layouts/web/message_broadcast.dart
  49. 148 149
      lib/src/layouts/web/message_list.dart
  50. 4 4
      lib/src/layouts/web/password.dart
  51. 3 3
      lib/src/layouts/web/request_create.dart
  52. 9 9
      lib/src/layouts/web/request_select.dart
  53. 5 5
      lib/src/layouts/web/request_success.dart
  54. 1 1
      lib/src/model/token/token.g.dart
  55. 18 7
      lib/src/utils/U.dart
  56. 0 1
      lib/src/utils/cache_manager.dart
  57. 7 16
      lib/src/utils/dio_logging_interceptors.dart
  58. 45 29
      lib/src/utils/provider.dart
  59. 297 0
      lib/src/utils/ui_service.dart
  60. 45 37
      pubspec.lock
  61. 75 48
      pubspec.yaml

+ 1 - 1
android/app/build.gradle.kts

@@ -109,7 +109,7 @@ android {
         applicationId = "com.datacomsolusindo.telnow2.telnow_mobile2"
         // You can update the following values to match your application needs.
         // For more information, see: https://flutter.dev/to/review-gradle-config.
-        minSdk = 23//flutter.minSdkVersion
+        minSdk = flutter.minSdkVersion//flutter.minSdkVersion
         targetSdk = flutter.targetSdkVersion
         versionCode = flutter.versionCode
         versionName = flutter.versionName

+ 2 - 1
android/settings.gradle.kts

@@ -51,7 +51,8 @@ plugins {
     // START: FlutterFire Configuration
     id("com.google.gms.google-services") version("4.3.15") apply false
     // END: FlutterFire Configuration
-    id("org.jetbrains.kotlin.android") version "1.8.22" apply false
+    id("org.jetbrains.kotlin.android") version "2.1.0" apply false
+//    id("org.jetbrains.kotlin.android") version "1.8.22" apply false
 }
 
 include(":app")

+ 5 - 4
lib/app_router.dart

@@ -8,6 +8,7 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 
 @AutoRouterConfig(replaceInRouteName: 'Screen|Page,Route')
 class AppRouter extends RootStackRouter {
+  AppRouter({super.navigatorKey});
   final SharedPreferencesManager _sharedPreferencesManager = locator<SharedPreferencesManager>();
 
   @override
@@ -55,7 +56,7 @@ class AppRouter extends RootStackRouter {
     AutoRouteGuard.simple((resolver, router) async{
       RouteMatch<dynamic> vRedirector = resolver.route;
       String? rawPid = '';
-      var pid;
+      String? pid = '';
       bool stb = false;
 
       if(vRedirector.params.isNotEmpty){
@@ -65,7 +66,7 @@ class AppRouter extends RootStackRouter {
       try {
         pid = Uri.decodeComponent(rawPid);
       } catch (e) {
-        print("err : $e");
+        debugPrint("err : $e");
         stb = true;
       }
 
@@ -82,7 +83,7 @@ class AppRouter extends RootStackRouter {
 
       if (U.getAccessCode() == null) {
         await _sharedPreferencesManager.putString(SharedPreferencesManager.keyBaseUrl, U.rewriteUrl(vRedirector.fragment));
-        await _sharedPreferencesManager.putString(SharedPreferencesManager.keyAccessCode, pid);
+        await _sharedPreferencesManager.putString(SharedPreferencesManager.keyAccessCode, pid!);
         await _sharedPreferencesManager.putString(SharedPreferencesManager.keySerialCode, 'P-$pid}');
         try {
           var ver = await U.reloadLicense();
@@ -105,7 +106,7 @@ class AppRouter extends RootStackRouter {
             var ver = await U.reloadLicense();
             await _sharedPreferencesManager.putInt(SharedPreferencesManager.version, ver['version']);
           } catch (e) {
-            print('Error get2: ${e.toString()}');
+            debugPrint('Error get2: ${e.toString()}');
             await _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyBaseUrl);
             await _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyAccessCode);
             await _sharedPreferencesManager.clearKey(SharedPreferencesManager.keySerialCode);

+ 30 - 34
lib/main.dart

@@ -29,6 +29,7 @@ import 'package:http/http.dart' as http;
 import 'package:telnow_mobile_new/src/api/jwt_token.dart';
 
 import 'app_router.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 // final SharedPreferencesManager _sharedPreferencesManager = locator<SharedPreferencesManager>();
 var isDebug = true;
@@ -67,9 +68,7 @@ void main() async{
 
     await setupLocator();
     SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
-    HttpOverrides.global = new MyHttpOverrides();
-    // WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
-    // FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
+    HttpOverrides.global = MyHttpOverrides();
     runApp(
         EasyLocalization(
             path: 'assets/lang',
@@ -91,12 +90,11 @@ void main() async{
         )
     );
   } catch (error, stacktrace) {
-    print('$error & $stacktrace');
+    debugPrint('$error & $stacktrace');
   }
 }
 
 class MyCustomScrollBehavior extends MaterialScrollBehavior {
-  // Override behavior methods and getters like dragDevices
   @override
   Set<PointerDeviceKind> get dragDevices {
     return {
@@ -106,11 +104,10 @@ class MyCustomScrollBehavior extends MaterialScrollBehavior {
   }
 }
 
+final _appRouter = AppRouter(navigatorKey: UIService.navigatorKey);
 class MyApp extends StatelessWidget {
-  MyApp({super.key});
-  final _appRouter = AppRouter();
+  const MyApp({super.key});
 
-  // This widget is the root of your application.
   @override
   Widget build(BuildContext context) {
     return MultiProvider(
@@ -123,6 +120,8 @@ class MyApp extends StatelessWidget {
         ChangeNotifierProvider(create: (_) => MessageModule()),
       ],
       child: MaterialApp.router(
+        // routerDelegate: _appRouter.delegate(),
+        // routeInformationParser: _appRouter.defaultRouteParser(),
         scrollBehavior: MyCustomScrollBehavior(),
         debugShowCheckedModeBanner: false,
         localizationsDelegates: context.localizationDelegates,
@@ -149,7 +148,7 @@ class NavigationService {
 
 @RoutePage()
 class HomeGuardPage extends StatefulWidget {
-  const HomeGuardPage({Key? key}) : super(key: key);
+  const HomeGuardPage({super.key});
 
   @override
   State<HomeGuardPage> createState() => _HomeGuardPageState();
@@ -161,11 +160,9 @@ class _HomeGuardPageState extends State<HomeGuardPage> {
   @override
   Widget build(BuildContext context) {
     eventBus.on().listen((event) {
-      // print("eventBus => ${event.toString()}");
       if(event.toString() == 'logout') {
-        // token.logout();
-        context.navigateToPath('/end-session');
-        // return;
+        UIService.navigateNamed('/end-session');
+        // context.navigateToPath('/end-session');
       }
     });
     // TODO: implement build
@@ -189,21 +186,21 @@ class NotificationClass {
   final ApiAuthProvider _apiAuthProvider = ApiAuthProvider();
 
   initFirebaseMessaging(BuildContext context) {
+    final locale = context.locale.toString();
+
     FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
       var mid = message.data['mid'];
       var token = await U.getFcmToken();
-      // print('tokennya');
-      // print(token);
       if (token != null) {
         _apiAuthProvider.postDataNoAuth('/api/notifications/received/$token/$mid').then((value) =>
-            print('Send confirm succes!')
+            debugPrint('Send confirm succes!')
         );
       }
 
       if(kIsWeb){
         QuickNotify.notify(
           title: message.data['subject'],
-          content: context.locale.toString() == 'id' ? message.data['description'] : message.data['descriptionEn'],
+          content: locale == 'id' ? message.data['description'] : message.data['descriptionEn'],
         );
       }
       else{
@@ -234,34 +231,38 @@ class NotificationClass {
         );
         var platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics);
         U.flutterLocalNotificationsPlugin.show(0, message.data['subject'],
-            context.locale.toString() == 'id' ? message.data['description'] : message.data['descriptionEn'], platformChannelSpecifics,
+            locale == 'id' ? message.data['description'] : message.data['descriptionEn'], platformChannelSpecifics,
             payload: jsonEncode(message.data));
       }
     });
 
     FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
+
     FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) {
       if (message != null && !U.hidePayload) {
-        // print(message.data);
-        goNotification(message.data, context);
+        // goNotification(message.data, context);
+        UIService.goNotification(message.data);
       }
     });
+
     FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
       U.setHidePayload(false);
-      goNotification(message.data, context);
+      UIService.goNotification(message.data);
+      // goNotification(message.data, context);
     });
   }
 
-  getActiveNotif() async {
-    List<ActiveNotification>? activeNotif = await U.flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.getActiveNotifications();
-    return activeNotif;
+  getActiveNotification() async {
+    List<ActiveNotification>? activeNotification = await U.flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.getActiveNotifications();
+    return activeNotification;
   }
 
-  startNotification(BuildContext context, {code}) async {
+  startNotification({code}) async {
+    final locale = NavigationService.navigatorKey.currentContext?.locale.toString();
     var token = await U.getFcmToken();
     if (token != null) {
-      Map data = {'token': token, 'language': code ?? context.locale.toString().toUpperCase()};
-      var res = _apiAuthProvider.postData('/api/fcmTokens/register', null, data, context);
+      Map data = {'token': token, 'language': code ?? locale?.toUpperCase()};
+      var res = _apiAuthProvider.postData('/api/fcmTokens/register', null, data);
       return res;
     }
   }
@@ -269,7 +270,7 @@ class NotificationClass {
   stopNotification(BuildContext context) async {
     var token = await U.getFcmToken();
     if (token != null) {
-      var res = _apiAuthProvider.postData('/api/fcmTokens/remove/$token', null, null, context);
+      var res = _apiAuthProvider.postData('/api/fcmTokens/remove/$token', null, null);
       return res;
     }
   }
@@ -287,11 +288,6 @@ class NotificationClass {
       context.router.removeLast();
       context.navigateToPath("/app/$pid/menu/history");
     }
-    // if (list['type'] == 'FORUM') {
-    //   Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: Forum(list['info'], list['currentStatus'] == 'DIMULAI' || list['currentStatus'] == 'DISELESAIKAN' ? true : false)));
-    // } else {
-    //   Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: DetailMisi(list['info'])));
-    // }
   }
 }
 
@@ -307,7 +303,7 @@ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
   FirebaseMessaging.instance.getToken().then((token) async {
     if (token != null) {
       http.post(Uri.https(U.decodeBase64Url(prefs.getString(SharedPreferencesManager.keyBaseUrl)!).split('//')[1], '$decAccCode/api/notifications/received/$token/$mid')).then((value) {
-        print("kirim confirm");
+        debugPrint("kirim confirm");
         prefs.setString(SharedPreferencesManager.lastMid, mid);
       });
     }

+ 58 - 57
lib/src/api/api_auth_provider.dart

@@ -3,13 +3,13 @@ import 'dart:convert';
 import 'dart:io';
 import 'package:dio/dio.dart';
 import 'package:flutter/cupertino.dart';
-import 'package:flutter/foundation.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
 import 'package:telnow_mobile_new/src/model/login/login_body.dart';
 import 'package:telnow_mobile_new/src/model/refreshtoken/refresh_token_body.dart';
 import 'package:telnow_mobile_new/src/model/token/token.dart';
 import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:telnow_mobile_new/src/utils/dio_logging_interceptors.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:easy_localization/easy_localization.dart';
@@ -21,7 +21,7 @@ const String host = '';
 // const String host = '192.168.100.18:8080';
 
 class ApiAuthProvider {
-  final Dio _dio = new Dio();
+  final Dio _dio = Dio();
   final SharedPreferencesManager _sharedPreferencesManager = locator<SharedPreferencesManager>();
 
   final String displayVersion = '4.0.17'; //versi aplikasi untuk di tampilkan
@@ -41,7 +41,7 @@ class ApiAuthProvider {
   final String clientSecret = '3JskYu5zxlXRDv6g';
   // final String clientIdWeb = 'web-apHca0ncOX';
   // final String clientSecretWeb = '1qeIwW8Wu9AF4DRF';
-  var isDebug;
+  bool isDebug = false;
 
   ApiAuthProvider() {
 //     print('ApiAuthProvider called');
@@ -76,15 +76,11 @@ class ApiAuthProvider {
       var strData = "";
       var i = 0;
       loginData.forEach((key, value) {
-        strData = strData +
-            (i == 0 ? '' : '&') +
-            key +
-            '=' +
-            Uri.encodeComponent(value);
+        strData = '$strData${i == 0 ? '' : '&'}$key=${Uri.encodeComponent(value)}';
         i++;
       });
       // print(strData);
-      final response = await _dio.post('/oauth/token?' + strData,
+      final response = await _dio.post('/oauth/token?$strData',
           options: Options(
             headers: {
               'Accept': 'application/json',
@@ -130,16 +126,12 @@ class ApiAuthProvider {
       var strData = "";
       var i = 0;
       refreshData.forEach((key, value) {
-        strData = strData +
-            (i == 0 ? '' : '&') +
-            key +
-            '=' +
-            Uri.encodeComponent(value);
+        strData = '$strData${i == 0 ? '' : '&'}$key=${Uri.encodeComponent(value)}';
         i++;
       });
       // print(strData);
       final response = await _dio.post(
-        '/oauth/token?' + strData,
+        '/oauth/token?$strData',
         options: Options(
           headers: {
             'Accept': 'application/json',
@@ -172,7 +164,7 @@ class ApiAuthProvider {
       );
       return json.decode(response.data!);
     } on DioException catch (error) {
-      print(error.response);
+      debugPrint(error.response.toString());
       if (error.response == null) {
         try {
           final result = await InternetAddress.lookup('google.com');
@@ -189,7 +181,7 @@ class ApiAuthProvider {
           handlingError(context, 2); //error connection
         } else {
           await Future.delayed(Duration(milliseconds: 200));
-          return getData(path, params, context, secondCheck: true);
+          return getData(path, params, secondCheck: true);
         }
         //error server
       } else if (error.response?.statusCode == 401) {
@@ -200,18 +192,18 @@ class ApiAuthProvider {
           handlingError(context, 2); //error connection
         } else {
           await Future.delayed(Duration(milliseconds: 200));
-          return getData(path, params, context, secondCheck: true);
+          return getData(path, params, secondCheck: true);
         }
       }
       return Future.error(error);
     } catch (error) {
-      print(error.toString());
+      debugPrint(error.toString());
       handlingError(context, 1);
       return Future.error(error);
     }
   }
 
-  Future getData(String path, var params, BuildContext context, {bool secondCheck = false}) async {
+  Future getData(String path, var params, {bool secondCheck = false}) async {
     try {
       Response<String> response = await _dio.getUri(
         Uri(path: path, queryParameters: params),
@@ -221,31 +213,28 @@ class ApiAuthProvider {
           },
         ),
       );
-//      print(response.data);
       return json.decode(response.data!);
     } on DioException catch (error) {
-      // print(error.response!.statusCode);
-      bool isOpen = ModalRoute.of(context)?.isCurrent != true;
+      bool isOpen = UIService.isCurrentRouteInactive;
       if (error.response == null) {
         try {
           final result = await InternetAddress.lookup('google.com');
           if (!isOpen && result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
-            handlingError(context, 1); //error server
+            UIService.handlingError(ErrorType.noInternet); //error server
           }
         } on SocketException catch (_) {
-          // if(!isOpen) handlingError(context, 0); //no internet
         }
+
       } else if (!isOpen && error.response!.statusCode! >= 500) {
-        handlingError(context, 1); //error server
+        UIService.handlingError(ErrorType.serverError); //error server
       } else if (!isOpen && error.response?.statusCode == 401) {
-        handlingError(context, 3); //error auth
+        UIService.handlingError(ErrorType.invalidAccount); //error auth
       } else {
         if(!isOpen){
           if (secondCheck) {
-            // print('secondCheck');
-            handlingError(context, 2); //error connection
+            UIService.handlingError(ErrorType.connectionError); //error connection
           } else {
-            return getData(path, params, context, secondCheck: true);
+            return getData(path, params, secondCheck: true);
           }
         }
       }
@@ -253,7 +242,7 @@ class ApiAuthProvider {
     }
   }
 
-  Future postData(String path, var params, var data, context) async {
+  Future postData(String path, var params, var data) async {
     try {
       Response response = await _dio.postUri(
         Uri(path: path, queryParameters: params),
@@ -272,37 +261,50 @@ class ApiAuthProvider {
         try {
           final result = await InternetAddress.lookup('google.com');
           if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
-            showError(context, 'errorConnection'.tr());
+            UIService.showError('errorConnection'.tr());
+            // showError(context, 'errorConnection'.tr());
           }
         } on SocketException catch (_) {
-          showError(context, 'noInternet'.tr());
+          UIService.showError('noInternet'.tr());
+          // showError(context, 'noInternet'.tr());
         }
       } else if (error.response!.statusCode! >= 500) {
-        showError(context, 'errorConnection'.tr());
+        UIService.showError('errorConnection'.tr());
+        // showError(context, 'errorConnection'.tr());
       } else if (error.response?.statusCode == 401) {
-        handlingError(context, 3); //error auth
+        UIService.handlingError(ErrorType.invalidAccount);
+        // handlingError(context, 3); //error auth
       } else if (error.response?.statusCode == 422) {
         if(error.response?.data['message'] == 'Worktime did not found'){
-          showError(context, 'notFoundWorktime'.tr());
+          UIService.showError('notFoundWorktime'.tr());
+          // showError(context, 'notFoundWorktime'.tr());
         } else if(error.response?.data['message'] == 'Cant send broadcast message, you have not permission.'){
-          showError(context, 'broadcastPermission'.tr());
+          UIService.showError('broadcastPermission'.tr());
+          // showError(context, 'broadcastPermission'.tr());
         } else if(error.response?.data['message'] == 'Cant send broadcast message, you have not permission.'){
-          showError(context, 'broadcastPermission'.tr());
+          UIService.showError('broadcastPermission'.tr());
+          // showError(context, 'broadcastPermission'.tr());
         } else if(error.response?.data['message'] == 'request did not match informant rights'){
-          showError(context, 'idNotMatch'.tr().replaceAll("#ID", data['user_id']));
+          UIService.showError('idNotMatch'.tr().replaceAll("#ID", data['user_id']));
+          // showError(context, 'idNotMatch'.tr().replaceAll("#ID", data['user_id']));
         } else if(error.response?.data['message'] == 'Informant user id not found.' ||
             error.response?.data['message'] == 'invalid user informant requested'
         ){
-          showError(context, 'idNotFound'.tr());
+          UIService.showError('idNotFound'.tr());
+          // showError(context, 'idNotFound'.tr());
         } else if(error.response?.data['message'] == 'You are not registered as receptionist or room attendant.'){
-          showError(context, 'informantNotRegistered'.tr());
+          UIService.showError('informantNotRegistered'.tr());
+          // showError(context, 'informantNotRegistered'.tr());
         } else if(error.response?.data['message'] == 'invalid parent ticket'){
-          showError(context, 'invalidParentTicket'.tr());
+          UIService.showError('invalidParentTicket'.tr());
+          // showError(context, 'invalidParentTicket'.tr());
         } else {
-          showError(context, error.response?.data['message']);
+          UIService.showError(error.response?.data['message']);
+          // showError(context, error.response?.data['message']);
         }
       } else {
-        showError(context, 'errorServer'.tr());
+        UIService.showError('errorServer'.tr());
+        // showError(context, 'errorServer'.tr());
       }
       return null;
     }
@@ -314,12 +316,12 @@ class ApiAuthProvider {
       Response response = await _dio.postUri(Uri(path: path));
       return response.data;
     } on DioException catch (error) {
-     print(error.response);
+     debugPrint(error.response.toString());
       return null;
     }
   }
 
-  Future patchData(String path, var data, context, {var params}) async {
+  Future patchData(String path, var data, {var params}) async {
     try {
       Response response = await _dio.patchUri(
         Uri(path: path, queryParameters: params),
@@ -337,26 +339,25 @@ class ApiAuthProvider {
         try {
           final result = await InternetAddress.lookup('google.com');
           if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
-            showError(context, 'errorConnection'.tr());
+            UIService.showError('errorConnection'.tr());
           }
         } on SocketException catch (_) {
-          showError(context, 'noInternet'.tr());
+          UIService.showError('noInternet'.tr());
         }
       } else if (error.response!.statusCode! >= 500) {
-        showError(context, 'errorConnection'.tr());
+        UIService.showError('errorConnection'.tr());
       } else if (error.response?.statusCode == 401) {
-        handlingError(context, 3); //error auth
+        UIService.handlingError(ErrorType.invalidAccount); //error auth
       } else if (error.response?.statusCode == 422) {
         if (error.response?.data['message'] == 'Old Password Not Match.') {
-          showError(context, 'wrongOldPass'.tr());
-        } else if (error.response?.data['message'] ==
-            'New password already in used.') {
-          showError(context, 'alreadyUsePass'.tr());
+          UIService.showError('wrongOldPass'.tr());
+        } else if (error.response?.data['message'] == 'New password already in used.') {
+          UIService.showError('alreadyUsePass'.tr());
         } else {
-          showError(context, error.response?.data['message']);
+          UIService.showError(error.response?.data['message']);
         }
       } else {
-        showError(context, 'errorServer'.tr());
+        UIService.showError('errorServer'.tr());
       }
       return null;
     }
@@ -429,6 +430,6 @@ class ApiAuthProvider {
   }
 
   String getServiceAsset(String key){
-    return _dio.options.baseUrl+'/assets/lotties/$key';
+    return '${_dio.options.baseUrl}/assets/lotties/$key';
   }
 }

+ 14 - 9
lib/src/api/jwt_token.dart

@@ -3,13 +3,14 @@ import 'dart:io';
 
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
+import 'package:telnow_mobile_new/main.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
-import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'api_auth_provider.dart';
 
 class JwtToken{
@@ -65,18 +66,22 @@ class JwtToken{
     return utf8.decode(base64Url.decode(output));
   }
 
-  Future<dynamic> getUserData(BuildContext context) async{
+  Future<dynamic> getUserData() async{
     bool isTokenExist = _sharedPreferencesManager.isKeyExists(SharedPreferencesManager.keyAccessToken)!;
     String token = _sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessToken)!;
     String username = _sharedPreferencesManager.getString(SharedPreferencesManager.keyUsername)!;
 
+    void setLocale(code){
+      NavigationService.navigatorKey.currentContext?.setLocale(Locale(code));
+    }
+
     if(isTokenExist && token != ''){
       var jwt = parseJwtPayLoad(token);
-      var url = '/api/informants/'+jwt['userId'].toString();
+      var url = '/api/informants/${jwt['userId']}';
       Map<String, dynamic>? user;
 
       if(U.getInternetStatus()){
-        user = await _apiAuthProvider.getData(url, null, context);
+        user = await _apiAuthProvider.getData(url, null);
       }
       else{
         var val = await CacheMan.readData(url);
@@ -86,13 +91,13 @@ class JwtToken{
       }
 
       if((user != null && user['userId'].toLowerCase() != username.toLowerCase())){
-        U.getChangedPassword() ? handlingError(context, 3) : (){}; //invalid user
+        if (U.getChangedPassword()) UIService.handlingError(ErrorType.invalidAccount);
         return null;
       } else if(user != null && user['dateExpired'] != null){
         final dateToCheck = DateTime.parse(user['dateExpired']);
         final now = DateTime.now();
         if(now.isAfter(dateToCheck)){
-          handlingError(context, 4); //expired
+          UIService.handlingError(ErrorType.expiredAccount); //expired
           return null;
         }
       }
@@ -100,9 +105,9 @@ class JwtToken{
       if(user != null){
         if(U.newServerVersion(1754624839)){
           var code = U.getLang(user['language']).toLowerCase();
-          context.setLocale(Locale(code));
+          setLocale(code);
         } else {
-          context.setLocale(Locale(user['language'].toLowerCase()));
+          setLocale(user['language'].toLowerCase());
         }
       }
 
@@ -111,7 +116,7 @@ class JwtToken{
       }
       return user;
     } else {
-      U.getChangedPassword() ? handlingError(context, 3) : (){}; //invalid user
+      U.getChangedPassword() ? UIService.handlingError(ErrorType.invalidAccount) : (){}; //invalid user
       return Future.error("not found");
     }
   }

+ 3 - 6
lib/src/bloc/login/login_bloc.dart

@@ -30,19 +30,16 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
   final ApiAuthRepository apiAuthRepository = ApiAuthRepository();
   final SharedPreferencesManager sharedPreferencesManager = locator<SharedPreferencesManager>();
 
-  LoginBloc(LoginState initialState) : super(initialState);
+  LoginBloc(super.initialState);
 
   // @override
-  // LoginState get initialState => LoginInitial();
-
-  @override
   Stream<LoginState> mapEventToState(LoginEvent event) async* {
     LoginBody loginBody = event.loginBody;
-    if (loginBody.username == null || loginBody.username.isEmpty) {
+    if (loginBody.username.isEmpty) {
       yield LoginFailure('username');
       return;
     }
-    else if (loginBody.password == null || loginBody.password.isEmpty) {
+    else if (loginBody.password.isEmpty) {
       yield LoginFailure('password');
       return;
     }

+ 23 - 16
lib/src/layouts/auth/change_code.dart

@@ -8,13 +8,14 @@ import 'package:telnow_mobile_new/src/api/jwt_token.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:toggle_switch/toggle_switch.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:http/http.dart' as http;
 
 @RoutePage()
 class ChangeCodePage extends StatefulWidget {
-  const ChangeCodePage({Key? key}) : super(key: key);
+  const ChangeCodePage({super.key});
 
   @override
   State<ChangeCodePage> createState() => _ChangeCodePageState();
@@ -31,7 +32,11 @@ class _ChangeCodePageState extends State<ChangeCodePage> {
   String imageUrl = '';
 
   getNewLicense(rawPid) async{
-    try{imageUrl = U.decodeBase64Url(U.getBaseUrl()!) + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';}catch(e){}
+    try{
+      imageUrl = '${U.decodeBase64Url(U.getBaseUrl()!)}${U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))}/assets/background/tn';
+    }catch(e){
+      debugPrint(e.toString());
+    }
     var pid = Uri.decodeComponent(rawPid);
     var response = await http.post(Uri.https(U.decodeBase64Url(U.getBaseUrl()!).split('//')[1], '${U.decodeBase64Url(pid)}/api/license'));
     var ver = json.decode(response.body);
@@ -120,8 +125,8 @@ class _ChangeCodePageState extends State<ChangeCodePage> {
                             height: 30, width: 100,
                             child: TextButton(
                               style: ButtonStyle(
-                                  backgroundColor: MaterialStateProperty.all<Color>(Colors.black12),
-                                  shape: MaterialStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)))
+                                  backgroundColor: WidgetStateProperty.all<Color>(Colors.black12),
+                                  shape: WidgetStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)))
                               ),
                               child: Text('textCancel'.tr(), style: TextStyle(color: Colors.black, fontSize: 12)),
                               onPressed: (){
@@ -141,8 +146,8 @@ class _ChangeCodePageState extends State<ChangeCodePage> {
                             height: 30, width: 100,
                             child: TextButton(
                               style: ButtonStyle(
-                                  backgroundColor: MaterialStateProperty.all<Color>(Color(0xff0D497F)),
-                                  shape: MaterialStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)))
+                                  backgroundColor: WidgetStateProperty.all<Color>(Color(0xff0D497F)),
+                                  shape: WidgetStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)))
                               ),
                               child: Text('textContinue'.tr(), style: TextStyle(color: Colors.white, fontSize: 12)),
                               onPressed: ()async{
@@ -160,31 +165,33 @@ class _ChangeCodePageState extends State<ChangeCodePage> {
                                     var tkn = await U.getFcmToken();
                                     if (tkn == null) {
                                       token.logout();
-                                      context.router.removeLast();
-                                      context.navigateToPath("/app/$rawPid/login");
+                                      // context.router.removeLast();
+                                      UIService.navigateNamed("/app/$rawPid/login");
                                     } else {
                                       var param = {'token': tkn, 'whiteListToken': false};
-                                      var res = await _apiAuthProvider.postData('/api/fcmTokens/remove/', null, param, context);
+                                      var res = await _apiAuthProvider.postData('/api/fcmTokens/remove/', null, param);
                                       if (res != null && res['success']) {
                                         token.logout();
-                                        context.router.removeLast();
-                                        context.navigateToPath("/app/$rawPid/login");
+                                        // context.router.removeLast();
+                                        UIService.navigateNamed("/app/$rawPid/login");
                                       }
                                     }
                                   }
                                   else{
                                     token.logout();
-                                    context.router.removeLast();
-                                    context.navigateToPath("/app/$rawPid/login");
+                                    UIService.navigateNamed("/app/$rawPid/login");
+                                    // context.router.removeLast();
+                                    // context.navigateToPath("/app/$rawPid/login");
                                   }
                                 } catch(e) {
-                                  print(e.toString());
+                                  debugPrint(e.toString());
                                   await _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyAccessCode);
                                   await _sharedPreferencesManager.clearKey(SharedPreferencesManager.keySerialCode);
                                   await _sharedPreferencesManager.clearKey(SharedPreferencesManager.version);
                                   token.logout();
-                                  context.router.removeLast();
-                                  context.navigateToPath("/qr");
+                                  UIService.navigateNamed("/qr");
+                                  // context.router.removeLast();
+                                  // context.navigateToPath("/qr");
                                 }
                               },
                             ),

+ 3 - 4
lib/src/layouts/auth/end_session.dart

@@ -1,4 +1,3 @@
-import 'package:auto_route/annotations.dart';
 import 'package:auto_route/auto_route.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/foundation.dart';
@@ -20,15 +19,15 @@ class EndSessionPage extends StatefulWidget {
 class _EndSessionPageState extends State<EndSessionPage> {
   @override
   Widget build(BuildContext context) {
-    bool useAsset = false;
+    // bool useAsset = false;
     String imageUrl = '';
     final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
 
     try {
-      imageUrl = apiAuthProvider.baseUrl + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';
+      imageUrl = '${apiAuthProvider.baseUrl}${U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))}/assets/background/tn';
       http.head(Uri.parse(imageUrl));
     }catch(e){
-      useAsset = true;
+      // useAsset = true;
     }
 
     return Scaffold(

+ 68 - 48
lib/src/layouts/auth/login.dart

@@ -16,6 +16,7 @@ import 'package:telnow_mobile_new/src/model/login/login_body.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/C.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
 import 'package:toggle_switch/toggle_switch.dart';
 import 'package:url_launcher/url_launcher.dart';
@@ -23,7 +24,7 @@ import 'package:telnow_mobile_new/src/model/token/token.dart';
 
 @RoutePage()
 class LoginPage extends StatefulWidget {
-  const LoginPage({Key? key}) : super(key: key);
+  const LoginPage({super.key});
 
   @override
   State<LoginPage> createState() => _LoginPageState();
@@ -47,7 +48,11 @@ class _LoginPageState extends State<LoginPage> {
 
   @override
   void initState() {
-    try{imageUrl = apiAuthProvider.baseUrl + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';}catch(e){}
+    try{
+      imageUrl = '${apiAuthProvider.baseUrl}${U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))}/assets/background/tn';
+    }catch(e){
+      debugPrint(e.toString());
+    }
     U.getServerVersion();
     getCompanyName();
     // TODO: implement initState
@@ -109,7 +114,7 @@ class _LoginPageState extends State<LoginPage> {
                       width: 160, height: 160,
                       decoration: BoxDecoration(
                         borderRadius: BorderRadius.all(Radius.circular(80)),
-                        image: DecorationImage(image: CachedNetworkImageProvider(companyLogo+'?bridge-cache=true', cacheManager: CacheManager(CacheMan.config(companyLogo))), fit: BoxFit.cover)
+                        image: DecorationImage(image: CachedNetworkImageProvider('$companyLogo?bridge-cache=true', cacheManager: CacheManager(CacheMan.config(companyLogo))), fit: BoxFit.cover)
                       ),
                     ):SizedBox(height: 115),
                     Expanded(
@@ -147,7 +152,7 @@ class _LoginPageState extends State<LoginPage> {
                           ),
                           companyName.isNotEmpty?Text(companyName, style: TextStyle(fontSize: 16, color: Colors.white), textAlign: TextAlign.center):Container(),
                           SizedBox(height: companyName.isNotEmpty?5:0),
-                          serialNumber.isNotEmpty?Text('serialNumber'.tr() + ' ' + serialNumber, style: TextStyle(color: Colors.white, fontSize: 14)):Container(),
+                          serialNumber.isNotEmpty?Text('${'serialNumber'.tr()} $serialNumber', style: TextStyle(color: Colors.white, fontSize: 14)):Container(),
                         ],
                       ),
                     ),
@@ -157,8 +162,8 @@ class _LoginPageState extends State<LoginPage> {
                       child: GestureDetector(
                         child: Text('policy'.tr(), style: TextStyle(color: Colors.white, fontSize: 14, decoration: TextDecoration.underline)),
                         onTap: () async {
-                          Uri _url = Uri.parse('https://telmessenger.com/privacy-police/telnow.html');
-                          await canLaunchUrl(_url) ? await launchUrl(_url) : print('Could not launch $_url');
+                          Uri url = Uri.parse('https://telmessenger.com/privacy-police/telnow.html');
+                          await canLaunchUrl(url) ? await launchUrl(url) : debugPrint('Could not launch $url');
                         },
                       ),
                     ),
@@ -243,6 +248,7 @@ class _LoginPageState extends State<LoginPage> {
   }
 
   loginAction() async{
+    final languageTag = context.locale.toLanguageTag();
     if(!loading){
       setState(()=>loading = true);
 
@@ -254,60 +260,64 @@ class _LoginPageState extends State<LoginPage> {
       }
 
       if (errMsg.isNotEmpty) {
-        showError(context, errMsg);
+        UIService.showError(errMsg);
+        // showError(context, errMsg);
         setState(()=>loading = false);
         return;
       }
 
       Token token = await apiAuthRepository.postLoginUser(LoginBody(username.trim(), password.trim(), "password"));
       if (token.error != null) {
-        showError(context, token.error);
+        UIService.showError(token.error.toString());
         setState(()=>loading = false);
         return;
       }
 
-      var pid = U.getPidFromUrl(context.router.currentUrl);
+      var pid = U.getPidFromUrl(UIService.getCurrentUrl());
       await sharedPreferencesManager.putString(SharedPreferencesManager.keyAccessToken, token.accessToken!);
       await sharedPreferencesManager.putString(SharedPreferencesManager.keyRefreshToken, token.refreshToken!);
 
-      // print(JwtDecoder.decode(token.accessToken!));
       var accTkn = JwtDecoder.decode(token.accessToken!);
-      switchLang(context, context.locale.toLanguageTag(), accTkn);
+      switchLang(languageTag, accTkn);
 
-      if(await setUsername()){
+      if(await setUsername() && mounted){
         var data = {
           'username': username.trim(),
           'password': password.trim()
         };
-        Navigator.push(context, MaterialPageRoute(builder: (context) => ForceChgPassPage(data: data))).then((value) async {
+        Navigator.push(
+          context,
+          MaterialPageRoute(builder: (_) => ForceChgPassPage(data: data)),
+        ).then((_) async {
           if (!U.getChangedPassword()) {
             await U.clearPreferences();
             U.setHidePayload(true);
-            context.router.removeLast();
-            context.navigateToPath("/app/$pid/menu");
-            return;
+            UIService.navigateNamed("/app/$pid/menu");
           }
         });
-      }
-      else{
+
+      } else {
         await sharedPreferencesManager.putBool(SharedPreferencesManager.keyIsLogin, true);
         await sharedPreferencesManager.putString(SharedPreferencesManager.keyScoope, 'INSIDE');
         await sharedPreferencesManager.putInt(SharedPreferencesManager.keyCountRefreshToken, 0);
         await sharedPreferencesManager.putString(SharedPreferencesManager.keyPendingData, jsonEncode([]));
         U.setHidePayload(true);
-        context.router.removeLast();
-        context.navigateToPath("/app/$pid/menu");
+        UIService.navigateNamed("/app/$pid/menu");
+        // context.router.removeLast();
+        // context.navigateToPath("/app/$pid/menu");
       }
     }
   }
 
-  switchLang(BuildContext context, code, tkn) async {
-    // print("switchLang called");
+  switchLang(code, tkn) async {
+    // final ctx = UIService.context;
+    // if(ctx == null) return;
+
     var lg = code.toUpperCase();
     if(lang.indexOf(code) > 1){
       lg = lang.indexOf(code) - 1;
     }
-    var p;
+    Map<String, dynamic> p;
     if(U.newServerVersion(1754624839)){
       p = {
         'userId': tkn['user_name'].toString().replaceFirst('inf-', ""),
@@ -321,12 +331,14 @@ class _LoginPageState extends State<LoginPage> {
       };
     }
     try{
-      var res = await apiAuthProvider.patchData('/api/informants/' + tkn['userId'].toString(), p, context);
+      var res = await apiAuthProvider.patchData('/api/informants/${tkn['userId']}', p);
       if (res != null) {
         // print("res $res");
         // context.setLocale(Locale(code));
       }
-    }catch(e){}
+    }catch(e){
+      debugPrint(e.toString());
+    }
 
   }
 
@@ -339,7 +351,7 @@ class _LoginPageState extends State<LoginPage> {
 
       return jwt['forceChgPass'];
     } catch(e) {
-      print(e.toString());
+      debugPrint(e.toString());
     }
   }
 }
@@ -437,8 +449,8 @@ changeLangLogin(BuildContext context, List lang){
 //------------------------------------------------------------------------------
 
 class ForceChgPassPage extends StatefulWidget {
-  Map<String, String> data;
-  ForceChgPassPage({required this.data, Key? key}) : super(key: key);
+  final Map<String, String> data;
+  const ForceChgPassPage({required this.data, super.key});
 
   @override
   State<ForceChgPassPage> createState() => _ForceChgPassPageState();
@@ -450,7 +462,7 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
   final ApiAuthRepository apiAuthRepository = ApiAuthRepository();
   final SharedPreferencesManager sharedPreferencesManager = locator<SharedPreferencesManager>();
 
-  var tokenData;
+  Map<String, dynamic> tokenData = {};
   String companyName = '';
   String serialNumber = '';
   String guestName = '';
@@ -458,11 +470,15 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
   String imageUrl = '';
 
   String newPassword = '';
-  String cfrmPassword = '';
+  String confirmPassword = '';
 
   @override
   void initState() {
-    try{imageUrl = apiAuthProvider.baseUrl + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';}catch(e){}
+    try{
+      imageUrl = '${apiAuthProvider.baseUrl}${U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))}/assets/background/tn';
+    }catch(e){
+      debugPrint(e.toString());
+    }
     getTokenData();
     getCompanyName();
     // TODO: implement initState
@@ -472,7 +488,7 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
   getTokenData() async {
     String accessToken = sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessToken)!;
     var jwt = token.parseJwtPayLoad(accessToken);
-    var user = await apiAuthProvider.getData('/api/informants/'+jwt['userId'].toString(), null, context);
+    var user = await apiAuthProvider.getData('/api/informants/${jwt['userId']}', null);
     if(user!=null){
       setState((){
         tokenData = jwt;
@@ -483,11 +499,11 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
   }
 
   getCompanyName() async {
-    var lics = await U.reloadLicense();
-    if (lics != null && mounted) {
+    var license = await U.reloadLicense();
+    if (license != null && mounted) {
       setState(() {
-        companyName = lics['companyName']??'';
-        serialNumber = lics['serialNumber']??'';
+        companyName = license['companyName']??'';
+        serialNumber = license['serialNumber']??'';
       });
     }
   }
@@ -579,7 +595,7 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
                         children: [
                           loginFieldTemplate(placeholder: 'newPassword'.tr(), value: newPassword, action: (val)=>newPassword = val, isPassword: true, submit: (val)=>loginAction()),
                           SizedBox(height: 12),
-                          loginFieldTemplate(placeholder: 'confirmPassword'.tr(), value: cfrmPassword, action: (val)=>cfrmPassword = val, isPassword: true, submit: (val)=>loginAction()),
+                          loginFieldTemplate(placeholder: 'confirmPassword'.tr(), value: confirmPassword, action: (val)=>confirmPassword = val, isPassword: true, submit: (val)=>loginAction()),
                           GestureDetector(
                             child: Container(
                               width: double.infinity,
@@ -607,7 +623,7 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
                     Container(
                       margin: const EdgeInsets.only(top: 145),
                       alignment: Alignment.bottomCenter,
-                      child: Text('version'.tr() + ' ' + ApiAuthProvider().displayVersion, style: TextStyle(color: Colors.white, fontSize: 14))
+                      child: Text('${'version'.tr()} ${ApiAuthProvider().displayVersion}', style: TextStyle(color: Colors.white, fontSize: 14))
                     ),
                   ],
                 ),
@@ -621,21 +637,22 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
 
   loginAction() async{
     if(!loading){
-      if(newPassword.isNotEmpty && cfrmPassword.isNotEmpty){
-        if(newPassword.trim() == cfrmPassword.trim()){
+      if(newPassword.isNotEmpty && confirmPassword.isNotEmpty){
+        if(newPassword.trim() == confirmPassword.trim()){
           setState(()=>loading = true);
           var data = {'oldPassword': widget.data['password'], 'password': newPassword.trim()};
 
           try {
-            var res = await apiAuthProvider.patchData('/api/informants/${tokenData['userId']}/changePassword', data, context);
+            var res = await apiAuthProvider.patchData('/api/informants/${tokenData['userId']}/changePassword', data);
             if (res != null) {
-              String password = tokenData['related'] && tokenData['tenantCode'] != null && tokenData['tenantCode'] != '' ? tokenData['tenantCode'] + ' ' + data['password'] : data['password'];
+              String password = tokenData['related'] && tokenData['tenantCode'] != null && tokenData['tenantCode'] != '' ?  tokenData['tenantCode'] + ' ${data['password']}' : data['password'];
 
               LoginBody loginBody = LoginBody(widget.data['username']!, password, 'password');
               Token token = await apiAuthRepository.postLoginUser(loginBody);
               if (token.error != null) {
                 setState(()=>loading = false);
-                showError(context, token.error);
+                UIService.showError(token.error.toString());
+                // showError(context, token.error);
               } else {
                 await U.setChangedPassword(true);
                 var parsedToken = U.token.parseJwtPayLoad(token.accessToken!);
@@ -648,13 +665,14 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
                 await sharedPreferencesManager.putString(SharedPreferencesManager.keyPendingData, jsonEncode([]));
                 U.setHidePayload(true);
                 // var pid = U.getAccessCode();
-                var pid = U.getPidFromUrl(context.router.currentUrl);
-                context.router.removeLast();
-                context.navigateToPath("/app/$pid/menu");
+                var pid = U.getPidFromUrl(UIService.getCurrentUrl());
+                UIService.navigateNamed("/app/$pid/menu");
+                // context.router.removeLast();
+                // context.navigateToPath("/app/$pid/menu");
               }
             }
           } catch(e) {
-            print(e.toString());
+            debugPrint(e.toString());
             setState(()=>loading = false);
           }
         }
@@ -663,7 +681,9 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
         }
       }
       else{
-        showError(context, 'cannotEmpty'.tr());
+        debugPrint("Mbuh");
+        UIService.showError('cannotEmpty'.tr());
+        // showError(context, 'cannotEmpty'.tr());
       }
     }
   }

+ 23 - 26
lib/src/layouts/auth/qr.dart

@@ -14,13 +14,14 @@ import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/layouts/auth/login.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:toggle_switch/toggle_switch.dart';
 import 'package:url_launcher/url_launcher.dart';
 import 'package:http/http.dart' as http;
 
 @RoutePage()
 class NewQrPage extends StatefulWidget {
-  const NewQrPage({Key? key}) : super(key: key);
+  const NewQrPage({super.key});
 
   @override
   State<NewQrPage> createState() => _NewQrPageState();
@@ -31,7 +32,7 @@ class _NewQrPageState extends State<NewQrPage> {
   final ApiAuthRepository apiAuthRepository = ApiAuthRepository();
   final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
   String? accCode = "";
-  bool? _isAccCodeExist = false;
+  // bool? _isAccCodeExist = false;
 
   String companyName = '';
   String serialNumber = '';
@@ -43,7 +44,7 @@ class _NewQrPageState extends State<NewQrPage> {
   @override
   void initState() {
     try {
-      imageUrl = apiAuthProvider.baseUrl + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';
+      imageUrl = '${apiAuthProvider.baseUrl}${U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))}/assets/background/tn';
       http.head(Uri.parse(imageUrl));
     }catch(e){
       useAsset = true;
@@ -168,8 +169,8 @@ class _NewQrPageState extends State<NewQrPage> {
                     GestureDetector(
                       child: Text('policy'.tr(), style: TextStyle(color: Colors.white, fontSize: 14, decoration: TextDecoration.underline)),
                       onTap: () async {
-                        Uri _url = Uri.parse('https://telmessenger.com/privacy-police/telnow.html');
-                        await canLaunchUrl(_url) ? await launchUrl(_url) : print('Could not launch $_url');
+                        Uri url = Uri.parse('https://telmessenger.com/privacy-police/telnow.html');
+                        await canLaunchUrl(url) ? await launchUrl(url) : debugPrint('Could not launch $url');
                       },
                     ),
                     SizedBox(height: 5),
@@ -229,12 +230,11 @@ class _NewQrPageState extends State<NewQrPage> {
   Future scanDialog() async {
     try {
       final result = await InternetAddress.lookup('google.com');
-      if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
-        // String barcode = await FlutterBarcodeScanner.scanBarcode('#ff6666', 'Cancel', true, ScanMode.QR);
+      if (result.isNotEmpty && result[0].rawAddress.isNotEmpty && mounted) {
         showModalBottomSheet<void>(
           context: context,
           backgroundColor: Colors.white,
-          builder: (BuildContext contextt) {
+          builder: (BuildContext ctx) {
             return SafeArea(
               child: Column(
                 mainAxisSize: MainAxisSize.min,
@@ -250,7 +250,7 @@ class _NewQrPageState extends State<NewQrPage> {
                     leading: Icon(Icons.image),
                     title: Text('fromGallery'.tr(), style: TextStyle(color: textColor)),
                     onTap: () async{
-                      Navigator.of(contextt).pop();
+                      Navigator.of(ctx).pop();
                       XFile? file = await ImagePicker().pickImage(source: ImageSource.gallery);
                       if(file != null){
                         final BarcodeCapture? barcodeCapture = await MobileScannerController().analyzeImage(file.path);
@@ -259,7 +259,7 @@ class _NewQrPageState extends State<NewQrPage> {
                           scan(str);
                         }
                         else{
-                          showError(context, 'messageInvalidCode'.tr());
+                          UIService.showError('messageInvalidCode'.tr());
                         }
                       }
                     },
@@ -268,7 +268,7 @@ class _NewQrPageState extends State<NewQrPage> {
                     leading: Icon(Icons.camera),
                     title: Text('fromCamera'.tr(), style: TextStyle(color: textColor)),
                     onTap: () async{
-                      Navigator.of(contextt).pop();
+                      Navigator.of(ctx).pop();
                       Navigator.push(context, MaterialPageRoute(builder: (context) => const ScanBarcodePage())).then((barcode){
                         if(barcode != null){
                           scan(barcode);
@@ -283,26 +283,23 @@ class _NewQrPageState extends State<NewQrPage> {
         );
       }
     } on SocketException catch (_) {
-      handlingError(context, 0); //no internet
+      UIService.handlingError(ErrorType.noInternet); //no internet
     } on PlatformException catch (e) {
       if (e.code != '') {
-        showError(context, 'messageCamPermission'.tr());
+        UIService.showError('messageCamPermission'.tr());
       } else {
-        showError(context, 'messageInvalidCode'.tr());
+        UIService.showError('messageInvalidCode'.tr());
       }
     } catch (e) {
-      showError(context, 'messageInvalidCode'.tr());
+      UIService.showError('messageInvalidCode'.tr());
     }
   }
 
   Future scan(String barcode) async {
-// print("called scan()");
     try {
       var pidString = barcode.split("app/").last;
       var pid = Uri.decodeComponent(pidString.split('/').first);
       var splitPID = pid.split('#');
-// print(splitPID);
-// print(U.decodeBase64Url(splitPID[1]));
 
       await _sharedPreferencesManager.clearKey(SharedPreferencesManager.version);
       await _sharedPreferencesManager.putString(SharedPreferencesManager.keyBaseUrl, U.rewriteUrl(splitPID[1]));
@@ -311,15 +308,17 @@ class _NewQrPageState extends State<NewQrPage> {
       apiAuthRepository.init();
 
       if (!pidString.contains('loginme')) {
-        context.router.removeLast();
-        context.navigateToPath("/app/${Uri.encodeComponent(splitPID[0])}/login");
+        UIService.navigateNamed("/app/${Uri.encodeComponent(splitPID[0])}/login");
+        // context.router.removeLast();
+        // context.navigateToPath("/app/${Uri.encodeComponent(splitPID[0])}/login");
       } else {
-        context.router.removeLast();
-        context.navigateToPath("/app/${Uri.encodeComponent(splitPID[0])}/loginme"+pidString.split("/loginme").last);
+        UIService.navigateNamed("/app/${Uri.encodeComponent(splitPID[0])}/loginme${pidString.split("/loginme").last}");
+        // context.router.removeLast();
+        // context.navigateToPath("/app/${Uri.encodeComponent(splitPID[0])}/loginme${pidString.split("/loginme").last}");
       }
     } catch (e) {
-      print(e);
-      if(mounted) showError(context, 'messageInvalidCode'.tr());
+      debugPrint(e.toString());
+      UIService.showError('messageInvalidCode'.tr());
     }
   }
 }
@@ -338,8 +337,6 @@ class _ScanBarcodePageState extends State<ScanBarcodePage> {
     return Scaffold(
       body: MobileScanner(
         onDetect: (barcodes) {
-          // print('sini');
-          // print(barcodes.barcodes.singleOrNull?.displayValue);
           Navigator.of(context).pop(barcodes.barcodes.firstOrNull?.displayValue);
         },
       ),

+ 40 - 37
lib/src/layouts/components/auto_login.dart

@@ -13,9 +13,12 @@ import 'package:telnow_mobile_new/src/model/token/token.dart';
 import 'package:flutter/material.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 @RoutePage()
 class AutoLoginPage extends StatefulWidget {
+  const AutoLoginPage({super.key});
+
   @override
   State<AutoLoginPage> createState() => _AutoLoginPageState();
 }
@@ -35,8 +38,8 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
   String progressMsg = 'initializing'.tr();
 
   String newPassword = '';
-  String cfrmPassword = '';
-  Map <String, String> Ddata = {};
+  String confirmPassword = '';
+  Map <String, String> dData = {};
 
   @override
   void initState() {
@@ -59,7 +62,7 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
           case 'prm':prmJson = U.decodeBase64Url(Uri.decodeComponent(v));
         }
       });
-      Ddata = {
+      dData = {
         "username": jsonDecode(prmJson)["username"],
         "password": jsonDecode(prmJson)["password"]
       };
@@ -67,26 +70,24 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
       getTokenData();
     } catch(e) {
       U.showDebugToast(e.toString());
-      print("invalid param: " + e.toString());
+      debugPrint("invalid param: $e");
       redirectToLoginPage = true;
       Future.delayed(Duration.zero, (){
-        context.router.removeLast();
-        context.navigateToPath("/app/${U.getAccessCode()}");
+        UIService.navigateNamed("/app/${U.getAccessCode()}");
+        // context.router.removeLast();
+        // context.navigateToPath("/app/${U.getAccessCode()}");
       });
     }
   }
 
   getTokenData({int n = 0}) async {
     JwtToken jwtToken = JwtToken();
-    // if (n > 2){
-    //
-    // }
     showLoading(context);
     try {
-      Token token = await apiAuthRepository.postLoginUser(LoginBody(Ddata["username"]!.trim(), Ddata["password"]!.trim(), "password"));
+      Token token = await apiAuthRepository.postLoginUser(LoginBody(dData["username"]!.trim(), dData["password"]!.trim(), "password"));
       if (token.error != null) {
-        closeLoading(context);
-        showError(context, token.error);
+        UIService.closeLoading();
+        UIService.showError(token.error.toString());
         setState(() {
           loading = false;
           invalidUserLogin = true;
@@ -101,9 +102,9 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
       String accessToken = sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessToken)!;
       var jwt = jwtToken.parseJwtPayLoad(accessToken);
       // print(jwt);
-      var user = await apiAuthProvider.getData('/api/informants/'+jwt['userId'].toString(), null, context);
+      var user = await apiAuthProvider.getData('/api/informants/${jwt['userId']}', null);
       if(user != null){
-        closeLoading(context);
+        UIService.closeLoading();
         setState((){
           tokenData = jwt;
           context.setLocale(Locale(tokenData!['language'].toLowerCase()));
@@ -111,18 +112,18 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
         });
       }
     } catch(e){
-      closeLoading(context);
-      print(e.toString());
+      UIService.closeLoading();
+      debugPrint(e.toString());
       U.showDebugToast(e.toString());
       getTokenData(n: n + 1);
     }
   }
 
   getCompanyName() async {
-    var lics = await U.getLicense();
-    if (lics != null && lics['companyName'] != null && mounted) {
+    var license = await U.getLicense();
+    if (license != null && license['companyName'] != null && mounted) {
       setState(() {
-        companyName = lics['companyName'];
+        companyName = license['companyName'];
       });
     }
   }
@@ -131,11 +132,12 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
     try {
       // U.clearAccessCode();
       U.clearPreferences().then((value) {
-        context.router.removeLast();
-        context.navigateToPath('/qr?new=true');
+        UIService.navigateNamed('/qr?new=true');
+        // context.router.removeLast();
+        // context.navigateToPath('/qr?new=true');
       });
     } catch(e) {
-      print(e.toString());
+      debugPrint(e.toString());
       U.showDebugToast(e.toString());
     }
   }
@@ -204,7 +206,7 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
                     ),
                     !invalidUserLogin ? Column(
                       children: [
-                        Text('welcome'.tr()+' $guestName', style: TextStyle(color: Colors.white, fontSize: 16), textAlign: TextAlign.center),
+                        Text('${'welcome'.tr()} $guestName', style: TextStyle(color: Colors.white, fontSize: 16), textAlign: TextAlign.center),
                         SizedBox(height: 16),
                         Text('createNewPassText'.tr(), style: TextStyle(color: Colors.white, fontSize: 16), textAlign: TextAlign.center),
                       ],
@@ -214,7 +216,7 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
                         SizedBox(height: 80),
                         loginFieldTemplate(placeholder: 'newPassword'.tr(), value: newPassword, action: (val)=>newPassword = val, isPassword: true, submit: (val)=>loginAction()),
                         SizedBox(height: 12),
-                        loginFieldTemplate(placeholder: 'confirmPassword'.tr(), value: cfrmPassword, action: (val)=>cfrmPassword = val, isPassword: true, submit: (val)=>loginAction()),
+                        loginFieldTemplate(placeholder: 'confirmPassword'.tr(), value: confirmPassword, action: (val)=>confirmPassword = val, isPassword: true, submit: (val)=>loginAction()),
                         GestureDetector(
                           child: Container(
                             width: double.infinity,
@@ -265,55 +267,56 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
 
   loginAction() async{
     if(!loading && tokenData != null){
-      if(newPassword.isNotEmpty && cfrmPassword.isNotEmpty){
-        if(newPassword.trim() == cfrmPassword.trim()){
+      if(newPassword.isNotEmpty && confirmPassword.isNotEmpty){
+        if(newPassword.trim() == confirmPassword.trim()){
           setState(() {
             loading = true;
             onProgress = true;
           });
-          var data = {'oldPassword': Ddata['password'], 'password': newPassword.trim()};
+          var data = {'oldPassword': dData['password'], 'password': newPassword.trim()};
 
           try {
-            var res = await apiAuthProvider.patchData('/api/informants/${tokenData!['userId']}/changePassword', data, context);
+            var res = await apiAuthProvider.patchData('/api/informants/${tokenData!['userId']}/changePassword', data);
             if (res != null) {
               // setState(() => progressMsg = "Initializing...");
-              String password = tokenData!['related'] && tokenData!['tenantCode'] != null && tokenData!['tenantCode'] != '' ? tokenData!['tenantCode'] + ' ' + data['password'] : data['password'];
+              String password = tokenData!['related'] && tokenData!['tenantCode'] != null && tokenData!['tenantCode'] != '' ? tokenData!['tenantCode'] + ' ${data['password']}' : data['password'];
 
-              LoginBody loginBody = LoginBody(Ddata['username']!, password, 'password');
+              LoginBody loginBody = LoginBody(dData['username']!, password, 'password');
               Token token = await apiAuthRepository.postLoginUser(loginBody);
               if (token.error != null) {
                 setState(() => loading = false);
-                showError(context, token.error);
+                UIService.showError(token.error.toString());
               } else {
                 setState(() => progressMsg = "redirecting".tr());
                 await U.setChangedPassword(true);
                 var parsedToken = U.token.parseJwtPayLoad(token.accessToken!);
                 await sharedPreferencesManager.putString(SharedPreferencesManager.keyAccessToken, token.accessToken!);
                 await sharedPreferencesManager.putString(SharedPreferencesManager.keyRefreshToken, token.refreshToken!);
-                await sharedPreferencesManager.putString(SharedPreferencesManager.keyUsername, tokenData!['related'] ? parsedToken['user_name'].split("-").last : Ddata['username']);
+                await sharedPreferencesManager.putString(SharedPreferencesManager.keyUsername, tokenData!['related'] ? parsedToken['user_name'].split("-").last : dData['username']);
                 await sharedPreferencesManager.putBool(SharedPreferencesManager.keyIsLogin, true);
                 await sharedPreferencesManager.putString(SharedPreferencesManager.keyScoope, 'INSIDE');
                 await sharedPreferencesManager.putInt(SharedPreferencesManager.keyCountRefreshToken, 0);
                 U.setHidePayload(true);
                 var pid = U.getAccessCode();
-                context.router.removeLast();
-                context.navigateToPath("/app/$pid/menu");
+                UIService.navigateNamed("/app/$pid/menu");
+                // context.router.removeLast();
+                // context.navigateToPath("/app/$pid/menu");
               }
             }
             else{
               setState(() => loading = false);
             }
           } catch(e) {
-            print(e.toString());
+            debugPrint(e.toString());
             setState(() => loading = false);
           }
         }
         else{
-          showError(context, 'checkNewPass'.tr());
+          UIService.showError('checkNewPass'.tr());
         }
       }
       else{
-        showError(context, 'cannotEmpty'.tr());
+        UIService.showError('cannotEmpty'.tr());
       }
     }
   }

+ 6 - 4
lib/src/layouts/components/camera.dart

@@ -1,9 +1,10 @@
 import 'package:camera/camera.dart';
 import 'package:flutter/material.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 class OpenCamera extends StatefulWidget {
   final List<CameraDescription>? cameras;
-  const OpenCamera({required this.cameras, Key? key}) : super(key: key);
+  const OpenCamera({required this.cameras, super.key});
 
   @override
   State<OpenCamera> createState() => _OpenCameraState();
@@ -35,9 +36,10 @@ class _OpenCameraState extends State<OpenCamera> {
     try {
       await _cameraController.setFlashMode(FlashMode.off);
       XFile pickedFile = await _cameraController.takePicture();
-      Navigator.of(context).pop(pickedFile);
+      UIService.navigatorKey.currentState?.pop(pickedFile);
+      // Navigator.of(context).pop(pickedFile);
     } on CameraException catch (e) {
-      debugPrint('Error occured while taking picture: $e');
+      debugPrint('Error occurred while taking picture: $e');
       return null;
     }
   }
@@ -59,7 +61,7 @@ class _OpenCameraState extends State<OpenCamera> {
   Widget build(BuildContext context) {
     return Scaffold(
         body: SafeArea(
-          child: Container(
+          child: SizedBox(
             width: double.infinity, height: double.infinity,
             child: Stack(children: [
               (_cameraController.value.isInitialized) ? CameraPreview(_cameraController) : Container(color: Colors.black, child: const Center(child: CircularProgressIndicator())),

+ 13 - 16
lib/src/layouts/components/error_page.dart

@@ -6,33 +6,31 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 
 @RoutePage()
 class ErrorPage extends StatefulWidget {
-  final code;
-  final errMsg;
+  final String code;
+  final String errMsg;
 
-  ErrorPage(this.code, this.errMsg);
+  const ErrorPage(this.code, this.errMsg, {super.key});
 
   @override
-  _ErrorPageState createState() => _ErrorPageState();
+  ErrorPageState createState() => ErrorPageState();
 }
 
-class _ErrorPageState extends State<ErrorPage> {
+class ErrorPageState extends State<ErrorPage> {
   final ApiAuthProvider _apiAuthProvider = ApiAuthProvider();
   String stringError = '';
   String imageUrl = '';
 
   @override
   void initState() {
-    try{imageUrl = _apiAuthProvider.baseUrl + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';}catch(e){}
+    try{
+      imageUrl = '${_apiAuthProvider.baseUrl}${U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))}/assets/background/tn';
+    } catch(e) {
+      debugPrint(e.toString());
+    }
     // TODO: implement initState
     switch (widget.errMsg) {
       case "_strErrVer":
-        stringError = "invalidVersion".tr() +
-          ". " +
-          "sugestVersion".tr() + " " +
-          U.getVersion().toString() +
-          ". " +
-          "currentVersion".tr() + " " +
-          ApiAuthProvider().currentVersion.toString() + ".";
+        stringError = "${"invalidVersion".tr()}. ${"sugestVersion".tr()} ${U.getVersion()}. ${"currentVersion".tr()} ${ApiAuthProvider().currentVersion}.";
         break;
         default:
           stringError = widget.errMsg;
@@ -45,14 +43,13 @@ class _ErrorPageState extends State<ErrorPage> {
     return Scaffold(
       body: Stack(
         children: [
-          Container(
+          SizedBox(
             width: double.infinity, height: double.infinity,
             child: Image.network(imageUrl, fit: BoxFit.cover, width: double.infinity, height: double.infinity, errorBuilder: (context, error, stackTrace) {
               return Image.asset('assets/image/background/background.jpg', fit: BoxFit.cover, width: double.infinity, height: double.infinity);
             }),
           ),
           Container(
-            // decoration: BoxDecoration(color: Color(0xff0D497F).withValues(alpha: 0.85)),
             decoration: BoxDecoration(
                 gradient: LinearGradient(
                     begin: Alignment.topCenter, end: Alignment.bottomCenter,
@@ -69,7 +66,7 @@ class _ErrorPageState extends State<ErrorPage> {
                 Padding(
                   padding: EdgeInsets.only(bottom: 30),
                   child: Image(
-                    image: new AssetImage("assets/image/logo/logo.png"),
+                    image: AssetImage("assets/image/logo/logo.png"),
                     width: 100,
                   ),
                 ),

+ 19 - 21
lib/src/layouts/components/photo_chat.dart

@@ -1,6 +1,4 @@
 import 'dart:convert';
-import 'dart:typed_data';
-
 import 'package:camera/camera.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/foundation.dart';
@@ -12,29 +10,29 @@ import 'package:telnow_mobile_new/src/api/api_auth_provider.dart';
 import 'package:telnow_mobile_new/src/layouts/components/camera.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:image/image.dart' as img;
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 class PhotoChat extends StatefulWidget {
-  final data;
+  final Map<String, dynamic> data;
   final ImageSource? media;
   final bool? isBroadcast;
   final Uint8List? image;
 
-  PhotoChat(this.data, this.media, this.isBroadcast, this.image);
+  const PhotoChat(this.data, this.media, this.isBroadcast, this.image, {super.key});
 
   @override
-  _PhotoChatState createState() => _PhotoChatState();
+  PhotoChatState createState() => PhotoChatState();
 }
 
-class _PhotoChatState extends State<PhotoChat> {
+class PhotoChatState extends State<PhotoChat> {
   final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
-  TextEditingController controllerPesan = new TextEditingController();
+  TextEditingController controllerPesan = TextEditingController();
   Uint8List? image;
 
   Future getImage() async {
-//    print('photochat in');
     XFile? pickedFile;
     if(widget.media == ImageSource.camera){
-      await availableCameras().then((camera) => Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: OpenCamera(cameras: camera))).then((value) async{
+      await availableCameras().then((camera) => Navigator.push(UIService.navigatorKey.currentContext!, PageTransition(type: PageTransitionType.rightToLeft, child: OpenCamera(cameras: camera))).then((value) async{
         if(value!=null){
           pickedFile = value;
         }
@@ -45,15 +43,16 @@ class _PhotoChatState extends State<PhotoChat> {
     }
 
     if (pickedFile != null) {
-      var imagess = img.decodeImage(await pickedFile!.readAsBytes());
-      var imgPercent = (1000 / (imagess!.width / 100)).toDouble();
-      if (imagess.width > 1000) {
-        imagess = img.copyResize(imagess, width: ((imagess.width / 100) * imgPercent).toInt(), height: ((imagess.height / 100) * imgPercent).toInt());
+      var images = img.decodeImage(await pickedFile!.readAsBytes());
+      var imgPercent = (1000 / (images!.width / 100)).toDouble();
+      if (images.width > 1000) {
+        images = img.copyResize(images, width: ((images.width / 100) * imgPercent).toInt(), height: ((images.height / 100) * imgPercent).toInt());
       }
-      var compressed = img.encodeJpg(imagess, quality: 60);
+      var compressed = img.encodeJpg(images, quality: 60);
       setState(() => image = compressed);
     } else {
-      Navigator.of(context).pop(null);
+      UIService.navigatorKey.currentState?.pop(null);
+      // Navigator.of(context).pop(null);
     }
   }
 
@@ -83,7 +82,7 @@ class _PhotoChatState extends State<PhotoChat> {
         ),
       ),
       body: Center(
-        child: Container(
+        child: SizedBox(
           width: U.bodyWidth(context),
           child: Stack(
             children: [
@@ -97,7 +96,6 @@ class _PhotoChatState extends State<PhotoChat> {
               Container(
                 alignment: Alignment.bottomCenter,
                 child: Container(
-//              width: double.infinity,
                   padding: const EdgeInsets.all(10),
                   color: Colors.black54,
                   child: Row(
@@ -129,12 +127,12 @@ class _PhotoChatState extends State<PhotoChat> {
                             height: 50,
                             child: ElevatedButton(
                               style: ButtonStyle(
-                                backgroundColor: MaterialStateProperty.all<Color>(primaryColor),
-                                shape: MaterialStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(
+                                backgroundColor: WidgetStateProperty.all<Color>(primaryColor),
+                                shape: WidgetStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(
                                   borderRadius: BorderRadius.circular(50),
                                 )),
-                                elevation: MaterialStateProperty.all(2),
-                                padding: MaterialStateProperty.all(const EdgeInsets.only(left: 1)),
+                                elevation: WidgetStateProperty.all(2),
+                                padding: WidgetStateProperty.all(const EdgeInsets.only(left: 1)),
                               ),
                               child: Image.asset('assets/image/icon/Send.png', width: 25, height: 25),
                               onPressed: () async {

+ 4 - 7
lib/src/layouts/components/responsive.dart

@@ -1,9 +1,6 @@
 import 'package:auto_route/auto_route.dart';
 import 'package:flutter/material.dart';
 import 'package:telnow_mobile_new/src/layouts/mobile/app_mobile.dart';
-import 'package:telnow_mobile_new/src/layouts/mobile/menu_account.dart';
-import 'package:telnow_mobile_new/src/layouts/mobile/menu_history.dart';
-import 'package:telnow_mobile_new/src/layouts/mobile/menu_home.dart';
 import 'package:telnow_mobile_new/src/layouts/web/app_web.dart';
 import 'package:telnow_mobile_new/src/layouts/web/menu_account.dart';
 import 'package:telnow_mobile_new/src/layouts/web/menu_history.dart';
@@ -16,7 +13,7 @@ class AppResponsive extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return U.webView(context)?WebAppPage(AutoRouter()):MobAppPage(AutoRouter());
+    return U.webView(context) ? WebAppPage(AutoRouter()) : MobAppPage(AutoRouter());
   }
 }
 
@@ -26,7 +23,7 @@ class HomeResponsive extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return U.webView(context)?WebHomePage():MobMenuTemplate(child: MobHomePage());
+    return U.webView(context) ? WebHomePage() : MobMenuTemplate();
   }
 }
 
@@ -36,7 +33,7 @@ class HistoryResponsive extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return U.webView(context)?WebHistoryPage():MobMenuTemplate(child: MobHistoryPage());
+    return U.webView(context) ? WebHistoryPage() : MobMenuTemplate();
   }
 }
 
@@ -46,7 +43,7 @@ class AccountResponsive extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return U.webView(context)?WebAccountPage():MobMenuTemplate(child: MobAccountPage());
+    return U.webView(context) ? WebAccountPage() : MobMenuTemplate();
   }
 }
 

+ 59 - 71
lib/src/layouts/components/template.dart

@@ -18,19 +18,14 @@ import 'package:lottie/lottie.dart';
 import 'package:page_transition/page_transition.dart';
 import 'package:photo_view/photo_view.dart';
 import 'package:photo_view/photo_view_gallery.dart';
-import 'package:provider/provider.dart';
-import 'package:telnow_mobile_new/src/api/api_auth_provider.dart';
 import 'package:telnow_mobile_new/src/api/jwt_token.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
 import 'package:telnow_mobile_new/src/layouts/auth/qr.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
-import 'package:telnow_mobile_new/src/utils/dio_logging_interceptors.dart';
 import 'package:uuid/uuid.dart';
 
-import '../../utils/provider.dart';
-
 PreferredSizeWidget appBarTemplate({required BuildContext context, required String title, String icon = 'arrow-left', List<Widget>? action, exc = false}){
   return AppBar(
     elevation: 0,
@@ -46,16 +41,16 @@ PreferredSizeWidget appBarTemplate({required BuildContext context, required Stri
   );
 }
 
-Widget buttonTemplate({double width = double.infinity, double height = 51, required String text, required action(), backgroundColor = primaryColor, textColor = Colors.white, borderColor = primaryColor}){
+Widget buttonTemplate({double width = double.infinity, double height = 51, required String text, required Function() action, backgroundColor = primaryColor, textColor = Colors.white, borderColor = primaryColor}){
   return SizedBox(
     width: width,
     height: height,
     child: ElevatedButton(
       style: ButtonStyle(
-          elevation: MaterialStateProperty.all<double>(0),
-          backgroundColor: MaterialStateProperty.all<Color>(backgroundColor),
-          shape: MaterialStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(12))),
-          side: MaterialStateProperty.all<BorderSide>(BorderSide(color: borderColor))
+          elevation: WidgetStateProperty.all<double>(0),
+          backgroundColor: WidgetStateProperty.all<Color>(backgroundColor),
+          shape: WidgetStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(12))),
+          side: WidgetStateProperty.all<BorderSide>(BorderSide(color: borderColor))
       ),
       onPressed: ()=>action(),
       child: Text(text, style: TextStyle(fontSize: 16, color: textColor, fontWeight: FontWeight.w500)),
@@ -93,16 +88,16 @@ Widget textHorizontal(String title, String subtitle, {double size = 14, double o
               },
             ),
           ],
-        ):Text("$subtitle", style: TextStyle(color: textColor, fontSize: size), maxLines: 3, overflow: TextOverflow.ellipsis, textAlign: TextAlign.end,),
+        ):Text(subtitle, style: TextStyle(color: textColor, fontSize: size), maxLines: 3, overflow: TextOverflow.ellipsis, textAlign: TextAlign.end,),
       ),
     ],
   );
 }
 
-Widget divider({opacity = 0.15, EdgeInsetsGeometry padding = const EdgeInsets.only(top: 0)}){
+Widget divider({double opacity = 0.15, EdgeInsetsGeometry padding = const EdgeInsets.only(top: 0), Color color = textColor, thickness = 1.0}){
   return Padding(
     padding: padding,
-    child: Divider(height: 1, thickness: 1, color: textColor.withValues(alpha: opacity)),
+    child: Divider(height: 1, thickness: thickness, color: color.withValues(alpha: opacity)),
   );
 }
 
@@ -122,17 +117,17 @@ Widget separator(){
 Widget categoryContainer({required BuildContext context, required String iconUrl}){
   double size = U.bodyWidth(context)/6;
 
-  String ext = iconUrl.split(".").last;
+  // String ext = iconUrl.split(".").last;
 
   return Container(
     padding: EdgeInsets.all(5),
-    child: Image(image: CachedNetworkImageProvider(iconUrl+'?bridge-cache=true', cacheManager: CacheManager(CacheMan.config(iconUrl+'?bridge-cache=true'))), width: size, height: size),
     decoration: BoxDecoration(
         color: Colors.white,
         borderRadius: BorderRadius.all(Radius.circular(20)),
         border: Border.all(width: 0.2, color: Colors.black12),
         boxShadow: [BoxShadow(color: Colors.grey.withValues(alpha: 0.3), blurRadius: 2, offset: Offset(0, 2))]
     ),
+    child: Image(image: CachedNetworkImageProvider('$iconUrl?bridge-cache=true', cacheManager: CacheManager(CacheMan.config('$iconUrl?bridge-cache=true'))), width: size, height: size),
   );
 }
 
@@ -149,18 +144,6 @@ Widget shimmerTopMenu(BuildContext context){
           borderRadius: BorderRadius.all(Radius.circular(20)),
         ),
       );
-
-      // return Shimmer.fromColors(
-      //   baseColor: Colors.grey[400]!,
-      //   highlightColor: Colors.white,
-      //   child: Container(
-      //     width: size, height: size,
-      //     decoration: BoxDecoration(
-      //       color: Colors.white,
-      //       borderRadius: BorderRadius.all(Radius.circular(20)),
-      //     ),
-      //   ),
-      // );
     }),
   );
 }
@@ -171,6 +154,11 @@ Widget requestTiles({required String image, required String title, required Stri
   return Container(
     margin: EdgeInsets.symmetric(vertical: vertical),
     padding: EdgeInsetsDirectional.only(end: border?16:0),
+    decoration: BoxDecoration(
+        color: Colors.white,
+        border: border?Border.all(color: textColor.withValues(alpha: 0.15)):null,
+        borderRadius: border?BorderRadius.all(Radius.circular(12)):null
+    ),
     child: Row(
       children: [
         imageTiles(imageUrl: image),
@@ -187,11 +175,6 @@ Widget requestTiles({required String image, required String title, required Stri
         )
       ],
     ),
-    decoration: BoxDecoration(
-        color: Colors.white,
-        border: border?Border.all(color: textColor.withValues(alpha: 0.15)):null,
-        borderRadius: border?BorderRadius.all(Radius.circular(12)):null
-    ),
   );
 }
 
@@ -208,7 +191,7 @@ Widget imageTiles({required String imageUrl, double width = 100, double height =
         color: textColor.withValues(alpha: 0.1),
         borderRadius: BorderRadius.all(Radius.circular(radius)),
         image: DecorationImage(
-          image: CachedNetworkImageProvider(imageUrl+'?bridge-cache=true', cacheManager: CacheManager(CacheMan.config(imageUrl+'?bridge-cache=true'))),
+          image: CachedNetworkImageProvider('$imageUrl?bridge-cache=true', cacheManager: CacheManager(CacheMan.config('$imageUrl?bridge-cache=true'))),
           fit: BoxFit.cover,
         ),
       ),
@@ -216,15 +199,24 @@ Widget imageTiles({required String imageUrl, double width = 100, double height =
   ): Container(
     height: height,
     width: width,
-    child: Center(child: Text("noImage".tr(), style: TextStyle(fontStyle: FontStyle.italic, fontSize: width<100?10:14, color: Colors.black38), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center,)),
     decoration: BoxDecoration(
         color: Colors.black12,
         borderRadius: BorderRadius.all(Radius.circular(12.0))
     ),
+    child: Center(child: Text("noImage".tr(), style: TextStyle(fontStyle: FontStyle.italic, fontSize: width<100?10:14, color: Colors.black38), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center,)),
   );
 }
 
-Widget loginFieldTemplate({required String value, placeholder = '', required action(value), required submit(value), isPassword = false}){
+typedef FieldCallback = void Function(String value);
+Widget loginFieldTemplate(
+    {
+      required String value,
+      placeholder = '',
+      required FieldCallback action,
+      required FieldCallback submit,
+      isPassword = false
+    }
+){
   TextEditingController controller = TextEditingController()..text = value;
   bool hidePass = true;
   return SizedBox(
@@ -262,26 +254,20 @@ Widget loadingTemplate(VoidCallback setState){
     setState();
   });
   return Center(
-    child: Container(
+    child: SizedBox(
       height: 36,
-      child: LoadingIndicator(
-        indicatorType: Indicator.ballPulseRise,
-        colors: U.defaultRainbowColors(),
-        strokeWidth: 2,
-        backgroundColor: Colors.black.withValues(alpha: 0),
-        pathBackgroundColor: Colors.black,
-      ),
+      child: loadingTemplateNoVoid()
     ),
   );
 }
 
 Widget loadingTemplateNoVoid(){
   return Center(
-    child: Container(
+    child: SizedBox(
       height: 36,
       child: LoadingIndicator(
-        indicatorType: Indicator.ballPulseRise,
-        colors: U.defaultRainbowColors(),
+        indicatorType: Indicator.ballClipRotatePulse,
+        colors: U.loadingIndicatorColors(),
         strokeWidth: 2,
         backgroundColor: Colors.black.withValues(alpha: 0),
         pathBackgroundColor: Colors.black,
@@ -330,22 +316,22 @@ Widget infoContainer(String text){
   return Container(
     width: double.infinity,
     padding: EdgeInsets.all(12),
-    child: Text(text, style: TextStyle(color: textColor), textAlign: TextAlign.center),
     decoration: BoxDecoration(
         color: secondaryColor.withValues(alpha: 0.1),
         border: Border.all(color: secondaryColor),
         borderRadius: BorderRadius.all(Radius.circular(12))
     ),
+    child: Text(text, style: TextStyle(color: textColor), textAlign: TextAlign.center),
   );
 }
 
 class PhotoPreview extends StatelessWidget {
-  final title;
+  final String title;
   final imageUrl;
   final bool isUrl;
   final bool isNetwork;
 
-  PhotoPreview(this.title, this.imageUrl, this.isUrl, {this.isNetwork = false});
+  const PhotoPreview(this.title, this.imageUrl, this.isUrl, {super.key, this.isNetwork = false});
 
   @override
   Widget build(BuildContext context) {
@@ -362,7 +348,7 @@ class PhotoPreview extends StatelessWidget {
           onTap: () => Navigator.of(context).pop(),
         ),
       ),
-      body: Container(
+      body: SizedBox(
           width: MediaQuery.of(context).size.width,
           height: MediaQuery.of(context).size.height,
           child: PhotoView(
@@ -390,12 +376,12 @@ class PhotoPreviewGallery extends StatelessWidget {
         title: Text(title, style: TextStyle(fontSize: 18)),
         backgroundColor: Colors.black.withValues(alpha: 0.4),
         titleSpacing: 0,
-        leading: new IconButton(
-          icon: new Icon(Icons.arrow_back_rounded),
+        leading: IconButton(
+          icon: Icon(Icons.arrow_back_rounded),
           onPressed: () => Navigator.of(context).pop(),
         ),
       ),
-      body: Container(
+      body: SizedBox(
           width: MediaQuery.of(context).size.width,
           height: MediaQuery.of(context).size.height,
           child: PhotoViewGallery.builder(
@@ -404,7 +390,7 @@ class PhotoPreviewGallery extends StatelessWidget {
             builder: (context, index) {
               var uuid = Uuid().v1().replaceAll('-', '');
               return PhotoViewGalleryPageOptions(
-                imageProvider: (isUrl?NetworkImage(imageList[index][column]+'?uuid='+uuid):imageList[index] is File?FileImage(imageList[index]):MemoryImage(imageList[index])) as ImageProvider<Object>?,
+                imageProvider: (isUrl ? NetworkImage('${imageList[index][column]}?uuid=$uuid') : imageList[index] is File ? FileImage(imageList[index]) : MemoryImage(imageList[index])) as ImageProvider<Object>?,
                 maxScale: 5.0, minScale: 0.3,
               );
             },
@@ -417,7 +403,7 @@ class PhotoPreviewGallery extends StatelessWidget {
 //------------------------------------------------------------------------------
 
 class MyClipper extends CustomClipper<Path> {
-  final isNip;
+  final bool isNip;
 
   MyClipper(this.isNip);
 
@@ -448,7 +434,7 @@ class MyClipper extends CustomClipper<Path> {
 }
 
 class YourClipper extends CustomClipper<Path> {
-  final isNip;
+  final bool isNip;
 
   YourClipper(this.isNip);
 
@@ -529,7 +515,7 @@ void showError(BuildContext context, message) {
     flushbarPosition: FlushbarPosition.BOTTOM,
     margin: EdgeInsets.all(8),
     borderRadius: BorderRadius.all(Radius.circular(8)),
-  )..show(context);
+  ).show(context);
 }
 
 void showSuccess(context, message) {
@@ -544,7 +530,7 @@ void showSuccess(context, message) {
     flushbarPosition: FlushbarPosition.BOTTOM,
     margin: EdgeInsets.all(8),
     borderRadius: BorderRadius.all(Radius.circular(8)),
-  )..show(context);
+  ).show(context);
 }
 
 String convertDate(date, String locale) {
@@ -559,9 +545,9 @@ String convertDate(date, String locale) {
 
   final aDate = DateTime(dateToCheck.year, dateToCheck.month, dateToCheck.day);
   if (aDate == today) {
-    return 'today'.tr()+' '+DateFormat('HH:mm', locale).format(DateTime.parse(date));
+    return '${'today'.tr()} ${DateFormat('HH:mm', locale).format(DateTime.parse(date))}';
   } else if (aDate == yesterday) {
-    return 'yesterday'.tr()+' '+DateFormat('HH:mm', locale).format(DateTime.parse(date));
+    return '${'yesterday'.tr()} ${DateFormat('HH:mm', locale).format(DateTime.parse(date))}';
   } else {
     return DateFormat('dd MMM HH:mm', locale).format(DateTime.parse(date));
   }
@@ -572,7 +558,7 @@ String convertDate(date, String locale) {
 class RefreshPage extends StatelessWidget {
   final VoidCallback onRefresh;
 
-  RefreshPage(this.onRefresh);
+  const RefreshPage(this.onRefresh, {super.key});
 
   @override
   Widget build(BuildContext context) {
@@ -599,6 +585,8 @@ final JwtToken token = JwtToken();
 final SharedPreferencesManager _sharedPreferencesManager = locator<SharedPreferencesManager>();
 
 class NoDataPage extends StatelessWidget {
+  const NoDataPage({super.key});
+
   @override
   Widget build(BuildContext context) {
     return Center(
@@ -677,7 +665,7 @@ handlingError(context, type) {
                   width: 200,
                   padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
                   child: Image(
-                      image: AssetImage('assets/image/error/' + data[type]['image']!))),
+                      image: AssetImage('assets/image/error/${data[type]['image']!}'))),
               Padding(
                 padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
                 child: Text(data[type]['title']!,
@@ -699,13 +687,13 @@ handlingError(context, type) {
                     child: TextButton(
                       style: ButtonStyle(
                           backgroundColor:
-                          MaterialStateProperty.all<Color>(primaryColor),
-                          shape: MaterialStateProperty.all<
+                          WidgetStateProperty.all<Color>(primaryColor),
+                          shape: WidgetStateProperty.all<
                               RoundedRectangleBorder>(
                               RoundedRectangleBorder(
                                 borderRadius: BorderRadius.circular(5),
                               ))),
-                      child: new Text('logout'.tr().toUpperCase(),
+                      child: Text('logout'.tr().toUpperCase(),
                           style: TextStyle(color: Colors.white),
                           textAlign: TextAlign.center),
                       onPressed: () {
@@ -744,7 +732,7 @@ handlingError(context, type) {
                       },
                     )),
               )
-                  : Container(height: 1, width: 1)
+                  : SizedBox(height: 1, width: 1)
             ],
           ),
         );
@@ -755,11 +743,11 @@ handlingError(context, type) {
 class NoImageFound extends StatelessWidget {
   bool show = true;
 
-  NoImageFound(this.show);
+  NoImageFound(this.show, {super.key});
 
   @override
   Widget build(BuildContext context) {
-    return Container(
+    return SizedBox(
       width: MediaQuery.of(context).size.width - 150,
       height: MediaQuery.of(context).size.height / 4,
       child: Stack(
@@ -776,12 +764,12 @@ class NoImageFound extends StatelessWidget {
                 child: show
                     ? Container(
                   padding: const EdgeInsets.all(10),
-                  child: Text('notFoundImg'.tr(),
-                      style: TextStyle(fontSize: 12)),
                   decoration: BoxDecoration(
                       color: Colors.white24,
                       borderRadius:
                       BorderRadius.all(Radius.circular(50))),
+                  child: Text('notFoundImg'.tr(),
+                      style: TextStyle(fontSize: 12)),
                 )
                     : Center(
                   child: CircularProgressIndicator(),
@@ -828,7 +816,7 @@ showLoading(BuildContext context, {String? text, String? lottie}){
     barrierColor: lottie!=null?Colors.white:null,
     builder: (BuildContext context) {
       return WillPopScope(
-        onWillPop: ()=>Future.value(false),
+        onWillPop: () => Future.value(false),
         child: Center(
           child: Column(
             mainAxisSize: MainAxisSize.min,

+ 46 - 40
lib/src/layouts/functions/account.dart

@@ -1,5 +1,4 @@
 import 'package:auto_route/auto_route.dart';
-import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:provider/provider.dart';
 import 'package:telnow_mobile_new/main.dart';
@@ -10,25 +9,28 @@ import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferenc
 import 'package:telnow_mobile_new/src/utils/C.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 class AccountFunction{
   final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
   final NotificationClass notification = NotificationClass();
   final JwtToken token = JwtToken();
-  SharedPreferencesManager _sharedPreferencesManager = SharedPreferencesManager();
+  final SharedPreferencesManager _sharedPreferencesManager = SharedPreferencesManager();
+
   getUser(BuildContext context) async {
-    var res = await token.getUserData(context);
+    final userModule = Provider.of<UserModule>(context, listen: false);
+    var res = await token.getUserData();
     if (res != null) {
       var ver = await apiAuthProvider.getDataNoAuth('/api/license');
-      Provider.of<UserModule>(context, listen: false).setSerialNum(ver['serialNumber']);
-      Provider.of<UserModule>(context, listen: false).setList(res);
+      userModule.setSerialNum(ver['serialNumber']);
+      userModule.setList(res);
     } else {
-      Provider.of<UserModule>(context, listen: false).setResetData(true);
+      userModule.setResetData(true);
     }
   }
 
-  switchLang(BuildContext context, code, user) async {
-    showLoading(context);
+  switchLang(code, user) async {
+    UIService.showLoading();
     var locale = 'id';
     if(code is int){
       var vl = U.retValidLang();
@@ -38,30 +40,30 @@ class AccountFunction{
       locale = code;
     }
     try{
-      var p;
+      Map<String, dynamic> p;
       if(await U.isCompatibleWith(VersionKey.multiBahasa)){
         p = {'userId': user['userId'], 'language': 'ID', '_language': code.toUpperCase()};
       } else {
         p = {'userId': user['userId'], 'language': code.toUpperCase()};
       }
-      var res = await apiAuthProvider.patchData('/api/informants/' + user['id'].toString(), p, context);
+      var res = await apiAuthProvider.patchData('/api/informants/${user['id']}', p);
       if (res != null) {
-        closeLoading(context);
-        context.setLocale(Locale(locale));
-        notification.startNotification(context, code: code);
-        // Navigator.of(context).pop();
+        UIService.closeLoading();
+        UIService.setLocale(Locale(locale));
+        // context.setLocale(Locale(locale));
+        notification.startNotification(code: code);
       } else {
-        closeLoading(context);
+        UIService.closeLoading();
       }
     }catch(e){
-      closeLoading(context);
+      UIService.closeLoading();
     }
   }
 
-  Future<bool> setDndStatus(BuildContext context, bool dnd)async{
-    var res = await apiAuthProvider.patchData('/api/informants/${Provider.of<UserModule>(context, listen: false).user()['id']}/setDnd', null, context, params: {'dnd': '${!dnd}'});
+  Future<bool> setDndStatus(String id, bool dnd)async{
+    var res = await apiAuthProvider.patchData('/api/informants/$id/setDnd', null, params: {'dnd': '${!dnd}'});
     if (res != null) {
-      Provider.of<UserModule>(context, listen: false).setDndStatus(dnd);
+      // Provider.of<UserModule>(context, listen: false).setDndStatus(dnd);
       return true;
     }
     return false;
@@ -71,33 +73,37 @@ class AccountFunction{
     showLoading(context, text: '');
     String pid = U.getPidFromUrl(context.router.currentUrl);
 
+    void loggingOut(){
+      Navigator.of(context).pop();
+      UIService.closeLoading();
+      token.logout();
+      context.navigateToPath("/app/$pid/login");
+    }
+
     try {
-      var tkn = await _sharedPreferencesManager.getString(SharedPreferencesManager.keyFcmToken);
-      // var tkn = await U.getFcmToken();
-      if (tkn == null) {
-        Navigator.of(context).pop();
-        closeLoading(context);
-        token.logout();
-        // context.router.removeLast();
-        context.navigateToPath("/app/$pid/login");
-      } else {
+      var tkn = _sharedPreferencesManager.getString(SharedPreferencesManager.keyFcmToken);
+
+      if (tkn == null)
+      {
+        loggingOut();
+      }
+      else
+      {
         var param = {'token': tkn, 'whiteListToken': false};
-        var res = await apiAuthProvider.postData('/api/fcmTokens/remove/', null, param, context);
-        if (res != null && res['success']) {
-          Navigator.of(context).pop();
-          closeLoading(context);
-          token.logout();
-          // context.router.removeLast();
-          context.navigateToPath("/app/$pid/login");
+        var res = await apiAuthProvider.postData('/api/fcmTokens/remove/', null, param);
+
+        if (res != null && res['success'])
+        {
+          loggingOut();
         }
-        else{
-          Future.delayed(const Duration(seconds: 5), () => closeLoading(context));
+        else
+        {
+          Future.delayed(const Duration(seconds: 5), () => UIService.closeLoading());
         }
       }
     } catch(e) {
-      print(e.toString());
-      // context.router.removeLast();
-      context.navigateToPath("/app/$pid/login");
+      debugPrint(e.toString());
+      UIService.navigateNamed("/app/$pid/login");
     }
   }
 }

+ 13 - 11
lib/src/layouts/functions/detail.dart

@@ -7,6 +7,7 @@ import 'package:telnow_mobile_new/src/api/api_auth_provider.dart';
 import 'package:telnow_mobile_new/src/api/jwt_token.dart';
 import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:translator/translator.dart';
 
 class DetailFunction{
@@ -14,8 +15,8 @@ class DetailFunction{
   final JwtToken token = JwtToken();
   final translator = GoogleTranslator();
 
-  Future getMission(BuildContext context, list) async{
-    var userData = await token.getUserData(context);
+  Future getMission(String locale, list) async{
+    var userData = await token.getUserData();
     var data = list;
     if (data['datetimeScheduled'] != null && data['datetimeScheduled'] != '' && data['noteFormat'] == 'DATE') {
       var date = data['datetimeScheduled'];
@@ -27,7 +28,7 @@ class DetailFunction{
     data['noteCompleteTranslate'] = '';
     data['noteCancelTranslate'] = '';
 
-    var locale = context.locale.toString() == 'zh' ? 'zh-cn' : context.locale.toString();
+    if(locale == 'zh') locale = 'zh-cn';
 
     if (data['noteStart'] == 'transferNoteBySystem'){
       data['noteStart'] = 'transferNoteBySystem'.tr();
@@ -76,23 +77,24 @@ class DetailFunction{
 
   cancelRequest(BuildContext context, list, controllerNote){
     var note = controllerNote.text.trim();
-    if (note.length > 128) {
+    if (note.length > 128)
+    {
       showError(context, 'noteLongAlert'.tr());
     }
-    else{
+    else
+    {
       dialogConfirm(context: context, title: 'buttonCancel'.tr(), text: 'confirmCancel'.tr(), actionYes: ()async{
         showLoading(context);
         var data = {
           'note': controllerNote.text.trim(),
         };
 
-        var res = await apiAuthProvider.postData('/api/requestHistories/search/request/cancel/'+list['ticketNo'], null, data, context);
-        if(res != null){
-          closeLoading(context);
+        var res = await apiAuthProvider.postData('/api/requestHistories/search/request/cancel/${list['ticketNo']}', null, data);
+        if(res != null && context.mounted){
+          UIService.closeLoading();
           navigateBack(context, exc: true);
-        }
-        else{
-          closeLoading(context);
+        } else {
+          UIService.closeLoading();
         }
       });
     }

+ 140 - 105
lib/src/layouts/functions/history.dart

@@ -1,178 +1,208 @@
 import 'dart:convert';
 
 import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 import 'package:telnow_mobile_new/src/api/api_auth_provider.dart';
 import 'package:telnow_mobile_new/src/api/jwt_token.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
-import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/layouts/mobile/request_create.dart';
 import 'package:telnow_mobile_new/src/layouts/web/request_create.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
+enum HistoryTab {
+  ongoing,
+  done;
+
+  List get filters {
+    switch(this){
+      case HistoryTab.ongoing:
+        return [
+          {'title': 'stateQueue'.tr(), 'value': 'queue', 'filter': '{"or":[{"f":["currentState","EQ","DIANTRIKAN"]},{"f":["currentState","EQ","DIPROSES"]}]}'},
+          {'title': 'stateDone'.tr(), 'value': 'done', 'filter': '{"f":["currentState","EQ","DIMULAI"]}'},
+          {'title': 'hold'.tr(), 'value': 'hold', 'filter': '{"f":["currentState","EQ","HOLD"]}'}
+        ];
+      case HistoryTab.done:
+        return [
+          {'title': 'stateFinish'.tr(), 'value': 'finish', 'filter': '{"or":[{"f":["currentState","EQ","DISELESAIKAN"]},{"f":["currentState","EQ","TUNTAS"]}]}'},
+          {'title': 'stateCancel'.tr(), 'value': 'cancel', 'filter': '{"f":["currentState","EQ","DIBATALKAN"]}'}
+        ];
+    }
+  }
+}
 class HistoryFunction{
   final JwtToken token = JwtToken();
   final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
   final SharedPreferencesManager sharedPreferencesManager = locator<SharedPreferencesManager>();
 
+  final BuildContext context = UIService.context!;
+  late final HistoryModule historyModule = Provider.of<HistoryModule>(context, listen: false);
+  late final UserModule userModule = Provider.of<UserModule>(context, listen: false);
+
+  List filterList = [
+    {'title': 'stateQueue'.tr(), 'value': 1, 'filter': '{"or":[{"f":["currentState","EQ","DIANTRIKAN"]},{"f":["currentState","EQ","DIPROSES"]}]}'},
+    {'title': 'stateDone'.tr(), 'value': 2, 'filter': '{"f":["currentState","EQ","DIMULAI"]}'},
+    {'title': 'hold'.tr(), 'value': 3, 'filter': '{"f":["currentState","EQ","HOLD"]}'},
+    {'title': 'stateFinish'.tr(), 'value': 4, 'filter': '{"or":[{"f":["currentState","EQ","DISELESAIKAN"]},{"f":["currentState","EQ","TUNTAS"]}]}'},
+    {'title': 'stateCancel'.tr(), 'value': 5, 'filter': '{"f":["currentState","EQ","DIBATALKAN"]}'}
+  ];
+
   List doneList = [
-    {'title': 'stateFinish'.tr(), 'value': 3, 'filter': '{"or":[{"f":["currentState","EQ","DISELESAIKAN"]},{"f":["currentState","EQ","TUNTAS"]}]}'},
-    {'title': 'stateCancel'.tr(), 'value': 4, 'filter': '{"f":["currentState","EQ","DIBATALKAN"]}'},
+    {'title': 'stateFinish'.tr(), 'value': 4, 'filter': '{"or":[{"f":["currentState","EQ","DISELESAIKAN"]},{"f":["currentState","EQ","TUNTAS"]}]}'},
+    {'title': 'stateCancel'.tr(), 'value': 5, 'filter': '{"f":["currentState","EQ","DIBATALKAN"]}'},
   ];
+
   List ongoingList = [
     {'title': 'stateQueue'.tr(), 'value': 1, 'filter': '{"or":[{"f":["currentState","EQ","DIANTRIKAN"]},{"f":["currentState","EQ","DIPROSES"]}]}'},
     {'title': 'stateDone'.tr(), 'value': 2, 'filter': '{"f":["currentState","EQ","DIMULAI"]}'},
     {'title': 'hold'.tr(), 'value': 3, 'filter': '{"f":["currentState","EQ","HOLD"]}'},
   ];
 
-  getActiveForum(BuildContext context, {bool loadAfterAction = false}) async {
+  getActiveForum({bool loadAfterAction = false}) async {
     try {
       String urlForum = '/api/notifications/search/forumNotification';
 
       var val = await CacheMan.readData(urlForum);
       if (val != null) {
-        Provider.of<HistoryModule>(context, listen: false).setActiveForum(val['data']);
+        historyModule.setActiveForum(val['data']);
       }
 
-      var data = await apiAuthProvider.getData(urlForum, null, context);
+      var data = await apiAuthProvider.getData(urlForum, null);
       if (data != null) {
-        Provider.of<HistoryModule>(context, listen: false).setActiveForum(data);
+        historyModule.setActiveForum(data);
         CacheMan.writeData(urlForum, data);
-        getUser(context);
-      }
-      else{
-        getUser(context);
       }
+      // kayaknya ga guna getUser disini. 031125
+      // getUser();
     } catch (e) {
-      print(e.toString());
+      debugPrint(e.toString());
     }
   }
 
-  getUser(BuildContext context) async {
+  getUser() async {
     try {
-      var res = await token.getUserData(context);
+      var res = await token.getUserData();
       if (res != null) {
-        Provider.of<UserModule>(context, listen: false).setList(res);
-        getMission(context);
+        userModule.setList(res);
+        // dipindah di event onRefresh, paralel sama get notifForum. 031125
+        // getMission();
       } else {
-        Provider.of<UserModule>(context, listen: false).setResetData(true);
+        userModule.setResetData(true);
       }
     } catch (e) {
-      Provider.of<UserModule>(context, listen: false).setResetData(true);
+      userModule.setResetData(true);
     }
   }
 
-  getMission(BuildContext context) async {
+  getMission() async {
+    HistoryTab tab = historyModule.activeTab();
+    List filters = tab.filters;
+    String selectedFilter = historyModule.selectedFilter();
+    String state = '${tab.name}::$selectedFilter';
+
     try {
-      if (!Provider.of<HistoryModule>(context, listen: false).isLoadHistory() && !Provider.of<HistoryModule>(context, listen: false).stopLoadHistory()) {
-        Provider.of<HistoryModule>(context, listen: false).setLoadHistory(true);
+      // debugPrint('historyModule.isLoadHistory(state: state): ${historyModule.isLoadHistory(state: state)}');
+      if (!historyModule.isLoadHistory(state: state) && !historyModule.stopLoadHistory()) {
+        historyModule.setLoadHistory(true, state);
         var sort = ['datetimeRequest,desc'];
-        var filter = '';
+        String filter;
 
-        if (Provider.of<HistoryModule>(context, listen: false).selectedFilter() == 0) {
-          if (Provider.of<HistoryModule>(context, listen: false).pressAttention() != 0) {
-            for (int i = 0; i < doneList.length; i++) {
-              if (Provider.of<HistoryModule>(context, listen: false).checkPressAttention(doneList[i]['value'])) {
-                filter = doneList[i]['filter'];
-              }
-            }
-          } else {
+        // If no selected filter button, use default filter;
+        if(selectedFilter == 'none'){
+          if(historyModule.activeTab().name == "done"){
             filter = '{"or":[{"f":["currentState","EQ","DISELESAIKAN"]},{"f":["currentState","EQ","TUNTAS"]},{"f":["currentState","EQ","DIBATALKAN"]}]}';
-          }
-        } else {
-          if((Provider.of<HistoryModule>(context, listen: false).pressAttention() == 0 || Provider.of<HistoryModule>(context, listen: false).pressAttention() == 1) && U.newServerVersion(1709864293) && !U.getInternetStatus()){
-            List pendingList = sharedPreferencesManager.isKeyExists(SharedPreferencesManager.keyPendingData)!?jsonDecode(sharedPreferencesManager.getString(SharedPreferencesManager.keyPendingData)!):[];
-            Provider.of<HistoryModule>(context, listen: false).setPendingData(pendingList);
-          }
-
-          if (Provider.of<HistoryModule>(context, listen: false).pressAttention() != 0) {
-            for (int i = 0; i < ongoingList.length; i++) {
-              if (Provider.of<HistoryModule>(context, listen: false).checkPressAttention(ongoingList[i]['value'])) {
-                filter = ongoingList[i]['filter'];
-              }
-            }
           } else {
             filter = '{"or":[{"f":["currentState","EQ","DIANTRIKAN"]},{"f":["currentState","EQ","DIPROSES"]},{"f":["currentState","EQ","DIMULAI"]}]}';
           }
+        } else {
+          filter = filters.firstWhere((f) => f['value'] == selectedFilter)['filter'];
+        }
+
+        // If no internet connection and selected status is On going and filter button is Queued, display pending data.
+        if(historyModule.activeTab().name == "ongoing" && (selectedFilter == 'none' || selectedFilter == 'queue') && U.newServerVersion(1709864293) && !U.getInternetStatus()){
+          List pendingList = sharedPreferencesManager.isKeyExists(SharedPreferencesManager.keyPendingData)!?jsonDecode(sharedPreferencesManager.getString(SharedPreferencesManager.keyPendingData)!):[];
+          historyModule.setPendingData(pendingList);
         }
 
         String url = '/api/requestHistories/search/myReqHistory';
 
         String key = url + filter;
         var val = await CacheMan.readData(key);
-        if (val != null && Provider.of<HistoryModule>(context, listen: false).page() == 0) {
-          Provider.of<HistoryModule>(context, listen: false).setMisi(val['data']);
+        if (val != null && historyModule.page() == 0) {
+          historyModule.setDataRequests(state, val['data']);
         }
 
-        var misi = await apiAuthProvider.getData(url, {'size': '30', 'page': Provider.of<HistoryModule>(context, listen: false).page().toString(), 'sort': sort, 'filter': filter}, context);
-        if (misi != null) {
+        var mission = await apiAuthProvider.getData(url, {'size': '30', 'page': historyModule.page().toString(), 'sort': sort, 'filter': filter});
+        if (mission != null){
+          // debugPrint("Cek tekan kene!!");
+          historyModule.setLoadHistory(false, state);
           List tempData = [];
-          if (misi.containsKey('_embedded')) {
-            for (int i = 0; i < misi['_embedded']['requestHistories'].length; i++) {
-              var _isFrm = false;
-              var _id = null;
-              var _msg = null;
+          if (mission.containsKey('_embedded')) {
+            // debugPrint('Balikan data: ${mission['_embedded']['requestHistories'].length}');
+            for (int i = 0; i < mission['_embedded']['requestHistories'].length; i++) {
+              var isFrm = false;
+              String id = "";
+              String msg = "";
 
-              Provider.of<HistoryModule>(context, listen: false).activeForum().forEach((el) {
-                if (el['ticketId'] == misi['_embedded']['requestHistories'][i]['ticketNo']) {
-                  _id = el['id'];
-                  _msg = el['desc'];
+              historyModule.activeForum().forEach((el) {
+                if (el['ticketId'] == mission['_embedded']['requestHistories'][i]['ticketNo']) {
+                  id = el['id'];
+                  msg = el['desc'];
                 }
               });
 
-              if (_id != null) _isFrm = true;
-              misi['_embedded']['requestHistories'][i]['_hasForum'] = _isFrm;
-              misi['_embedded']['requestHistories'][i]['_forumId'] = _id;
-              misi['_embedded']['requestHistories'][i]['_forumMsg'] = _msg;
+              if (id != "") isFrm = true;
+              mission['_embedded']['requestHistories'][i]['_hasForum'] = isFrm;
+              mission['_embedded']['requestHistories'][i]['_forumId'] = id;
+              mission['_embedded']['requestHistories'][i]['_forumMsg'] = msg;
 
-              tempData.add(misi['_embedded']['requestHistories'][i]);
+              tempData.add(mission['_embedded']['requestHistories'][i]);
             }
           }
 
-          Provider.of<HistoryModule>(context, listen: false).setLoadHistory(false);
-          if (tempData.length == 0) {
-            Provider.of<HistoryModule>(context, listen: false).setStopLoadHistory(true);
-            Provider.of<HistoryModule>(context, listen: false).clearMisi();
+          // debugPrint('Filter Index: ${indexFilterSelected} & Filter: $filter');
+          // historyModule.setLoadHistory(false);
+          if (tempData.isEmpty) {
+            historyModule.setStopLoadHistory(true);
+            historyModule.clearDataRequests();
             CacheMan.writeData(key, tempData);
           } else {
-            if (Provider.of<HistoryModule>(context, listen: false).page() == 0) {
+            if (historyModule.page() == 0) {
               CacheMan.writeData(key, tempData);
-              Provider.of<HistoryModule>(context, listen: false).clearMisi();
+              historyModule.clearDataRequests();
             }
-            Provider.of<HistoryModule>(context, listen: false).addPage();
+            historyModule.addPage();
           }
-          // print("tempData");
-          // print(tempData);
-          Provider.of<HistoryModule>(context, listen: false).setMisi(tempData);
+          historyModule.setDataRequests(state, tempData);
 
-          if (Provider.of<HistoryModule>(context, listen: false).getMisiLength() >= misi['page']['totalElements']) {
-            Provider.of<HistoryModule>(context, listen: false).setStopLoadHistory(true);
+          if (historyModule.getMisiLength() >= mission['page']['totalElements']) {
+            historyModule.setStopLoadHistory(true);
           }
-        }
-        else{
-          Provider.of<HistoryModule>(context, listen: false).setLoadHistory(false);
-          Provider.of<HistoryModule>(context, listen: false).setStopLoadHistory(true);
+        } else {
+          historyModule.setLoadHistory(false, state);
+          historyModule.setStopLoadHistory(true);
         }
       }
     } catch (e) {
-      print(e.toString());
+      debugPrint(e.toString());
     }
   }
 
-  onRefresh(BuildContext context) async {
-    Provider.of<HistoryModule>(context, listen: false).clearMisi();
-    Provider.of<HistoryModule>(context, listen: false).setPendingData([]);
-    Provider.of<HistoryModule>(context, listen: false).setActiveForum([]);
-    Provider.of<HistoryModule>(context, listen: false).setSelectedIndexes([]);
-    Provider.of<HistoryModule>(context, listen: false).setMultiSelectMode(false);
-    Provider.of<HistoryModule>(context, listen: false).setStopLoadHistory(false);
-    Provider.of<HistoryModule>(context, listen: false).setPage(0);
-    getActiveForum(context);
+  onRefresh() async {
+    historyModule.setCurrentPage();
+    historyModule.clearDataRequests();
+    historyModule.setPendingData([]);
+    historyModule.setActiveForum([]);
+    historyModule.setSelectedIndexes([]);
+    historyModule.setMultiSelectMode(false);
+    historyModule.setStopLoadHistory(false);
+    historyModule.setPage(0);
+    getMission();
+    getActiveForum();
   }
 
   bool askForRate(list) {
@@ -190,12 +220,12 @@ class HistoryFunction{
   }
 
   selectedController(BuildContext context, i) {
-    if (Provider.of<HistoryModule>(context, listen: false).selectedIndexes().contains(i)) {
-      Provider.of<HistoryModule>(context, listen: false).removeSelectedIndexes(i);
+    if (historyModule.selectedIndexes().contains(i)) {
+      historyModule.removeSelectedIndexes(i);
     } else {
-      Provider.of<HistoryModule>(context, listen: false).addSelectedIndexes(i);
+      historyModule.addSelectedIndexes(i);
     }
-    Provider.of<HistoryModule>(context, listen: false).setMultiSelectMode(Provider.of<HistoryModule>(context, listen: false).selectedIndexes().length == 0 ? false : true);
+    historyModule.setMultiSelectMode(historyModule.selectedIndexes().isEmpty ? false : true);
   }
 
   deleteData(BuildContext context) {
@@ -217,12 +247,12 @@ class HistoryFunction{
                 onPressed: () async {
                   Navigator.of(contextt).pop();
                   List<String> params = [];
-                  Provider.of<HistoryModule>(context, listen: false).selectedIndexes().forEach((e) {
-                    params.add(Provider.of<HistoryModule>(context, listen: false).dataMisi()[e]["ticketNo"]);
+                  historyModule.selectedIndexes().forEach((e) {
+                    params.add(historyModule.dataRequests()[e]["ticketNo"]);
                   });
-                  var res = await apiAuthProvider.postData("/api/requestHistories/deleteMyHistory", {"ticketNumbers": params}, null, context);
+                  var res = await apiAuthProvider.postData("/api/requestHistories/deleteMyHistory", {"ticketNumbers": params}, null);
                   if (res != null) {
-                    onRefresh(context);
+                    onRefresh();
                   }
                 },
                 child: Text("buttonYes".tr())),
@@ -232,27 +262,32 @@ class HistoryFunction{
     );
   }
 
-  requestAgainAction(BuildContext context, int i) async{
+  requestAgainAction(int i) async{
+    final isWebView = U.webView(context);
     try {
       String tenant = '';
       String filter = '';
 
-      if ((Provider.of<HistoryModule>(context, listen: false).dataMisi()[i]['requestGroupCode']).split(" ").length != 1) {
-        tenant = ',{"f":["tenantCode","EQ","${(Provider.of<HistoryModule>(context, listen: false).dataMisi()[i]['requestGroupCode']).split(" ").first}"]}';
+      if ((historyModule.dataRequests()[i]['requestGroupCode']).split(" ").length != 1) {
+        tenant = ',{"f":["tenantCode","EQ","${(historyModule.dataRequests()[i]['requestGroupCode']).split(" ").first}"]}';
       }
-      filter = '{"and":[{"f":["code","EQ","${Provider.of<HistoryModule>(context, listen: false).dataMisi()[i]['requestCode']}"]}$tenant]}';
+      filter = '{"and":[{"f":["code","EQ","${historyModule.dataRequests()[i]['requestCode']}"]}$tenant]}';
 
-      var res = await apiAuthProvider.getData('/api/requests/search/customFind', {'filter': filter}, context);
+      var res = await apiAuthProvider.getData('/api/requests/search/customFind', {'filter': filter});
       if (res != null) {
         if (res.containsKey('_embedded')) {
-          navigateTo(context, U.webView(context)?WebReqCreatePage(user: Provider.of<UserModule>(context, listen: false).user(), request: res['_embedded']['requests'][0]):MobReqCreatePage(user: Provider.of<UserModule>(context, listen: false).user(), request: res['_embedded']['requests'][0]));
+          UIService.navigateTo(
+            isWebView ?
+              WebReqCreatePage(user: userModule.user(), request: res['_embedded']['requests'][0]) :
+              MobReqCreatePage(user: userModule.user(), request: res['_embedded']['requests'][0])
+          );
         } else {
-          showError(context, 'reqCodeNotFound'.tr());
+          UIService.showError('reqCodeNotFound'.tr());
         }
       }
     } catch (e) {
-      showError(context, e.toString());
-      print(e);
+      UIService.showError(e.toString());
+      debugPrint(e.toString());
     }
   }
 }

+ 99 - 93
lib/src/layouts/functions/home.dart

@@ -9,37 +9,43 @@ import 'package:telnow_mobile_new/src/utils/C.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 class HomeFunction{
   final SharedPreferencesManager sharedPreferencesManager = locator<SharedPreferencesManager>();
   final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
   final JwtToken token = JwtToken();
+  final BuildContext context = UIService.context!;
+  late final ServiceModule serviceModule = Provider.of<ServiceModule>(context, listen: false);
+  late final UserModule userModule = Provider.of<UserModule>(context, listen: false);
 
   getProfileData(BuildContext context)async{
-    if (!Provider.of<ServiceModule>(context, listen: false).initialized()) {
-      Provider.of<ServiceModule>(context, listen: false).setInitialized(true);
+    if (!serviceModule.initialized()) {
+      serviceModule.setInitialized(true);
 
       try{
         var license = await U.getLicense(showErr: false);
-        Provider.of<UserModule>(context, listen: false).setHouseKeeping(license['houseKeepingFunction']);
-      }catch(e){}
+        userModule.setHouseKeeping(license['houseKeepingFunction']);
+      }catch(e){
+        debugPrint(e.toString());
+      }
 
       try{
-        var res = await token.getUserData(context);
+        var res = await token.getUserData();
         if(res != null){
-          Provider.of<UserModule>(context, listen: false).setList(res);
-          Provider.of<UserModule>(context, listen: false).setProfile(res['_profile']['data']);
-          Provider.of<UserModule>(context, listen: false).setDndStatus(res['dnd']);
+          userModule.setList(res);
+          userModule.setProfile(res['_profile']['data']);
+          userModule.setDndStatus(res['dnd']);
 
           getOptions(context);
         } else {
-          Provider.of<ServiceModule>(context, listen: false).setInitialized(false);
-          Provider.of<UserModule>(context, listen: false).setResetData(true);
+          serviceModule.setInitialized(false);
+          userModule.setResetData(true);
         }
       }catch(e){
         showError(context, e.toString());
-        Provider.of<ServiceModule>(context, listen: false).setInitialized(false);
-        Provider.of<UserModule>(context, listen: false).setResetData(true);
+        serviceModule.setInitialized(false);
+        userModule.setResetData(true);
       }
     }
   }
@@ -50,174 +56,174 @@ class HomeFunction{
 
       var val = await CacheMan.readData(url);
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setScoope(val['data']);
+        serviceModule.setScoope(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, null, context);
+      var res = await apiAuthProvider.getData(url, null);
       if (res != null) {
         CacheMan.writeData(url, res);
-        Provider.of<ServiceModule>(context, listen: false).setScoope(res);
+        serviceModule.setScoope(res);
       }
 
-      if(Provider.of<ServiceModule>(context, listen: false).scoope().isNotEmpty){
+      if(serviceModule.scoope().isNotEmpty){
         //dev-note: cek 1-1, mekanisme load nya. ada yg bikin berat tidak?
-        for (int i = 0; i < Provider.of<ServiceModule>(context, listen: false).scoope().length; i++) {
-          if (sharedPreferencesManager.isKeyExists(SharedPreferencesManager.keyScoope)! && sharedPreferencesManager.getString(SharedPreferencesManager.keyScoope) == Provider.of<ServiceModule>(context, listen: false).scoope()[i]['key']) {
-            Provider.of<ServiceModule>(context, listen: false).setScoopeValue(Provider.of<ServiceModule>(context, listen: false).scoope()[i]['key']);
-            Provider.of<ServiceModule>(context, listen: false).setScoopeName(Provider.of<ServiceModule>(context, listen: false).scoope()[i]['value']);
+        for (int i = 0; i < serviceModule.scoope().length; i++) {
+          if (sharedPreferencesManager.isKeyExists(SharedPreferencesManager.keyScoope)! && sharedPreferencesManager.getString(SharedPreferencesManager.keyScoope) == serviceModule.scoope()[i]['key']) {
+            serviceModule.setScoopeValue(serviceModule.scoope()[i]['key']);
+            serviceModule.setScoopeName(serviceModule.scoope()[i]['value']);
           }
         }
 
-        var profile = Provider.of<UserModule>(context, listen: false).profile();
+        var profile = userModule.profile();
         if(profile['topMenu']['show'] != null && profile['topMenu']['show'] == true) {
           if(U.newServerVersion(1709864293)){
-            getTopMenuNew(context);
+            getTopMenuNew();
           }
           else{
-            getTopMenu(context);
-            getReqGroup(context);
+            getTopMenu();
+            getReqGroup();
           }
         }
         if(profile['specialOffer'] != null && profile['specialOffer']['show'] == true) getSpecialOffer(context);
-        if(profile['frequentlyRequested'] != null && profile['frequentlyRequested']['show'] == true) getFrequentlyRequested(context);
+        if(profile['frequentlyRequested'] != null && profile['frequentlyRequested']['show'] == true) getFrequentlyRequested();
         if(profile['banner'] != null && profile['banner']['show'] == true) getBanner(context);
         if(profile['quickAction'] != null && profile['quickAction']['show'] == true) getQuickAction(context);
         getUnreadMessages(context);
       }
-      Provider.of<ServiceModule>(context, listen: false).setInitialized(false);
+      serviceModule.setInitialized(false);
     } catch (e) {
-      Provider.of<ServiceModule>(context, listen: false).setInitialized(false);
+      serviceModule.setInitialized(false);
       print(e.toString());
     }
   }
 
   getSpecialOffer(BuildContext context) async {
     try {
-      String url = '/api/informants/search/request/specialOffer/'+Provider.of<ServiceModule>(context, listen: false).scoopeValue();
+      String url = '/api/informants/search/request/specialOffer/'+serviceModule.scoopeValue();
 
       var val = await CacheMan.readData(url);
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setSpecialOffer(val['data']);
+        serviceModule.setSpecialOffer(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, null, context);
+      var res = await apiAuthProvider.getData(url, null);
       if (res != null) {
         if (res.length > 0) {
           CacheMan.writeData(url, res);
-          Provider.of<ServiceModule>(context, listen: false).setSpecialOffer(res);
+          serviceModule.setSpecialOffer(res);
         } else {
-          Provider.of<ServiceModule>(context, listen: false).setSpecialOffer([]);
+          serviceModule.setSpecialOffer([]);
         }
       }
     } catch (e) {
       print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setSpecialOffer([]);
+      serviceModule.setSpecialOffer([]);
     }
   }
 
-  getTopMenuNew(BuildContext context) async {
+  getTopMenuNew() async {
     try {
-      var url = '/api/requestGroups/search/dashboard/topMenu/'+Provider.of<ServiceModule>(context, listen: false).scoopeValue();
+      var url = '/api/requestGroups/search/dashboard/topMenu/'+serviceModule.scoopeValue();
 
       var val = await CacheMan.readData('$url?responseServantGroup=${U.servantDisplay()}');
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setTopMenu(val['data']['topMenu']);
-        Provider.of<ServiceModule>(context, listen: false).setReqGroup(val['data']['excludeTopMenu']);
+        serviceModule.setTopMenu(val['data']['topMenu']);
+        serviceModule.setReqGroup(val['data']['excludeTopMenu']);
       }
 
-      var res = await apiAuthProvider.getData(url, {'responseServantGroup': U.servantDisplay().toString()}, context);
+      var res = await apiAuthProvider.getData(url, {'responseServantGroup': U.servantDisplay().toString()});
       if (res != null) {
         CacheMan.writeData('$url?responseServantGroup=${U.servantDisplay()}', res);
-        Provider.of<ServiceModule>(context, listen: false).setTopMenu(res['topMenu']);
-        Provider.of<ServiceModule>(context, listen: false).setReqGroup(res['excludeTopMenu']);
+        serviceModule.setTopMenu(res['topMenu']);
+        serviceModule.setReqGroup(res['excludeTopMenu']);
       }
     } catch (e) {
       print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setTopMenu([]);
-      Provider.of<ServiceModule>(context, listen: false).setReqGroup([]);
+      serviceModule.setTopMenu([]);
+      serviceModule.setReqGroup([]);
     }
   }
 
-  getTopMenu(BuildContext context) async {
+  getTopMenu() async {
     try {
-      var url = '/api/requestGroups/search/topMenu/'+Provider.of<ServiceModule>(context, listen: false).scoopeValue();
+      var url = '/api/requestGroups/search/topMenu/'+serviceModule.scoopeValue();
 
       var val = await CacheMan.readData('$url?responseServantGroup=${U.servantDisplay()}');
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setTopMenu(val['data']);
+        serviceModule.setTopMenu(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, {'responseServantGroup': U.servantDisplay().toString()}, context);
+      var res = await apiAuthProvider.getData(url, {'responseServantGroup': U.servantDisplay().toString()});
       if (res != null) {
         List tempData = [];
         tempData.addAll(res);
 
         if (tempData.length > 0) {
           CacheMan.writeData('$url?responseServantGroup=${U.servantDisplay()}', res);
-          Provider.of<ServiceModule>(context, listen: false).setTopMenu(tempData);
+          serviceModule.setTopMenu(tempData);
         } else {
           CacheMan.writeData('$url?responseServantGroup=${U.servantDisplay()}', []);
-          Provider.of<ServiceModule>(context, listen: false).setTopMenu([]);
+          serviceModule.setTopMenu([]);
         }
       }
       else{
-        Provider.of<ServiceModule>(context, listen: false).setTopMenu([]);
+        serviceModule.setTopMenu([]);
       }
     } catch (e) {
       print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setTopMenu([]);
+      serviceModule.setTopMenu([]);
     }
   }
 
-  getReqGroup(BuildContext context) async {
+  getReqGroup() async {
     try {
-      var url = '/api/requestGroups/search/excludeTopMenu/'+Provider.of<ServiceModule>(context, listen: false).scoopeValue();
+      var url = '/api/requestGroups/search/excludeTopMenu/'+serviceModule.scoopeValue();
 
       var val = await CacheMan.readData('$url?responseServantGroup=${U.servantDisplay()}');
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setReqGroup(val['data']);
+        serviceModule.setReqGroup(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, {'responseServantGroup': U.servantDisplay().toString()}, context);
+      var res = await apiAuthProvider.getData(url, {'responseServantGroup': U.servantDisplay().toString()});
       if (res != null) {
         List tempData = [];
         tempData.addAll(res);
 
         if (tempData.length > 0) {
           CacheMan.writeData('$url?responseServantGroup=${U.servantDisplay()}', res);
-          Provider.of<ServiceModule>(context, listen: false).setReqGroup(tempData);
+          serviceModule.setReqGroup(tempData);
         } else {
           CacheMan.writeData('$url?responseServantGroup=${U.servantDisplay()}', []);
-          Provider.of<ServiceModule>(context, listen: false).setReqGroup([]);
+          serviceModule.setReqGroup([]);
         }
       }
     } catch (e) {
       print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setReqGroup([]);
+      serviceModule.setReqGroup([]);
     }
   }
 
-  getFrequentlyRequested(BuildContext context) async {
+  getFrequentlyRequested() async {
     try {
-      var url = '/api/requestHistories/search/frequently/request/' + Provider.of<ServiceModule>(context, listen: false).scoopeValue();
+      var url = '/api/requestHistories/search/frequently/request/' + serviceModule.scoopeValue();
 
       var val = await CacheMan.readData(url);
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setData(val['data']);
+        serviceModule.setData(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, null, context);
+      var res = await apiAuthProvider.getData(url, null);
       if (res != null) {
         if (res.length > 0) {
           CacheMan.writeData(url, res);
-          Provider.of<ServiceModule>(context, listen: false).setData(res);
+          serviceModule.setData(res);
         } else {
-          Provider.of<ServiceModule>(context, listen: false).setData([]);
+          serviceModule.setData([]);
         }
       }
     } catch (e) {
-      print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setData([]);
+      debugPrint(e.toString());
+      serviceModule.setData([]);
     }
   }
 
@@ -227,78 +233,78 @@ class HomeFunction{
 
       var val = await CacheMan.readData(url);
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setBanner(val['data']);
+        serviceModule.setBanner(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, {'size': '20'}, context);
+      var res = await apiAuthProvider.getData(url, {'size': '20'});
       if (res != null) {
         if (res.length > 0) {
           CacheMan.writeData(url, res);
-          Provider.of<ServiceModule>(context, listen: false).setBanner(res);
+          serviceModule.setBanner(res);
         } else {
-          Provider.of<ServiceModule>(context, listen: false).setBanner([]);
+          serviceModule.setBanner([]);
         }
       }
     } catch (e) {
       print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setBanner([]);
+      serviceModule.setBanner([]);
     }
   }
 
   getQuickAction(BuildContext context) async {
     try {
-      String url = '/api/informants/search/request/quickAction/'+Provider.of<ServiceModule>(context, listen: false).scoopeValue();
+      String url = '/api/informants/search/request/quickAction/'+serviceModule.scoopeValue();
 
       var val = await CacheMan.readData(url);
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setQuickAct(val['data']);
+        serviceModule.setQuickAct(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, null, context);
+      var res = await apiAuthProvider.getData(url, null);
       if (res != null) {
         if (res.length > 0) {
           CacheMan.writeData(url, res);
-          Provider.of<ServiceModule>(context, listen: false).setQuickAct(res);
+          serviceModule.setQuickAct(res);
         } else {
-          Provider.of<ServiceModule>(context, listen: false).setQuickAct([]);
+          serviceModule.setQuickAct([]);
         }
       }
     } catch (e) {
       print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setQuickAct([]);
+      serviceModule.setQuickAct([]);
     }
   }
 
   getContactCenter(BuildContext context) async {
     try{
       var url = "/api/contactCenter/whatsapp" ;
-      var res = await apiAuthProvider.getData(url, null, context);
+      var res = await apiAuthProvider.getData(url, null);
       if (res != null) {
-        Provider.of<ServiceModule>(context, listen: false).setContactCenter(res);
+        serviceModule.setContactCenter(res);
       }
     }catch(e){
-      Provider.of<ServiceModule>(context, listen: false).setContactCenter('');
+      serviceModule.setContactCenter('');
     }
   }
 
   getUnreadMessages(BuildContext context) async {
     try {
-      var res = await apiAuthProvider.getData('/api/messages/search/myMessages', null, context);
+      var res = await apiAuthProvider.getData('/api/messages/search/myMessages', null);
       if (res != null) {
         List tempData = [];
-        Provider.of<ServiceModule>(context, listen: false).setUnreadMessage(false);
+        serviceModule.setUnreadMessage(false);
         if(res.containsKey('_embedded')){
           for (int i = 0; i < res['_embedded']['myMessages'].length; i++) {
-            if(res['_embedded']['myMessages'][i]['lastReadStatus'] == 'UNREAD' && res['_embedded']['myMessages'][i]['userId'] != Provider.of<UserModule>(context, listen: false).user()['userId']){
-              Provider.of<ServiceModule>(context, listen: false).setUnreadMessage(true);
+            if(res['_embedded']['myMessages'][i]['lastReadStatus'] == 'UNREAD' && res['_embedded']['myMessages'][i]['userId'] != userModule.user()['userId']){
+              serviceModule.setUnreadMessage(true);
             }
             tempData.add(res['_embedded']['myMessages'][i]);
           }
         }
 
-        Provider.of<ServiceModule>(context, listen: false).setMessage([]);
+        serviceModule.setMessage([]);
         if (tempData.length > 0) {
-          Provider.of<ServiceModule>(context, listen: false).setMessage(tempData);
+          serviceModule.setMessage(tempData);
         }
       }
     } catch (e) {
@@ -308,11 +314,11 @@ class HomeFunction{
     if(await U.isCompatibleWith(VersionKey.multiBahasa) == false) return;
     try{
       String url = '/api/messages/search/myForum';
-      var res = await apiAuthProvider.getData(url, null, context);
+      var res = await apiAuthProvider.getData(url, null);
       if (res != null) {
         for (int i = 0; i < res.length; i++) {
-          if (res[i]['readStatus'] == 'UNREAD' && res[i]['userId'] != Provider.of<UserModule>(context, listen: false).user()['userId']){
-            Provider.of<ServiceModule>(context, listen: false).setUnreadMessage(true);
+          if (res[i]['readStatus'] == 'UNREAD' && res[i]['userId'] != userModule.user()['userId']){
+            serviceModule.setUnreadMessage(true);
           }
         }
       }
@@ -320,18 +326,18 @@ class HomeFunction{
   }
 
   onRefresh(BuildContext context){
-    var profile = Provider.of<UserModule>(context, listen: false).profile();
+    var profile = userModule.profile();
     if(profile['topMenu']['show'] != null && profile['topMenu']['show'] == true) {
       if(U.newServerVersion(1709864293)){
-        getTopMenuNew(context);
+        getTopMenuNew();
       }
       else{
-        getTopMenu(context);
-        getReqGroup(context);
+        getTopMenu();
+        getReqGroup();
       }
     }
     if(profile['specialOffer'] != null && profile['specialOffer']['show'] == true) getSpecialOffer(context);
-    if(profile['frequentlyRequested'] != null && profile['frequentlyRequested']['show'] == true) getFrequentlyRequested(context);
+    if(profile['frequentlyRequested'] != null && profile['frequentlyRequested']['show'] == true) getFrequentlyRequested();
     if(profile['banner'] != null && profile['banner']['show'] == true) getBanner(context);
     if(profile['quickAction'] != null && profile['quickAction']['show'] == true) getQuickAction(context);
     getUnreadMessages(context);

+ 5 - 8
lib/src/layouts/functions/message.dart

@@ -1,8 +1,6 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/cupertino.dart';
-import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
 import 'package:provider/provider.dart';
 import 'package:telnow_mobile_new/src/api/api_auth_provider.dart';
 import 'package:telnow_mobile_new/src/api/jwt_token.dart';
@@ -14,14 +12,13 @@ import 'package:telnow_mobile_new/src/utils/C.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
-import 'package:url_launcher/url_launcher.dart';
 
 class MessageFunction{
   final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
   final JwtToken token = JwtToken();
 
   getUser(BuildContext context)async{
-    var res = await token.getUserData(context);
+    var res = await token.getUserData();
     if(res != null){
       Provider.of<MessageModule>(context, listen: false).setUser(res);
       getDataMessages(context);
@@ -36,7 +33,7 @@ class MessageFunction{
       Provider.of<MessageModule>(context, listen: false).setData(val['data']);
     }
 
-    var res = await apiAuthProvider.getData(url, null, context);
+    var res = await apiAuthProvider.getData(url, null);
     if (res != null) {
       List tempData = [];
       if (res['_embedded'] != null) {
@@ -61,7 +58,7 @@ class MessageFunction{
       messageModule.setForum(val['data']);
     }
 
-    var res = await apiAuthProvider.getData(url, null, context);
+    var res = await apiAuthProvider.getData(url, null);
     if (res != null) {
       List tempData = [];
       for (int i = 0; i < res.length; i++) {
@@ -77,7 +74,7 @@ class MessageFunction{
   }
 
   setAsRead(context, ticketNo) async {
-    var res = await apiAuthProvider.postData('/api/notifications/readMyForum/$ticketNo', null, null, context);
+    var res = await apiAuthProvider.postData('/api/notifications/readMyForum/$ticketNo', null, null);
     if (res != null) {
     }
   }
@@ -92,7 +89,7 @@ class MessageFunction{
     }
 
     try{
-      var res = await apiAuthProvider.getData(url, {'isPaged': 'true', 'size': '1000', 'sort': 'description'}, context);
+      var res = await apiAuthProvider.getData(url, {'isPaged': 'true', 'size': '1000', 'sort': 'description'});
       if (res != null) {
         List tempData = [];
         if (res['_embedded'] != null) {

+ 7 - 5
lib/src/layouts/functions/request.dart

@@ -19,6 +19,7 @@ import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
 
 import 'package:image/image.dart' as img;
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:uuid/uuid.dart';
 
 class RequestFunction{
@@ -76,7 +77,7 @@ class RequestFunction{
         }
         List tempData = [];
 
-        var res = await apiAuthProvider.getData(url, {'filter': filter, 'nopage': 'true'}, context);
+        var res = await apiAuthProvider.getData(url, {'filter': filter, 'nopage': 'true'});
         if (res != null && res.toString() != "[]") {
           if (res.containsKey('_embedded')) {
             for (int i = 0; i < res['_embedded']['requests'].length; i++) {
@@ -102,7 +103,7 @@ class RequestFunction{
   }
 
   Future getSuggestionLocation(BuildContext context) async{
-    var res = await apiAuthProvider.getData('/api/requestHistories/search/location/suggestion', null, context);
+    var res = await apiAuthProvider.getData('/api/requestHistories/search/location/suggestion', null);
     if(res != null){
       Provider.of<CreateSerModule>(context, listen: false).setSuggestion(res);
     }
@@ -145,7 +146,8 @@ class RequestFunction{
   }
 
   sendRequest(BuildContext context, widget, controllerNote, controllerReferenceNumber, controllerLocation, controllerDateString, currentSliderValue) async{
-    showLoading(context, lottie: kIsWeb && !isCanvasKit ? null : 'Paperplane.json', text: 'sendingRequest'.tr());
+    // showLoading(context, lottie: kIsWeb && !isCanvasKit ? null : 'Paperplane.json', text: 'sendingRequest'.tr());
+    UIService.showLoading(text: 'sendingRequest'.tr(), lottie: kIsWeb && !isCanvasKit ? null : 'Paperplane.json' );
     var data;
     data = {
       'note': controllerNote.text.trim() == '' ? '' : controllerNote.text.trim(),
@@ -167,7 +169,7 @@ class RequestFunction{
       });
       data['images'] = imageEncode;
 
-      var res = await apiAuthProvider.postData('/api/requestHistories/search/request/' + widget.request['type'] + '/' + widget.request['id'].toString() + '/' + widget.request['noteFormat'] + '/submit', null, data, context);
+      var res = await apiAuthProvider.postData('/api/requestHistories/search/request/' + widget.request['type'] + '/' + widget.request['id'].toString() + '/' + widget.request['noteFormat'] + '/submit', null, data);
       if (res != null) {
         Future.delayed(Duration(seconds: 3), (){
           closeLoading(context);
@@ -222,7 +224,7 @@ class RequestFunction{
       data['req'][0]['images'] = imageEncode;
 
       try{
-        var res = await apiAuthProvider.postData('/api/receptionists/send/request', null, data, context);
+        var res = await apiAuthProvider.postData('/api/receptionists/send/request', null, data);
         print("res ##> $res");
         if (res != null) {
           Future.delayed(Duration(seconds: 3), (){

+ 0 - 3
lib/src/layouts/landing/landing_page.dart

@@ -1,6 +1,3 @@
-import 'dart:convert';
-import 'dart:typed_data';
-
 import 'package:auto_route/annotations.dart';
 import 'package:auto_route/auto_route.dart';
 import 'package:easy_localization/easy_localization.dart';

+ 44 - 23
lib/src/layouts/mobile/app_mobile.dart

@@ -6,13 +6,18 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:firebase_messaging/firebase_messaging.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
+import 'package:lazy_load_indexed_stack/lazy_load_indexed_stack.dart';
 import 'package:quick_notify_2/quick_notify.dart';
 import 'package:simple_connection_checker/simple_connection_checker.dart';
 import 'package:telnow_mobile_new/main.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
 import 'package:telnow_mobile_new/src/layouts/components/template.dart';
+import 'package:telnow_mobile_new/src/layouts/mobile/menu_account.dart';
+import 'package:telnow_mobile_new/src/layouts/mobile/menu_history.dart';
+import 'package:telnow_mobile_new/src/layouts/mobile/menu_home.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 class MobAppPage extends StatefulWidget {
   final Widget child;
@@ -59,32 +64,37 @@ class _MobAppPageState extends State<MobAppPage>with TickerProviderStateMixin {
 
   checkConnection() async{
     await U.getServerVersion();
-    // getOtherList();
-    SimpleConnectionChecker _simpleConnectionChecker = SimpleConnectionChecker();
-    subscription = _simpleConnectionChecker.onConnectionChange.listen((connected) {
+    final ctx = UIService.context;
+    var langCode = ctx!.locale.toString();
+
+    void initFirebase(){
+      notification.initFirebaseMessaging(context);
+    }
+
+    void checkPendingRequest(){
+      U.checkPendingRequest(context);
+      if(ModalRoute.of(context)?.isCurrent != true) navigateBack(context);
+    }
+
+    SimpleConnectionChecker simpleConnectionChecker = SimpleConnectionChecker();
+    subscription = simpleConnectionChecker.onConnectionChange.listen((connected) {
       U.setInternetStatus(connected);
-      setState(()=>begin = true);
+      setState(() => begin = true);
       if(connected){
-        setState(()=>isConnected = true);
-        notification.initFirebaseMessaging(context);
+        setState(() => isConnected = true);
+        initFirebase();
         var valLang = U.retValidLang();
-        var langCode = context.locale.toString();
-        var code;
+        int? code;
         if(valLang.indexOf(langCode) > 1){
           code = valLang.indexOf(langCode) - 1;
         }
-        notification.startNotification(context, code: code);
-
-        if(U.newServerVersion(1709864293)){
-          U.checkPendingRequest(context);
-        }
-        if(ModalRoute.of(context)?.isCurrent != true) navigateBack(context);
-      }
-      else{
+        notification.startNotification(code: code);
+        checkPendingRequest();
+      } else {
         Future.delayed(Duration(seconds: 4), (){
           if(!U.getInternetStatus()){
-            handlingError(context, 0);
-            setState(()=>isConnected = false);
+            UIService.handlingError(ErrorType.noInternet);
+            setState(() => isConnected = false);
           }
         });
       }
@@ -119,8 +129,8 @@ class _MobAppPageState extends State<MobAppPage>with TickerProviderStateMixin {
 //---------------------------------------------------------------------------------------------------------------
 
 class MobMenuTemplate extends StatefulWidget {
-  final Widget child;
-  const MobMenuTemplate({required this.child, Key? key}) : super(key: key);
+  // final Widget child;
+  const MobMenuTemplate({super.key});
 
   @override
   State<MobMenuTemplate> createState() => _MobMenuTemplateState();
@@ -151,6 +161,17 @@ class _MobMenuTemplateState extends State<MobMenuTemplate> {
     });
   }
 
+  indexedStack(index){
+    return LazyLoadIndexedStack(
+      index: index,
+      children: [
+        MobHomePage(),
+        MobHistoryPage(),
+        MobAccountPage()
+      ],
+    );
+  }
+
   @override
   void initState() {
     getUser();
@@ -170,10 +191,10 @@ class _MobMenuTemplateState extends State<MobMenuTemplate> {
 
     return Scaffold(
       backgroundColor: backgroundColor,
-      body: !kIsWeb?DoubleBackToExit(
+      body: !kIsWeb ? DoubleBackToExit(
         snackBarMessage: 'pressAgain'.tr(),
-        child: widget.child,
-      ):widget.child,
+        child: indexedStack(index)
+      ) : indexedStack(index),
       bottomNavigationBar: BottomNavigationBar(
         backgroundColor: Colors.white,
         selectedItemColor: textColor,

+ 3 - 3
lib/src/layouts/mobile/banner_detail.dart

@@ -9,9 +9,9 @@ import 'package:url_launcher/url_launcher.dart';
 import 'package:youtube_player_iframe/youtube_player_iframe.dart';
 
 class MobBannerDetailPage extends StatefulWidget {
-  Map<String, dynamic> user;
-  Map<String, dynamic> data;
-  MobBannerDetailPage({required this.user, required this.data, super.key});
+  final Map<String, dynamic> user;
+  final Map<String, dynamic> data;
+  const MobBannerDetailPage({required this.user, required this.data, super.key});
 
   @override
   State<MobBannerDetailPage> createState() => _MobBannerDetailPageState();

+ 12 - 13
lib/src/layouts/mobile/history_detail.dart

@@ -8,12 +8,13 @@ import 'package:telnow_mobile_new/src/layouts/mobile/history_forum.dart';
 import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:timelines_plus/timelines_plus.dart';
 import 'package:url_launcher/url_launcher.dart';
 
 class MobHistoryDetailPage extends StatefulWidget {
-  int index;
-  MobHistoryDetailPage({required this.index, super.key});
+  final int index;
+  const MobHistoryDetailPage({required this.index, super.key});
 
   @override
   State<MobHistoryDetailPage> createState() => _MobHistoryDetailPageState();
@@ -43,7 +44,7 @@ class _MobHistoryDetailPageState extends State<MobHistoryDetailPage> {
   }
 
   getData() async{
-    var res = await detFunc.getMission(context, Provider.of<HistoryModule>(context, listen: false).dataMisi()[widget.index]);
+    var res = await detFunc.getMission(UIService.getLocale(), Provider.of<HistoryModule>(context, listen: false).dataRequests()[widget.index]);
     setState((){
       user = res['user'];
       list = res['list'];
@@ -327,7 +328,7 @@ class _MobHistoryDetailPageState extends State<MobHistoryDetailPage> {
                 ),
               ):_timeLimit ? showButton(context) : loadingTemplate(() {if(mounted) setState(()=>_timeLimit=true);},),
             ),
-            divider(),
+            divider(color: primaryColor, opacity: 1.0, thickness: 2.0),
             list.isEmpty || list['autoResponse'] || list['currentState'] == 'DISELESAIKAN' || list['currentState'] == 'TUNTAS' || list['currentState'] == 'DIBATALKAN' ? Container() : Container(
               padding: EdgeInsets.symmetric(vertical: 10, horizontal: 16), color: Colors.white,
               child: list['currentState'] == 'DIMULAI' && list['_canNotCancel'] ? Text('canNotCancel2'.tr(), style: TextStyle(color: textColor, fontSize: 14), textAlign: TextAlign.center) : Container(
@@ -482,15 +483,13 @@ class _MobHistoryDetailPageState extends State<MobHistoryDetailPage> {
               mainAxisAlignment: MainAxisAlignment.spaceBetween,
               children: [
                 Text('state'.tr(), style: TextStyle(fontSize: 14, color: textColor)),
-                Container(
-                  child: Row(
-                    mainAxisAlignment: MainAxisAlignment.end,
-                    children: [
-                      Image(image: AssetImage('assets/image/general/Watch.png'), width: 20, height: 20),
-                      SizedBox(width: 5),
-                      Text('hold'.tr(), style: TextStyle(fontSize: 14, color: primaryColor)),
-                    ],
-                  ),
+                Row(
+                  mainAxisAlignment: MainAxisAlignment.end,
+                  children: [
+                    Image(image: AssetImage('assets/image/general/Watch.png'), width: 20, height: 20),
+                    SizedBox(width: 5),
+                    Text('hold'.tr(), style: TextStyle(fontSize: 14, color: primaryColor)),
+                  ],
                 )
               ],
             ),

+ 7 - 7
lib/src/layouts/mobile/history_detail_pending.dart

@@ -9,8 +9,8 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:timelines_plus/timelines_plus.dart';
 
 class MobHistoryDetailPendingPage extends StatefulWidget {
-  Map<String, dynamic> data;
-  MobHistoryDetailPendingPage({required this.data, super.key});
+  final Map<String, dynamic> data;
+  const MobHistoryDetailPendingPage({required this.data, super.key});
 
   @override
   State<MobHistoryDetailPendingPage> createState() => _MobHistoryDetailPendingPageState();
@@ -59,7 +59,7 @@ class _MobHistoryDetailPendingPageState extends State<MobHistoryDetailPendingPag
                             SizedBox(height: 16),
                             divider(opacity: 0.05),
                             SizedBox(height: 16),
-                            attachment_new(widget.data['imageList']),
+                            attachmentNew(widget.data['imageList']),
                             SizedBox(height: 16),
                             textVertical('note'.tr(), widget.data['note'] != '' ? widget.data['note'] : '-'),
                             SizedBox(height: 24),
@@ -136,15 +136,15 @@ class _MobHistoryDetailPendingPageState extends State<MobHistoryDetailPendingPag
     );
   }
 
-  Widget attachment_new(List images){
+  Widget attachmentNew(List images){
     var imageWidth = ((U.bodyWidth(context)-32)/5)-5;
 
     List imageList = [];
-    images.forEach((element) {
+    for (var element in images) {
       imageList.add(base64Decode(element));
-    });
+    }
 
-    return imageList.length > 0 ? Column(
+    return imageList.isNotEmpty ? Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
         Text('image'.tr(), style: TextStyle(color: textColor)),

+ 25 - 23
lib/src/layouts/mobile/history_forum.dart

@@ -39,7 +39,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
 
   String? idChat;
   String? imagePath;
-  TextEditingController controllerPesan = new TextEditingController();
+  TextEditingController controllerMessage = TextEditingController();
   ScrollController scrollController = ScrollController();
   List messageData = [];
   List deletedMessage = [];
@@ -75,9 +75,9 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
 
 
   getData() async {
-    idChat = U.decodeBase64Url(_sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessCode)!) + '-' + widget.data['ticketNo'];
+    idChat = '${U.decodeBase64Url(_sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessCode)!)}-${widget.data['ticketNo']}';
     if (!kIsWeb) imagePath = await token.getPath() + '/TelNow/Images/';
-    var res = await token.getUserData(context);
+    var res = await token.getUserData();
     if (res != null) {
       username = res['userId'];
       getMessage();
@@ -86,7 +86,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
   }
 
   setAsRead(ticketNo) async {
-    var res = await apiAuthProvider.postData('/api/notifications/readMyForum/$ticketNo', null, null, context);
+    var res = await apiAuthProvider.postData('/api/notifications/readMyForum/$ticketNo', null, null);
     if (res != null) {
     }
   }
@@ -95,7 +95,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
     if (!isLoad && !stopLoad) {
       setState(() => isLoad = true);
       setAsRead(widget.data['ticketNo']);
-      var mymess = await apiAuthProvider.getData('/api/messages/search/myMessages/' + idChat!, {'isPaged': 'true', 'page': page.toString(), 'size': '20'}, context);
+      var mymess = await apiAuthProvider.getData('/api/messages/search/myMessages/${idChat!}', {'isPaged': 'true', 'page': page.toString(), 'size': '20'});
       if (mymess.containsKey('_embedded')) {
         List data = mymess['_embedded']['myMessages'];
         for (int i = 0; i < data.length; i++) {
@@ -145,7 +145,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
           var translate = await translator.translate(element['msg'] ?? '', to: loc);
           element['translate'] = translate.text;
         }catch(e){
-          print("Error translate: ${e.toString()}");
+          debugPrint("Error translate: ${e.toString()}");
         }
       }
     });
@@ -160,7 +160,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
           if (result.type == DocumentChangeType.added && isAfterLoad) {
             if (
             (
-                (username == data!['from']['user'] && data!['senderType'] != 'SERVANT') &&
+                (username == data!['from']['user'] && data['senderType'] != 'SERVANT') &&
                     messageData.where((element) => element['uniqueId'] == data['uniqueId']).isNotEmpty
             ) ||
                 (username != data['from']['user'] && data['readStatus'] == 'DELETED')
@@ -175,7 +175,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                   var translate = await translator.translate(data['msg']??'', to: locale);
                   data['translate'] = translate.text;
                 }catch(e){
-                  print("Error translate: ${e.toString()}");
+                  debugPrint("Error translate: ${e.toString()}");
                 }
               }
               bool alreadyExists = messageData.any((element) => element['uniqueId'] == data['uniqueId']);
@@ -185,7 +185,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
             }
             downloadImage(data);
           } else if (result.type == DocumentChangeType.modified && isAfterLoad) {
-            if (messageData.where((element) => element['uniqueId'] == data!['uniqueId']).length > 0) {
+            if (messageData.where((element) => element['uniqueId'] == data!['uniqueId']).isNotEmpty) {
               int index = messageData.indexWhere((element) => element['uniqueId'] == data!['uniqueId']);
               messageData[index]['read'] = data!['read'];
               messageData[index]['readStatus'] = data['readStatus'];
@@ -194,7 +194,9 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
           if(mounted) setState(() {});
         });
       });
-    }catch(e){}
+    }catch(e){
+      debugPrint(e.toString());
+    }
 
     if(mounted) {
       setState(() {
@@ -230,7 +232,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
   sendMessage(data) async {
     setState(() {
       isSend = false;
-      controllerPesan.clear();
+      controllerMessage.clear();
       messageData.add({
         'msg': data['text'],
         'datetime': DateTime.now().toString(),
@@ -245,7 +247,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
       scrollBottom = true;
     });
 
-    var res = await apiAuthProvider.postData('/api/messages', null, data, context);
+    var res = await apiAuthProvider.postData('/api/messages', null, data);
     if (res != null) {
       int index = messageData.indexWhere((element) => element['uniqueId'] == data['uniqueId']);
       setState(() {
@@ -286,7 +288,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
   @override
   Widget build(BuildContext context) {
     WidgetsBinding.instance.addPostFrameCallback((_) {
-      if (messageData.length > 0) {
+      if (messageData.isNotEmpty) {
         if (scrollController.position.maxScrollExtent > 0 && !isReverse) {
           setState(() => isReverse = true);
         }
@@ -321,7 +323,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
             child: Container(
               alignment: Alignment.topCenter,
               width: U.bodyWidth(context),
-              child: messageData.length == 0 && !isAfterLoad ? loadingTemplateNoVoid() : SingleChildScrollView(
+              child: messageData.isEmpty && !isAfterLoad ? loadingTemplateNoVoid() : SingleChildScrollView(
                 controller: scrollController,
                 reverse: isReverse,
                 padding: EdgeInsets.fromLTRB(10, 10, 10, 7),
@@ -413,7 +415,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                                         }
                                       },
                                       onLongPress: (link) {
-                                        Clipboard.setData(new ClipboardData(text: link)).then((value){
+                                        Clipboard.setData(ClipboardData(text: link)).then((value){
                                           showSuccess('link_copied'.tr(), context);
                                         });
                                       },
@@ -444,11 +446,11 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                           var expander = Expanded(
                               flex: 8,
                               child: Column(
+                                crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
                                 children: [
                                   bubleChat(widget, timeBubble, isNip, isMe),
                                   SizedBox(height: 5),
                                 ],
-                                crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
                               )
                           );
 
@@ -492,7 +494,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                     child: SizedBox(
                       width: double.infinity,
                       child: TextField(
-                        controller: controllerPesan,
+                        controller: controllerMessage,
                         maxLength: 256,
                         style: const TextStyle(fontSize: 14, color: Colors.black),
                         keyboardType: TextInputType.multiline,
@@ -500,7 +502,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                         maxLines: 5,
                         decoration: InputDecoration(
                             counterText: '',
-                            hintText: 'writeMessage'.tr()+'..',
+                            hintText: '${'writeMessage'.tr()}..',
                             hintStyle: TextStyle(color: textColor.withValues(alpha: 0.5), fontSize: 14),
                             filled: true,
                             fillColor: backgroundColor,
@@ -515,7 +517,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                                   "userId": username,
                                   "recipientId": "#forum",
                                   "senderType": "INFORMANT",
-                                  "text": controllerPesan.text.trim(),
+                                  "text": controllerMessage.text.trim(),
                                   "chatId": idChat,
                                   "images": null
                                 };
@@ -611,21 +613,21 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                   GestureDetector(
                     child: Container(
                       padding: EdgeInsets.all(12),
-                      child: Image.asset('assets/image/icon/Send.png', width: 25, height: 25),
                       decoration: BoxDecoration(
                           color: primaryColor,
                           borderRadius: BorderRadius.all(Radius.circular(50))
                       ),
+                      child: Image.asset('assets/image/icon/Send.png', width: 25, height: 25),
                     ),
                     onTap: (){
-                      if(controllerPesan.text.isNotEmpty){
+                      if(controllerMessage.text.isNotEmpty){
                         var uuid = Uuid().v1().replaceAll('-', '');
                         var data = {
                           "uniqueId": uuid,
                           "userId": username,
                           "recipientId": "#forum",
                           "senderType": "INFORMANT",
-                          "text": controllerPesan.text.trim(),
+                          "text": controllerMessage.text.trim(),
                           "chatId": idChat,
                           "images": null
                         };
@@ -661,7 +663,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
         });
       }
     } catch (e) {
-      print(e.toString());
+      debugPrint(e.toString());
     }
   }
 

+ 5 - 5
lib/src/layouts/mobile/history_rating.dart

@@ -17,7 +17,7 @@ class _MobHistoryRatingPageState extends State<MobHistoryRatingPage> {
   String description = '';
   String ratingAspect = '';
   List aspectList = [];
-  TextEditingController controllerOptOther = new TextEditingController();
+  TextEditingController controllerOptOther = TextEditingController();
 
   var rating = [
     {'key': 1, 'image': "assets/image/icon/very_dissatisfied.png", 'label': 'disatisfied'.tr()},
@@ -29,7 +29,7 @@ class _MobHistoryRatingPageState extends State<MobHistoryRatingPage> {
 
   getAspectList(){
     String locale = context.locale.toString();
-    List optionList = widget.data['_ratingAspect'+locale[0].toUpperCase()+locale[1]] != null && widget.data['_ratingAspect'+locale[0].toUpperCase()+locale[1]].trim() != '' ? widget.data['_ratingAspect'+locale[0].toUpperCase()+locale[1]].split(';') : [];
+    List optionList = widget.data['_ratingAspect${locale[0].toUpperCase()}${locale[1]}'] != null && widget.data['_ratingAspect${locale[0].toUpperCase()}${locale[1]}'].trim() != '' ? widget.data['_ratingAspect${locale[0].toUpperCase()}${locale[1]}'].split(';') : [];
     if(optionList.isNotEmpty){
       optionList.add('OTHER_OPTION');
     }
@@ -109,7 +109,7 @@ class _MobHistoryRatingPageState extends State<MobHistoryRatingPage> {
                       child: Column(
                         crossAxisAlignment: CrossAxisAlignment.start,
                         children: [
-                          Text('whatMakesYou'.tr()+' '+description+'?', style: TextStyle(fontSize: 16)),
+                          Text('${'whatMakesYou'.tr()} $description?', style: TextStyle(fontSize: 16)),
                           SizedBox(height: 12),
                           Wrap(
                             runSpacing: 10, spacing: 10,
@@ -117,12 +117,12 @@ class _MobHistoryRatingPageState extends State<MobHistoryRatingPage> {
                               return GestureDetector(
                                 child: Container(
                                   padding: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
-                                  child: Text(aspectList[i]=='OTHER_OPTION'?'letMeWrite'.tr():aspectList[i], style: TextStyle(color: textColor)),
                                   decoration: BoxDecoration(
                                     color: aspectList[i] == ratingAspect ? primaryColor.withValues(alpha: 0.3) : Colors.white,
                                     border: Border.all(color: aspectList[i] == ratingAspect ? primaryColor : textColor),
                                     borderRadius: BorderRadius.all(Radius.circular(50))
                                   ),
+                                  child: Text(aspectList[i]=='OTHER_OPTION'?'letMeWrite'.tr():aspectList[i], style: TextStyle(color: textColor)),
                                 ),
                                 onTap: (){
                                   setState(() {
@@ -156,7 +156,7 @@ class _MobHistoryRatingPageState extends State<MobHistoryRatingPage> {
                             data['aspect'] = controllerOptOther.text;
                           }
 
-                          var res = await ApiAuthProvider() .postData('/api/requestHistories/rateSatisfy', null, data, context);
+                          var res = await ApiAuthProvider() .postData('/api/requestHistories/rateSatisfy', null, data);
                           closeLoading(context);
                           if (res != null) {
                             Navigator.of(context).pop(tempRating);

+ 42 - 40
lib/src/layouts/mobile/menu_account.dart

@@ -12,6 +12,7 @@ import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
 import 'package:permission_handler/permission_handler.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 import '../../utils/C.dart';
 
@@ -24,7 +25,7 @@ class MobAccountPage extends StatefulWidget {
 
 class _MobAccountPageState extends State<MobAccountPage> {
   final AccountFunction accFunc = AccountFunction();
-  
+
   bool isDeniedNotifPermission = false;
   bool serDis = false;
   bool dnd = false;
@@ -35,7 +36,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
 
   @override
   void initState() {
-    Provider.of<UserModule>(context, listen: false).reset();
+    // Provider.of<UserModule>(context, listen: false).reset();
 
     accFunc.getUser(context);
     setToggle();
@@ -71,6 +72,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
 
   @override
   Widget build(BuildContext context) {
+    final UserModule userModule = Provider.of<UserModule>(context);
     codeOflang = {
       "id": 'bahasa'.tr(),
       "en": 'english'.tr(),
@@ -83,7 +85,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
       "hi": 'hindi'.tr(),
       "nl": 'dutch'.tr()
     };
-    return Provider.of<UserModule>(context).user().isNotEmpty ? Scaffold(
+    return userModule.user().isNotEmpty ? Scaffold(
       backgroundColor: backgroundColor,
       appBar: AppBar(
         elevation: 0,
@@ -108,12 +110,12 @@ class _MobAccountPageState extends State<MobAccountPage> {
                       child: Row(
                         children: [
                           CircleAvatar(
-                            child: Text(Provider.of<UserModule>(context).user()['name'][0].toString().toUpperCase(), style: TextStyle(color: Colors.white)),
                             backgroundColor: primaryColor,
                             radius: 25,
+                            child: Text(userModule.user()['name'][0].toString().toUpperCase(), style: TextStyle(color: Colors.white)),
                           ),
                           SizedBox(width: 16),
-                          Expanded(child: Text(Provider.of<UserModule>(context).user()['name'], style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: textColor), maxLines: 2, overflow: TextOverflow.ellipsis))
+                          Expanded(child: Text(userModule.user()['name'], style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: textColor), maxLines: 2, overflow: TextOverflow.ellipsis))
                         ],
                       ),
                     ),
@@ -125,16 +127,16 @@ class _MobAccountPageState extends State<MobAccountPage> {
                         children: [
                           Text('info_label'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
                           SizedBox(height: 20),
-                          textHorizontal('userId'.tr(), Provider.of<UserModule>(context).user()['relatedTo'] ?? Provider.of<UserModule>(context).user()['userId'], size: 16, opacity: 0.75),
+                          textHorizontal('userId'.tr(), userModule.user()['relatedTo'] ?? userModule.user()['userId'], size: 16, opacity: 0.75),
                           SizedBox(height: 12),
-                          textHorizontal('location'.tr(), Provider.of<UserModule>(context).user()['location'] ?? '-', size: 16, opacity: 0.75),
+                          textHorizontal('location'.tr(), userModule.user()['location'] ?? '-', size: 16, opacity: 0.75),
                           SizedBox(height: 12),
-                          textHorizontal('servantGroup'.tr(), Provider.of<UserModule>(context).user()['requestTypeListDescriptionMobile'] != null ? Provider.of<UserModule>(context).user()['requestTypeListDescriptionMobile'].toLowerCase() == 'semua' ? 'all'.tr() : Provider.of<UserModule>(context).user()['requestTypeListDescriptionMobile'] : '-', size: 16, opacity: 0.75),
+                          textHorizontal('servantGroup'.tr(), userModule.user()['requestTypeListDescriptionMobile'] != null ? userModule.user()['requestTypeListDescriptionMobile'].toLowerCase() == 'semua' ? 'all'.tr() : userModule.user()['requestTypeListDescriptionMobile'] : '-', size: 16, opacity: 0.75),
                         ],
                       ),
                     ),
-                    Provider.of<UserModule>(context).houseKeeping()?separator():Container(),
-                    Provider.of<UserModule>(context).houseKeeping()?Container(
+                    userModule.houseKeeping()?separator():Container(),
+                    userModule.houseKeeping()?Container(
                       padding: EdgeInsets.all(16),
                       child: Column(
                         crossAxisAlignment: CrossAxisAlignment.start,
@@ -147,35 +149,35 @@ class _MobAccountPageState extends State<MobAccountPage> {
                               Text(dnd?'active_dnd'.tr():'inactive_dnd'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
                               SizedBox(width: 5),
                               GestureDetector(
+                                onTap: userModule.user()['checkedIn']?(){
+                                  dialogConfirm(context: context, title: 'set_dnd_status'.tr(), text: dnd?'msg_change_inactive'.tr():'msg_change_active'.tr(), actionYes: ()async{
+                                    bool res = await accFunc.setDndStatus(userModule.user()['id'], dnd);
+                                    if(res){
+                                      setState(() => dnd = !dnd);
+                                    }
+                                  });
+                                }:null,
                                 child: Container(
-                                  decoration: BoxDecoration(border: Border.all(color: Provider.of<UserModule>(context).user()['checkedIn']?primaryColor:Colors.black38, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
+                                  decoration: BoxDecoration(border: Border.all(color: userModule.user()['checkedIn']?primaryColor:Colors.black38, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
                                   child: Row(
                                     children: [
                                       !dnd?Container(
-                                        width: 20, height: 20, decoration: BoxDecoration(color: Provider.of<UserModule>(context).user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                      ):Container(width: 20, height: 20),
+                                        width: 20, height: 20, decoration: BoxDecoration(color: userModule.user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                      ):SizedBox(width: 20, height: 20),
                                       dnd?Container(
-                                        width: 20, height: 20, decoration: BoxDecoration(color: Provider.of<UserModule>(context).user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                      ):Container(width: 20, height: 20),
+                                        width: 20, height: 20, decoration: BoxDecoration(color: userModule.user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                      ):SizedBox(width: 20, height: 20),
                                     ],
                                   ),
                                 ),
-                                onTap: Provider.of<UserModule>(context).user()['checkedIn']?(){
-                                  dialogConfirm(context: context, title: 'set_dnd_status'.tr(), text: dnd?'msg_change_inactive'.tr():'msg_change_active'.tr(), actionYes: ()async{
-                                    bool res = await accFunc.setDndStatus(context, dnd);
-                                    if(res){
-                                      setState(() => dnd = !dnd);
-                                    }
-                                  });
-                                }:null,
                               ),
                             ],
                           )
                         ],
                       ),
                     ):Container(),
-                    !Provider.of<UserModule>(context).user()['_profile']['isRoom']?separator():Container(),
-                    !Provider.of<UserModule>(context).user()['_profile']['isRoom']?Container(
+                    !userModule.user()['_profile']['isRoom']?separator():Container(),
+                    !userModule.user()['_profile']['isRoom']?Container(
                       padding: EdgeInsets.all(16),
                       child: Column(
                         crossAxisAlignment: CrossAxisAlignment.start,
@@ -193,10 +195,10 @@ class _MobAccountPageState extends State<MobAccountPage> {
                                     children: [
                                       !serDis?Container(
                                         width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                      ):Container(width: 20, height: 20),
+                                      ):SizedBox(width: 20, height: 20),
                                       serDis?Container(
                                         width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                      ):Container(width: 20, height: 20),
+                                      ):SizedBox(width: 20, height: 20),
                                     ],
                                   ),
                                 ),
@@ -262,10 +264,10 @@ class _MobAccountPageState extends State<MobAccountPage> {
                                       children: [
                                         !autoTranslate?Container(
                                           width: 20, height: 20, decoration: BoxDecoration(color: Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                        ):Container(width: 20, height: 20),
+                                        ):SizedBox(width: 20, height: 20),
                                         autoTranslate?Container(
                                           width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                        ):Container(width: 20, height: 20),
+                                        ):SizedBox(width: 20, height: 20),
                                       ],
                                     ),
                                   ),
@@ -301,9 +303,9 @@ class _MobAccountPageState extends State<MobAccountPage> {
                                 ],
                               ),
                             ),
-                            onTap: ()=>Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: MobPasswordPage(user: Provider.of<UserModule>(context, listen: false).user()))).then((value) {
-                              value??false;
-                              if (value) showSuccess(context, 'messagePassChanged'.tr());
+                            onTap: ()=> Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: MobPasswordPage(user: Provider.of<UserModule>(context, listen: false).user()))).then((value) {
+                              value ?? false;
+                              if (value) UIService.showSuccess('messagePassChanged'.tr());
                             }),
                           ),
                           divider(),
@@ -364,7 +366,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
                       onTap: (){
                         var size = MediaQuery.of(context).size.width - (MediaQuery.of(context).size.width/3);
                         var pid = U.getPidFromUrl(context.router.currentUrl);
-                        var qr_data = 'https://telnow.telmessenger.com/#/app/${pid}%23${U.getBaseUrl()}';
+                        var qrData = 'https://telnow.telmessenger.com/#/app/$pid%23${U.getBaseUrl()}';
                         showDialog(
                           context: context,
                           builder: (BuildContext context) {
@@ -378,7 +380,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
                                     width: size,
                                     height: size,
                                     child: QrImageView(
-                                      data: qr_data,
+                                      data: qrData,
                                       version: QrVersions.auto,
                                       gapless: true,
                                     ),
@@ -427,7 +429,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
           )
         ],
       ),
-    ) : Provider.of<UserModule>(context).resetData() ? RefreshPage(() {
+    ) : userModule.resetData() ? RefreshPage(() {
       Provider.of<UserModule>(context, listen: false).setResetData(false);
       accFunc.getUser(context);
     }) : _timeLimit ? showButton(context) : loadingTemplate(() {if(mounted) setState(()=>_timeLimit=true);},);
@@ -466,7 +468,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
             title: Text('bahasa'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()=='id'?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != 'id' ? () async {
-              accFunc.switchLang(context, 'id', user);
+              accFunc.switchLang('id', user);
               Navigator.of(context).pop();
             } : null,
           ),
@@ -474,7 +476,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
             title: Text('english'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()=='en'?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != 'en' ? () async {
-              accFunc.switchLang(context, 'en', user);
+              accFunc.switchLang('en', user);
               Navigator.of(context).pop();
             } : null,
           ),
@@ -483,7 +485,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
             title: Text(codeOflang[lang[2]], style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()==lang[2]?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != lang[2] ? () async {
-              accFunc.switchLang(context, 1, user);
+              accFunc.switchLang(1, user);
               Navigator.of(context).pop();
             } : null,
           ) : Container(),
@@ -491,7 +493,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
             title: Text(codeOflang[lang[3]], style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()==lang[3]?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != lang[3] ? () async {
-              accFunc.switchLang(context, 2, user);
+              accFunc.switchLang(2, user);
               Navigator.of(context).pop();
             } : null,
           ) : Container(),
@@ -499,7 +501,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
             title: Text(codeOflang[lang[4]], style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()==lang[4]?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != lang[4] ? () async {
-              accFunc.switchLang(context, 3, user);
+              accFunc.switchLang(3, user);
               Navigator.of(context).pop();
             } : null,
           ) : Container(),

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 206 - 171
lib/src/layouts/mobile/menu_history.dart


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 98 - 78
lib/src/layouts/mobile/menu_home.dart


+ 1 - 1
lib/src/layouts/mobile/message_broadcast.dart

@@ -60,7 +60,7 @@ class _MobMessageBroadcastPageState extends State<MobMessageBroadcastPage> {
     setState(() {
       _isSending = true;
     });
-    var res = await _apiAuthProvider.postData('/api/messages/broadcast', params, data, context);
+    var res = await _apiAuthProvider.postData('/api/messages/broadcast', params, data);
     // print("res : ${res.toString()}");
     if (res != null && mounted) {
       Navigator.pop(context, 'pop');

+ 9 - 8
lib/src/layouts/mobile/message_chat.dart

@@ -59,7 +59,7 @@ class _MobMessageChatPageState extends State<MobMessageChatPage> {
 
   setReadStatus() async {
     if (!widget.askBroadcast) {
-      await apiAuthProvider.patchData('/api/messages/' + widget.messageId, {'readStatus': 'READ'}, context);
+      await apiAuthProvider.patchData('/api/messages/' + widget.messageId, {'readStatus': 'READ'});
     }
   }
 
@@ -83,7 +83,7 @@ class _MobMessageChatPageState extends State<MobMessageChatPage> {
     }
     if (!isLoad && !stopLoad) {
       setState(() => isLoad = true);
-      var mymess = await apiAuthProvider.getData('/api/messages/search/$url', {'isPaged': 'true', 'page': page.toString(), 'size': '20', 'filter': filter, 'tenant': widget.tenant}, context);
+      var mymess = await apiAuthProvider.getData('/api/messages/search/$url', {'isPaged': 'true', 'page': page.toString(), 'size': '20', 'filter': filter, 'tenant': widget.tenant});
       if (mymess.containsKey('_embedded') && mounted) {
         List data = mymess['_embedded']['myMessages'];
         for (int i = 0; i < data.length; i++) {
@@ -184,6 +184,7 @@ class _MobMessageChatPageState extends State<MobMessageChatPage> {
   }
 
   deleteCollection() {
+    debugPrint("popped");
     FirebaseFirestore.instance.collection("tmMessages").doc('messages').collection(idChat!).get().then((value) {
       for (DocumentSnapshot ds in value.docs) {
         ds.reference.delete();
@@ -233,7 +234,7 @@ class _MobMessageChatPageState extends State<MobMessageChatPage> {
   Widget build(BuildContext context) {
     var bodyWidth = U.bodyWidth(context);
     WidgetsBinding.instance.addPostFrameCallback((_) {
-      if (messageData.length > 0) {
+      if (messageData.isNotEmpty) {
         if (scrollController.position.maxScrollExtent > 0 && !isReverse) {
           setState(() => isReverse = true);
         }
@@ -244,11 +245,11 @@ class _MobMessageChatPageState extends State<MobMessageChatPage> {
       }
     });
 
-    return WillPopScope(
-      onWillPop: () async {
+    return PopScope(
+      onPopInvoked: (didPop) async {
         deleteCollection();
         Navigator.of(context).pop();
-        return true;
+        // return true;
       },
       child: Scaffold(
         backgroundColor: Colors.white,
@@ -258,10 +259,10 @@ class _MobMessageChatPageState extends State<MobMessageChatPage> {
           children: [
             divider(),
             U.getInternetStatus()?Expanded(
-              child: Container(width: bodyWidth,
+              child: SizedBox(width: bodyWidth,
                 child:LayoutBuilder(
                     builder: (context,constraint) {
-                      return messageData.length == 0 && !isAfterLoad ? loadingTemplateNoVoid() : Column(
+                      return messageData.isEmpty && !isAfterLoad ? loadingTemplateNoVoid() : Column(
                         children: [
                           Expanded(
                             child: SingleChildScrollView(

+ 2 - 2
lib/src/layouts/mobile/message_list.dart

@@ -12,8 +12,8 @@ import 'package:telnow_mobile_new/src/utils/provider.dart';
 import 'history_forum.dart';
 
 class MobMessageListPage extends StatefulWidget {
-  Map<String, dynamic>? user;
-  MobMessageListPage(this.user, {super.key});
+  final Map<String, dynamic>? user;
+  const MobMessageListPage(this.user, {super.key});
 
   @override
   State<MobMessageListPage> createState() => _MobMessageListPageState();

+ 3 - 3
lib/src/layouts/mobile/password.dart

@@ -11,8 +11,8 @@ import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferenc
 import 'package:telnow_mobile_new/src/utils/U.dart';
 
 class MobPasswordPage extends StatefulWidget {
-  Map<String, dynamic> user;
-  MobPasswordPage({required this.user, super.key});
+  final Map<String, dynamic> user;
+  const MobPasswordPage({required this.user, super.key});
 
   @override
   State<MobPasswordPage> createState() => _MobPasswordPageState();
@@ -88,7 +88,7 @@ class _MobPasswordPageState extends State<MobPasswordPage> {
                       'password':controllerNew.trim()
                     };
             
-                    var res = await apiAuthProvider.patchData('/api/informants/'+widget.user['id'].toString()+'/changePassword', data, context);
+                    var res = await apiAuthProvider.patchData('/api/informants/'+widget.user['id'].toString()+'/changePassword', data);
                     if(res != null){
                       String username = widget.user['_isRelated']?widget.user['relatedTo']:widget.user['userId'];
                       String? password = widget.user['_isRelated'] && widget.user['tenantCode']!=null && widget.user['tenantCode']!=''?widget.user['tenantCode']+' '+data['password']:data['password'];

+ 5 - 5
lib/src/layouts/mobile/request_create.dart

@@ -19,17 +19,17 @@ import 'package:telnow_mobile_new/src/utils/provider.dart';
 import 'package:toggle_switch/toggle_switch.dart';
 
 class MobReqCreatePage extends StatefulWidget {
-  Map<String, dynamic> user;
-  Map<String, dynamic> request;
+  final Map<String, dynamic> user;
+  final Map<String, dynamic> request;
   final bool fromSearch;
-  MobReqCreatePage({required this.user, required this.request, this.fromSearch = false, super.key});
+  const MobReqCreatePage({required this.user, required this.request, this.fromSearch = false, super.key});
 
   @override
   State<MobReqCreatePage> createState() => _MobReqCreatePageState();
 }
 
 getColorScheme(val){
-  var color;
+  Color color;
   switch (val){
     case 0: color = Color(0xFF4FB66C); break;
     case 50: color = Color(0xFFFFA800); break;
@@ -508,7 +508,7 @@ class _MobReqCreatePageState extends State<MobReqCreatePage> {
               ),
             ),
           ),
-          divider(),
+          divider(color: primaryColor, thickness: 2.0, opacity: 1.0),
           Container(
             alignment: Alignment.bottomCenter,
             width: U.bodyWidth(context), color: Colors.white,

+ 7 - 7
lib/src/layouts/mobile/request_select.dart

@@ -10,13 +10,13 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
 
 class MobReqSelectPage extends StatefulWidget {
-  String title;
-  String? groupCode;
-  String scope;
-  String? tenantCode;
-  String? placeholder;
-  Map<String, dynamic> user;
-  MobReqSelectPage({required this.title, this.groupCode, required this.scope, this.tenantCode, required this.user, this.placeholder, super.key});
+  final String title;
+  final String? groupCode;
+  final String scope;
+  final String? tenantCode;
+  final String? placeholder;
+  final Map<String, dynamic> user;
+  const MobReqSelectPage({required this.title, this.groupCode, required this.scope, this.tenantCode, required this.user, this.placeholder, super.key});
 
   @override
   State<MobReqSelectPage> createState() => _MobReqSelectPageState();

+ 5 - 5
lib/src/layouts/mobile/request_success.dart

@@ -12,10 +12,10 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:url_launcher/url_launcher.dart';
 
 class MobReqSuccessPage extends StatefulWidget {
-  Map<String, dynamic> user;
+  final Map<String, dynamic> user;
   final String ticketNo;
   final bool fromSearch;
-  MobReqSuccessPage({required this.user, required this.ticketNo, this.fromSearch = false, super.key});
+  const MobReqSuccessPage({required this.user, required this.ticketNo, this.fromSearch = false, super.key});
 
   @override
   State<MobReqSuccessPage> createState() => _MobReqSuccessPageState();
@@ -34,7 +34,7 @@ class _MobReqSuccessPageState extends State<MobReqSuccessPage> {
   }
 
   getMission() async{
-    var data = await apiAuthProvider.getData('/api/requestHistories/'+widget.ticketNo, null, context);
+    var data = await apiAuthProvider.getData('/api/requestHistories/'+widget.ticketNo, null);
     if(data != null) {
       if (data['datetimeScheduled'] != null && data['datetimeScheduled'] != '' && data['noteFormat'] == 'DATE') {
         var date = data['datetimeScheduled'];
@@ -259,9 +259,9 @@ class _MobReqSuccessPageState extends State<MobReqSuccessPage> {
 // --------------------------------------------------------------------------------------------------------------------------------------------------------
 
 class MobReqSuccessPendingPage extends StatefulWidget {
-  Map<String, dynamic> data;
+  final Map<String, dynamic> data;
   final bool fromSearch;
-  MobReqSuccessPendingPage({required this.data, this.fromSearch = false, super.key});
+  const MobReqSuccessPendingPage({required this.data, this.fromSearch = false, super.key});
 
   @override
   State<MobReqSuccessPendingPage> createState() => _MobReqSuccessPendingPageState();

+ 3 - 3
lib/src/layouts/web/banner_detail.dart

@@ -8,9 +8,9 @@ import 'package:url_launcher/url_launcher.dart';
 import 'package:youtube_player_iframe/youtube_player_iframe.dart';
 
 class WebBannerDetailPage extends StatefulWidget {
-  Map<String, dynamic> user;
-  Map<String, dynamic> data;
-  WebBannerDetailPage({required this.user, required this.data, super.key});
+  final Map<String, dynamic> user;
+  final Map<String, dynamic> data;
+  const WebBannerDetailPage({required this.user, required this.data, super.key});
 
   @override
   State<WebBannerDetailPage> createState() => _WebBannerDetailPageState();

+ 3 - 3
lib/src/layouts/web/history_detail.dart

@@ -11,8 +11,8 @@ import 'package:timelines_plus/timelines_plus.dart';
 import 'package:url_launcher/url_launcher.dart';
 
 class WebHistoryDetailPage extends StatefulWidget {
-  int index;
-  WebHistoryDetailPage({required this.index, super.key});
+  final int index;
+  const WebHistoryDetailPage({required this.index, super.key});
 
   @override
   State<WebHistoryDetailPage> createState() => _WebHistoryDetailPageState();
@@ -42,7 +42,7 @@ class _WebHistoryDetailPageState extends State<WebHistoryDetailPage> {
   }
 
   getData() async{
-    var res = await detFunc.getMission(context, Provider.of<HistoryModule>(context, listen: false).dataMisi()[widget.index]);
+    var res = await detFunc.getMission(context.locale.toString(), Provider.of<HistoryModule>(context, listen: false).dataRequests()[widget.index]);
     setState((){
       user = res['user'];
       list = res['list'];

+ 2 - 2
lib/src/layouts/web/history_detail_pending.dart

@@ -9,8 +9,8 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:timelines_plus/timelines_plus.dart';
 
 class WebHistoryDetailPendingPage extends StatefulWidget {
-  Map<String, dynamic> data;
-  WebHistoryDetailPendingPage({required this.data, super.key});
+  final Map<String, dynamic> data;
+  const WebHistoryDetailPendingPage({required this.data, super.key});
 
   @override
   State<WebHistoryDetailPendingPage> createState() => _WebHistoryDetailPendingPageState();

+ 4 - 4
lib/src/layouts/web/history_forum.dart

@@ -78,7 +78,7 @@ class _WebHistoryForumPageState extends State<WebHistoryForumPage> {
 
   getData() async {
     idChat = U.decodeBase64Url(_sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessCode)!) + '-' + widget.data['ticketNo'];
-    var res = await token.getUserData(context);
+    var res = await token.getUserData();
     if (res != null) {
       username = res['userId'];
       getMessage();
@@ -87,7 +87,7 @@ class _WebHistoryForumPageState extends State<WebHistoryForumPage> {
   }
 
   setAsRead(ticketNo) async {
-    var res = await apiAuthProvider.postData('/api/notifications/readMyForum/$ticketNo', null, null, context);
+    var res = await apiAuthProvider.postData('/api/notifications/readMyForum/$ticketNo', null, null);
     if (res != null) {
     }
   }
@@ -96,7 +96,7 @@ class _WebHistoryForumPageState extends State<WebHistoryForumPage> {
     if (!isLoad && !stopLoad) {
       setState(() => isLoad = true);
       setAsRead(widget.data['ticketNo']);
-      var mymess = await apiAuthProvider.getData('/api/messages/search/myMessages/' + idChat!, {'isPaged': 'true', 'page': page.toString(), 'size': '20'}, context);
+      var mymess = await apiAuthProvider.getData('/api/messages/search/myMessages/' + idChat!, {'isPaged': 'true', 'page': page.toString(), 'size': '20'});
       if (mymess.containsKey('_embedded')) {
         List data = mymess['_embedded']['myMessages'];
         for (int i = 0; i < data.length; i++) {
@@ -215,7 +215,7 @@ class _WebHistoryForumPageState extends State<WebHistoryForumPage> {
       scrollBottom = true;
     });
 
-    var res = await apiAuthProvider.postData('/api/messages', null, data, context);
+    var res = await apiAuthProvider.postData('/api/messages', null, data);
     if (res != null) {
       int index = messageData.indexWhere((element) => element['uniqueId'] == data['uniqueId']);
       setState(() {

+ 205 - 206
lib/src/layouts/web/menu_account.dart

@@ -12,6 +12,7 @@ import 'package:telnow_mobile_new/src/layouts/web/password.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
 import 'package:permission_handler/permission_handler.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 import '../../utils/C.dart';
 
@@ -62,7 +63,6 @@ class _WebAccountPageState extends State<WebAccountPage> {
       lang = license['languages'] != null ? license['languages'].split(',') : [];
     }
     setState(() {
-      dnd = Provider.of<UserModule>(context, listen: false).dndStatus();
       serDis = U.servantDisplay();
       autoTranslate = U.autoTranslate();
     });
@@ -70,6 +70,9 @@ class _WebAccountPageState extends State<WebAccountPage> {
 
   @override
   Widget build(BuildContext context) {
+    final UserModule userModule = Provider.of<UserModule>(context);
+    dnd = userModule.dndStatus();
+
     codeOflang = {
       "id": 'bahasa'.tr(),
       "en": 'english'.tr(),
@@ -82,7 +85,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
       "hi": 'hindi'.tr(),
       "nl": 'dutch'.tr()
     };
-    return Provider.of<UserModule>(context).user().isNotEmpty ? Scaffold(
+    return userModule.user().isNotEmpty ? Scaffold(
       backgroundColor: backgroundColor,
       appBar: PreferredSize(preferredSize: Size.fromHeight(0), child: AppBar(elevation: 0, backgroundColor: primaryColor)),
       body: Column(
@@ -124,12 +127,12 @@ class _WebAccountPageState extends State<WebAccountPage> {
                                 child: Row(
                                   children: [
                                     CircleAvatar(
-                                      child: Text(Provider.of<UserModule>(context).user()['name'][0].toString().toUpperCase(), style: TextStyle(color: Colors.white)),
                                       backgroundColor: primaryColor,
                                       radius: 25,
+                                      child: Text(userModule.user()['name'][0].toString().toUpperCase(), style: TextStyle(color: Colors.white)),
                                     ),
                                     SizedBox(width: 16),
-                                    Expanded(child: Text(Provider.of<UserModule>(context).user()['name'], style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: textColor), maxLines: 2, overflow: TextOverflow.ellipsis))
+                                    Expanded(child: Text(userModule.user()['name'], style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: textColor), maxLines: 2, overflow: TextOverflow.ellipsis))
                                   ],
                                 ),
                               ),
@@ -141,11 +144,11 @@ class _WebAccountPageState extends State<WebAccountPage> {
                                   children: [
                                     Text('info_label'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
                                     SizedBox(height: 20),
-                                    textHorizontal('userId'.tr(), Provider.of<UserModule>(context).user()['relatedTo'] != null ? Provider.of<UserModule>(context).user()['relatedTo'] : Provider.of<UserModule>(context).user()['userId'], size: 16, opacity: 0.75),
+                                    textHorizontal('userId'.tr(), userModule.user()['relatedTo'] ?? userModule.user()['userId'], size: 16, opacity: 0.75),
                                     SizedBox(height: 12),
-                                    textHorizontal('location'.tr(), Provider.of<UserModule>(context).user()['location'] != null ? Provider.of<UserModule>(context).user()['location'] : '-', size: 16, opacity: 0.75),
+                                    textHorizontal('location'.tr(), userModule.user()['location'] ?? '-', size: 16, opacity: 0.75),
                                     SizedBox(height: 12),
-                                    textHorizontal('servantGroup'.tr(), Provider.of<UserModule>(context).user()['requestTypeListDescriptionMobile'] != null ? Provider.of<UserModule>(context).user()['requestTypeListDescriptionMobile'].toLowerCase() == 'semua' ? 'all'.tr() : Provider.of<UserModule>(context).user()['requestTypeListDescriptionMobile'] : '-', size: 16, opacity: 0.75),
+                                    textHorizontal('servantGroup'.tr(), userModule.user()['requestTypeListDescriptionMobile'] != null ? userModule.user()['requestTypeListDescriptionMobile'].toLowerCase() == 'semua' ? 'all'.tr() : userModule.user()['requestTypeListDescriptionMobile'] : '-', size: 16, opacity: 0.75),
                                   ],
                                 ),
                               ),
@@ -172,215 +175,213 @@ class _WebAccountPageState extends State<WebAccountPage> {
                       child: Column(
                         crossAxisAlignment: CrossAxisAlignment.start,
                         children: [
-                          Provider.of<UserModule>(context).houseKeeping()?Container(
-                            child: Column(
-                              crossAxisAlignment: CrossAxisAlignment.start,
-                              children: [
-                                Text('doNotDisturb'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
-                                SizedBox(height: 20),
-                                Row(
-                                  children: [
-                                    Expanded(child: Text('set_dnd_status'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16))),
-                                    Text(dnd?'active_dnd'.tr():'inactive_dnd'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
-                                    SizedBox(width: 5),
-                                    GestureDetector(
-                                      child: Container(
-                                        decoration: BoxDecoration(border: Border.all(color: Provider.of<UserModule>(context).user()['checkedIn']?primaryColor:Colors.black38, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
-                                        child: Row(
-                                          children: [
-                                            !dnd?Container(
-                                              width: 20, height: 20, decoration: BoxDecoration(color: Provider.of<UserModule>(context).user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                            ):Container(width: 20, height: 20),
-                                            dnd?Container(
-                                              width: 20, height: 20, decoration: BoxDecoration(color: Provider.of<UserModule>(context).user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                            ):Container(width: 20, height: 20),
-                                          ],
-                                        ),
-                                      ),
-                                      onTap: Provider.of<UserModule>(context).user()['checkedIn']?(){
-                                        dialogConfirm(context: context, title: 'set_dnd_status'.tr(), text: dnd?'msg_change_inactive'.tr():'msg_change_active'.tr(), actionYes: ()async{
-                                          bool res = await accFunc.setDndStatus(context, dnd);
-                                          if(res){
-                                            setState(() => dnd = !dnd);
-                                          }
+                          if (userModule.houseKeeping()) Column(
+                            crossAxisAlignment: CrossAxisAlignment.start,
+                            children: [
+                              Text('doNotDisturb'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
+                              SizedBox(height: 20),
+                              Row(
+                                children: [
+                                  Expanded(child: Text('set_dnd_status'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16))),
+                                  Text(dnd?'active_dnd'.tr():'inactive_dnd'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
+                                  SizedBox(width: 5),
+                                  GestureDetector(
+                                    onTap: (){
+                                      if(userModule.user()['checkedIn']){
+                                        dialogConfirm(
+                                          context: context,
+                                          title: 'set_dnd_status'.tr(),
+                                          text: dnd ? 'msg_change_inactive'.tr() : 'msg_change_active'.tr(),
+                                          actionYes: ()async{
+                                            bool res = await accFunc.setDndStatus(userModule.user()['id'], dnd);
+                                            if(res){
+                                              userModule.setDndStatus(!dnd);
+                                            }
                                         });
-                                      }:null,
+                                      }
+                                    },
+                                    child: Container(
+                                      decoration: BoxDecoration(border: Border.all(color: userModule.user()['checkedIn']?primaryColor:Colors.black38, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
+                                      child: Row(
+                                        children: [
+                                          !dnd?Container(
+                                            width: 20, height: 20, decoration: BoxDecoration(color: userModule.user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                          ):SizedBox(width: 20, height: 20),
+                                          dnd?Container(
+                                            width: 20, height: 20, decoration: BoxDecoration(color: userModule.user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                          ):SizedBox(width: 20, height: 20),
+                                        ],
+                                      ),
                                     ),
-                                  ],
-                                )
-                              ],
-                            ),
-                          ):Container(),
-                          Provider.of<UserModule>(context).houseKeeping()?Padding(padding: EdgeInsets.symmetric(vertical: 16), child: separator()):Container(),
-
-                          !Provider.of<UserModule>(context).user()['_profile']['isRoom']?Container(
-                            child: Column(
-                              crossAxisAlignment: CrossAxisAlignment.start,
-                              children: [
-                                Text('display_menu'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
-                                SizedBox(height: 20),
-                                Row(
+                                  ),
+                                ],
+                              )
+                            ],
+                          ),
+                          if(userModule.houseKeeping()) Padding(padding: EdgeInsets.symmetric(vertical: 16), child: separator()),
+                          if(!userModule.user()['_profile']['isRoom']) Column(
+                            crossAxisAlignment: CrossAxisAlignment.start,
+                            children: [
+                              Text('display_menu'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
+                              SizedBox(height: 20),
+                              Row(
+                                children: [
+                                  Expanded(child: Text(serDis?'ser_group'.tr():'req_group'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16))),
+                                  SizedBox(width: 5),
+                                  GestureDetector(
+                                    child: Container(
+                                      decoration: BoxDecoration(border: Border.all(color: primaryColor, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
+                                      child: Row(
+                                        children: [
+                                          !serDis?Container(
+                                            width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                          ):SizedBox(width: 20, height: 20),
+                                          serDis?Container(
+                                            width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                          ):SizedBox(width: 20, height: 20),
+                                        ],
+                                      ),
+                                    ),
+                                    onTap: (){
+                                      setState(() {
+                                        if(serDis){
+                                          U.setServantDisplay(false);
+                                          serDis = false;
+                                        }
+                                        else{
+                                          U.setServantDisplay(true);
+                                          serDis = true;
+                                        }
+                                      });
+                                    },
+                                  ),
+                                ],
+                              )
+                            ],
+                          ),
+                          if(!userModule.user()['_profile']['isRoom']) Padding(padding: EdgeInsets.symmetric(vertical: 16), child: separator()),
+                          Column(
+                            crossAxisAlignment: CrossAxisAlignment.start,
+                            children: [
+                              Text('setting'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
+                              SizedBox(height: 6),
+                              GestureDetector(
+                                child: Container(
+                                  color: Colors.white,
+                                  padding: EdgeInsets.symmetric(vertical: 16),
+                                  child: Row(
+                                    children: [
+                                      U.iconsax('bold/global', color: textColor.withValues(alpha: 0.75)),
+                                      SizedBox(width: 16),
+                                      Expanded(child: Text('language'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16), overflow: TextOverflow.ellipsis)),
+                                      Text(codeOflang[context.locale.toString()]!, style: TextStyle(color: textColor)),
+                                      SizedBox(width: 12),
+                                      U.iconsax('arrow-right-3', size: 16, color: textColor.withValues(alpha: 0.75)),
+                                    ],
+                                  ),
+                                ),
+                                onTap: () => changeLang(context, Provider.of<UserModule>(context, listen: false).user()),
+                              ),
+                              divider(),
+                              Container(
+                                color: Colors.white,
+                                padding: EdgeInsets.symmetric(vertical: 16),
+                                child: Row(
                                   children: [
-                                    Expanded(child: Text(serDis?'ser_group'.tr():'req_group'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16))),
+                                    Icon(Icons.g_translate_rounded, color: textColor.withValues(alpha: 0.75)),
+                                    SizedBox(width: 16),
+                                    Expanded(child: Text('auto_translate'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16), overflow: TextOverflow.ellipsis)),
+                                    Text(autoTranslate?'ON':'OFF', style: TextStyle(color: textColor)),
                                     SizedBox(width: 5),
                                     GestureDetector(
                                       child: Container(
-                                        decoration: BoxDecoration(border: Border.all(color: primaryColor, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
+                                        decoration: BoxDecoration(border: Border.all(color: autoTranslate?primaryColor:Colors.black38, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
                                         child: Row(
                                           children: [
-                                            !serDis?Container(
-                                              width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                            ):Container(width: 20, height: 20),
-                                            serDis?Container(
+                                            !autoTranslate?Container(
+                                              width: 20, height: 20, decoration: BoxDecoration(color: Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                            ):SizedBox(width: 20, height: 20),
+                                            autoTranslate?Container(
                                               width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                            ):Container(width: 20, height: 20),
+                                            ):SizedBox(width: 20, height: 20),
                                           ],
                                         ),
                                       ),
                                       onTap: (){
                                         setState(() {
-                                          if(serDis){
-                                            U.setServantDisplay(false);
-                                            serDis = false;
+                                          if(autoTranslate){
+                                            U.setAutoTranslate(false);
+                                            autoTranslate = false;
                                           }
                                           else{
-                                            U.setServantDisplay(true);
-                                            serDis = true;
+                                            U.setAutoTranslate(true);
+                                            autoTranslate = true;
                                           }
                                         });
                                       },
                                     ),
                                   ],
-                                )
-                              ],
-                            ),
-                          ):Container(),
-                          !Provider.of<UserModule>(context).user()['_profile']['isRoom']?Padding(padding: EdgeInsets.symmetric(vertical: 16), child: separator()):Container(),
-
-                          Container(
-                            child: Column(
-                              crossAxisAlignment: CrossAxisAlignment.start,
-                              children: [
-                                Text('setting'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
-                                SizedBox(height: 6),
-                                GestureDetector(
-                                  child: Container(
-                                    color: Colors.white,
-                                    padding: EdgeInsets.symmetric(vertical: 16),
-                                    child: Row(
-                                      children: [
-                                        U.iconsax('bold/global', color: textColor.withValues(alpha: 0.75)),
-                                        SizedBox(width: 16),
-                                        Expanded(child: Text('language'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16), overflow: TextOverflow.ellipsis)),
-                                        Text(codeOflang[context.locale.toString()]!, style: TextStyle(color: textColor)),
-                                        SizedBox(width: 12),
-                                        U.iconsax('arrow-right-3', size: 16, color: textColor.withValues(alpha: 0.75)),
-                                      ],
-                                    ),
-                                  ),
-                                  onTap: () => changeLang(context, Provider.of<UserModule>(context, listen: false).user()),
                                 ),
-                                divider(),
-                                Container(
+                              ),
+                              divider(),
+                              GestureDetector(
+                                child: Container(
                                   color: Colors.white,
                                   padding: EdgeInsets.symmetric(vertical: 16),
                                   child: Row(
                                     children: [
-                                      Icon(Icons.g_translate_rounded, color: textColor.withValues(alpha: 0.75)),
+                                      U.iconsax('bold/key', color: textColor.withValues(alpha: 0.75)),
                                       SizedBox(width: 16),
-                                      Expanded(child: Text('auto_translate'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16), overflow: TextOverflow.ellipsis)),
-                                      Text(autoTranslate?'ON':'OFF', style: TextStyle(color: textColor)),
-                                      SizedBox(width: 5),
-                                      GestureDetector(
-                                        child: Container(
-                                          decoration: BoxDecoration(border: Border.all(color: autoTranslate?primaryColor:Colors.black38, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
-                                          child: Row(
-                                            children: [
-                                              !autoTranslate?Container(
-                                                width: 20, height: 20, decoration: BoxDecoration(color: Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                              ):Container(width: 20, height: 20),
-                                              autoTranslate?Container(
-                                                width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                              ):Container(width: 20, height: 20),
-                                            ],
-                                          ),
-                                        ),
-                                        onTap: (){
-                                          setState(() {
-                                            if(autoTranslate){
-                                              U.setAutoTranslate(false);
-                                              autoTranslate = false;
-                                            }
-                                            else{
-                                              U.setAutoTranslate(true);
-                                              autoTranslate = true;
-                                            }
-                                          });
-                                        },
-                                      ),
+                                      Text('password'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16)),
+                                      Expanded(child: Text('settingPasswordText'.tr(), style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis, textAlign: TextAlign.end)),
+                                      SizedBox(width: 12),
+                                      U.iconsax('arrow-right-3', size: 16, color: textColor.withValues(alpha: 0.75)),
                                     ],
                                   ),
                                 ),
-                                divider(),
-                                GestureDetector(
-                                  child: Container(
-                                    color: Colors.white,
-                                    padding: EdgeInsets.symmetric(vertical: 16),
-                                    child: Row(
-                                      children: [
-                                        U.iconsax('bold/key', color: textColor.withValues(alpha: 0.75)),
-                                        SizedBox(width: 16),
-                                        Text('password'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16)),
-                                        Expanded(child: Text('settingPasswordText'.tr(), style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis, textAlign: TextAlign.end)),
-                                        SizedBox(width: 12),
-                                        U.iconsax('arrow-right-3', size: 16, color: textColor.withValues(alpha: 0.75)),
-                                      ],
-                                    ),
-                                  ),
-                                  onTap: ()=>Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: WebPasswordPage(user: Provider.of<UserModule>(context, listen: false).user()))).then((value) {
-                                    value??false;
-                                    if (value) showSuccess(context, 'messagePassChanged'.tr());
-                                  }),
-                                ),
-                                divider(),
-                                GestureDetector(
-                                  child: Container(
-                                    color: Colors.white,
-                                    padding: EdgeInsets.symmetric(vertical: 16),
-                                    child: Row(
-                                      children: [
-                                        U.iconsax('bold/logout', color: textColor.withValues(alpha: 0.75)),
-                                        SizedBox(width: 16),
-                                        Expanded(child: Text('logout'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16), overflow: TextOverflow.ellipsis)),
-                                        U.iconsax('arrow-right-3', size: 16, color: textColor.withValues(alpha: 0.75)),
-                                      ],
-                                    ),
+                                onTap: ()=>Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: WebPasswordPage(user: Provider.of<UserModule>(context, listen: false).user()))).then((value) {
+                                  value??false;
+                                  if (value) UIService.showSuccess('messagePassChanged'.tr());
+                                }),
+                              ),
+                              divider(),
+                              GestureDetector(
+                                child: Container(
+                                  color: Colors.white,
+                                  padding: EdgeInsets.symmetric(vertical: 16),
+                                  child: Row(
+                                    children: [
+                                      U.iconsax('bold/logout', color: textColor.withValues(alpha: 0.75)),
+                                      SizedBox(width: 16),
+                                      Expanded(child: Text('logout'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16), overflow: TextOverflow.ellipsis)),
+                                      U.iconsax('arrow-right-3', size: 16, color: textColor.withValues(alpha: 0.75)),
+                                    ],
                                   ),
-                                  onTap: (){
-                                    showDialog(
-                                      context: context,
-                                      builder: (BuildContext context) {
-                                        return AlertDialog(
-                                          title: Text("logout".tr()),
-                                          content: Text("textLogout".tr()),
-                                          actions: <Widget>[
-                                            TextButton(
-                                              child: Text("buttonNo".tr()),
-                                              onPressed: () {
-                                                Navigator.of(context).pop();
-                                              },
-                                            ),
-                                            TextButton(
-                                                onPressed: () => accFunc.logoutAction(context),
-                                                child: Text("buttonYes".tr())),
-                                          ],
-                                        );
-                                      },
-                                    );
-                                  },
                                 ),
-                              ],
-                            ),
+                                onTap: (){
+                                  showDialog(
+                                    context: context,
+                                    builder: (BuildContext context) {
+                                      return AlertDialog(
+                                        title: Text("logout".tr()),
+                                        content: Text("textLogout".tr()),
+                                        actions: <Widget>[
+                                          TextButton(
+                                            child: Text("buttonNo".tr()),
+                                            onPressed: () {
+                                              Navigator.of(context).pop();
+                                            },
+                                          ),
+                                          TextButton(
+                                              onPressed: () => accFunc.logoutAction(context),
+                                              child: Text("buttonYes".tr())),
+                                        ],
+                                      );
+                                    },
+                                  );
+                                },
+                              ),
+                            ],
                           ),
                           separator(),
                           GestureDetector(
@@ -399,7 +400,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
                             onTap: (){
                               var size = MediaQuery.of(context).size.height/2;
                               var pid = U.getPidFromUrl(context.router.currentUrl);
-                              var qr_data = 'https://telnow.telmessenger.com/#/app/${pid}%23${U.getBaseUrl()}';
+                              var qrData = 'https://telnow.telmessenger.com/#/app/$pid%23${U.getBaseUrl()}';
                               showDialog(
                                 context: context,
                                 builder: (BuildContext context) {
@@ -413,7 +414,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
                                           width: size,
                                           height: size,
                                           child: QrImageView(
-                                            data: qr_data,
+                                            data: qrData,
                                             version: QrVersions.auto,
                                             gapless: true,
                                           ),
@@ -428,21 +429,19 @@ class _WebAccountPageState extends State<WebAccountPage> {
                             },
                           ),
                           isDeniedNotifPermission ? Padding(padding: EdgeInsets.symmetric(vertical: 16), child: separator()) : Container(),
-                          isDeniedNotifPermission ? Container(
-                            child: GestureDetector(
-                              child: Column(
-                                crossAxisAlignment: CrossAxisAlignment.start,
-                                children: [
-                                  Text("not_allow_permission".tr()),
-                                  SizedBox(height: 8,),
-                                  Text("tap_here".tr(), style: TextStyle(color: Color(0xFF198AF2)),),
-                                ],
-                              ),
-                              onTap: () => AppSettings.openAppSettings(type: AppSettingsType.notification).then((value) async {
-                                // print("after open setting");
-                                // checkPermission(2);
-                              }),
+                          isDeniedNotifPermission ? GestureDetector(
+                            child: Column(
+                              crossAxisAlignment: CrossAxisAlignment.start,
+                              children: [
+                                Text("not_allow_permission".tr()),
+                                SizedBox(height: 8,),
+                                Text("tap_here".tr(), style: TextStyle(color: Color(0xFF198AF2)),),
+                              ],
                             ),
+                            onTap: () => AppSettings.openAppSettings(type: AppSettingsType.notification).then((value) async {
+                              // print("after open setting");
+                              // checkPermission(2);
+                            }),
                           ) : Container(),
                         ],
                       ),
@@ -454,7 +453,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
           )
         ],
       ),
-    ) : Provider.of<UserModule>(context).resetData() ? RefreshPage(() {
+    ) : userModule.resetData() ? RefreshPage(() {
       Provider.of<UserModule>(context, listen: false).setResetData(false);
       accFunc.getUser(context);
     }) : _timeLimit ? showButton(context) : loadingTemplate(() {if(mounted) setState(()=>_timeLimit=true);},);
@@ -495,7 +494,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
             title: Text('bahasa'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()=='id'?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != 'id' ? () async {
-              accFunc.switchLang(context, 'id', user);
+              accFunc.switchLang('id', user);
               Navigator.of(cd).pop();
             } : null,
           ),
@@ -503,7 +502,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
             title: Text('english'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()=='en'?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != 'en' ? () async {
-              accFunc.switchLang(context, 'en', user);
+              accFunc.switchLang('en', user);
               Navigator.of(cd).pop();
             } : null,
           ),
@@ -511,7 +510,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
             title: Text(codeOflang[lang[2]], style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()==lang[2]?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != lang[2] ? () async {
-              accFunc.switchLang(context, 1, user);
+              accFunc.switchLang(1, user);
               Navigator.of(cd).pop();
             } : null,
           ) : Container(),
@@ -519,7 +518,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
             title: Text(codeOflang[lang[3]], style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()==lang[3]?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != lang[3] ? () async {
-              accFunc.switchLang(context, 2, user);
+              accFunc.switchLang(2, user);
               Navigator.of(cd).pop();
             } : null,
           ) : Container(),
@@ -527,7 +526,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
             title: Text(codeOflang[lang[4]], style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()==lang[4]?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != lang[4] ? () async {
-              accFunc.switchLang(context, 3, user);
+              accFunc.switchLang(3, user);
               Navigator.of(cd).pop();
             } : null,
           ) : Container(),

+ 133 - 129
lib/src/layouts/web/menu_history.dart

@@ -21,6 +21,8 @@ class WebHistoryPage extends StatefulWidget {
 class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStateMixin {
   final HistoryFunction hisFunc = HistoryFunction();
   late AnimationController _animationController;
+  late final HistoryModule historyModule = Provider.of<HistoryModule>(context, listen: false);
+  late final UserModule userModule = Provider.of<UserModule>(context, listen: false);
 
   var rating = [
     {'key': 1, 'image': "assets/image/icon/very_dissatisfied.png", 'label': 'disatisfied'.tr()},
@@ -34,12 +36,12 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
   @override
   void initState() {
     // print(U.newServerVersion(1716279633));
-    Provider.of<HistoryModule>(context, listen: false).reset();
-    _animationController = new AnimationController(vsync: this, duration: Duration(seconds: 1));
+    historyModule.reset();
+    _animationController = AnimationController(vsync: this, duration: Duration(seconds: 1));
     _animationController.repeat(reverse: true);
 
     WidgetsBinding.instance.addPostFrameCallback((_) {
-      hisFunc.getActiveForum(context);
+      hisFunc.getActiveForum();
     });
     // TODO: implement initState
     super.initState();
@@ -53,9 +55,11 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
 
   @override
   Widget build(BuildContext context) {
-    return Provider.of<UserModule>(context).resetData() ? RefreshPage(() {
-      Provider.of<UserModule>(context, listen: false).setResetData(false);
-      hisFunc.getActiveForum(context);
+    final HistoryModule historyModuleListen = Provider.of<HistoryModule>(context);
+    final UserModule userModuleListen = Provider.of<UserModule>(context);
+    return userModuleListen.resetData() ? RefreshPage(() {
+      userModule.setResetData(false);
+      hisFunc.getActiveForum();
     }) : Scaffold(
       backgroundColor: backgroundColor,
       appBar: PreferredSize(preferredSize: Size.fromHeight(0), child: AppBar(elevation: 0, backgroundColor: primaryColor)),
@@ -67,9 +71,10 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
               mainAxisAlignment: MainAxisAlignment.spaceBetween,
               children: [
                 Text('history'.tr(), style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
-                Provider.of<HistoryModule>(context).multiSelectMode() ? GestureDetector(
+                historyModuleListen.multiSelectMode() ? GestureDetector(
                   child: Container(
                     padding: EdgeInsets.fromLTRB(16, 9, 16, 9),
+                    decoration: BoxDecoration(color: primaryColor.withValues(alpha: 0.1), border: Border.all(color: primaryColor), borderRadius: BorderRadius.all(Radius.circular(50))),
                     child: Row(
                       children: [
                         Text('delete'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)),
@@ -77,7 +82,6 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                         U.iconsax('trash', color: primaryColor, size: 20)
                       ],
                     ),
-                    decoration: BoxDecoration(color: primaryColor.withValues(alpha: 0.1), border: Border.all(color: primaryColor), borderRadius: BorderRadius.all(Radius.circular(50))),
                   ),
                   onTap: () => hisFunc.deleteData(context),
                 ) : GestureDetector(
@@ -102,52 +106,52 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                     GestureDetector(
                       child: Container(
                         padding: EdgeInsets.symmetric(vertical: 5, horizontal: 16),
-                        child: Text('ongoing'.tr(), style: TextStyle(color: Provider.of<HistoryModule>(context).selectedFilter() == 1 ? textColor : textColor.withValues(alpha: 0.65), fontSize: 16)),
-                        decoration: BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: Provider.of<HistoryModule>(context).selectedFilter() == 1 ? primaryColor : primaryColor.withValues(alpha: 0.3), width: Provider.of<HistoryModule>(context).selectedFilter() == 1 ? 2 : 1))),
+                        decoration: BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: historyModuleListen.activeTab() == 1 ? primaryColor : primaryColor.withValues(alpha: 0.3), width: historyModuleListen.activeTab() == 1 ? 2 : 1))),
+                        child: Text('ongoing'.tr(), style: TextStyle(color: historyModuleListen.activeTab() == 1 ? textColor : textColor.withValues(alpha: 0.65), fontSize: 16)),
                       ),
                       onTap: () {
-                        if (!Provider.of<HistoryModule>(context, listen: false).isLoadHistory()) {
-                          Provider.of<HistoryModule>(context, listen: false).setPressAttention(0);
-                          Provider.of<HistoryModule>(context, listen: false).setSelectedFilter(1);
-                          hisFunc.onRefresh(context);
+                        if (!historyModule.isLoadHistory()) {
+                          historyModule.setSelectedFilter(0);
+                          historyModule.setActiveTab(HistoryTab.ongoing);
+                          hisFunc.onRefresh();
                         }
                       },
                     ),
                     GestureDetector(
                       child: Container(
                         padding: EdgeInsets.symmetric(vertical: 5, horizontal: 16),
-                        child: Text('done'.tr(), style: TextStyle(color: Provider.of<HistoryModule>(context).selectedFilter() == 0 ? textColor : textColor.withValues(alpha: 0.65), fontSize: 16)),
-                        decoration: BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: Provider.of<HistoryModule>(context).selectedFilter() == 0 ? primaryColor : primaryColor.withValues(alpha: 0.3), width: Provider.of<HistoryModule>(context).selectedFilter() == 0 ? 2 : 1))),
+                        decoration: BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: historyModuleListen.activeTab() == 0 ? primaryColor : primaryColor.withValues(alpha: 0.3), width: historyModuleListen.activeTab() == 0 ? 2 : 1))),
+                        child: Text('done'.tr(), style: TextStyle(color: historyModuleListen.activeTab() == 0 ? textColor : textColor.withValues(alpha: 0.65), fontSize: 16)),
                       ),
                       onTap: () {
-                        if (!Provider.of<HistoryModule>(context, listen: false).isLoadHistory()) {
-                          Provider.of<HistoryModule>(context, listen: false).setPressAttention(0);
-                          Provider.of<HistoryModule>(context, listen: false).setSelectedFilter(0);
-                          hisFunc.onRefresh(context);
+                        if (!historyModule.isLoadHistory()) {
+                          historyModule.setSelectedFilter(0);
+                          historyModule.setActiveTab(HistoryTab.done);
+                          hisFunc.onRefresh();
                         }
                       },
                     ),
                   ],
                 ),
                 SizedBox(height: 20),
-                Provider.of<HistoryModule>(context).selectedFilter() == 0 ? doneFilter() : ongoingFilter(),
+                historyModuleListen.activeTab() == 0 ? doneFilter() : ongoingFilter(),
               ],
             ),
           ),
           Expanded(
-            child: Provider.of<UserModule>(context).user().isNotEmpty ? Container(
+            child: userModuleListen.user().isNotEmpty ? Container(
               width: double.infinity,
               padding: EdgeInsets.symmetric(horizontal: 100),
               child: LazyLoadScrollView(
-                onEndOfPage: () => hisFunc.getMission(context),
+                onEndOfPage: () => hisFunc.getMission(),
                 scrollOffset: 500,
                 child: EasyRefresh(
                   header: MaterialHeader(clamping: true, color: primaryColor),
-                  onRefresh: ()=>hisFunc.onRefresh(context),
-                  child: Provider.of<HistoryModule>(context).selectedFilter() == 0 ? doneContainer() : ongoingContainer(),
+                  onRefresh: ()=>hisFunc.onRefresh(),
+                  child: historyModuleListen.activeTab() == 0 ? doneContainer() : ongoingContainer(),
                 ),
               ),
-            ) : Provider.of<UserModule>(context).resetData() ? Container() : _timeLimit ? showButton(context) : loadingTemplate(() {if(mounted) setState(()=>_timeLimit=true);},),
+            ) : userModuleListen.resetData() ? Container() : _timeLimit ? showButton(context) : loadingTemplate(() {if(mounted) setState(()=>_timeLimit=true);},),
           )
         ],
       ),
@@ -155,6 +159,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
   }
 
   Widget ongoingFilter() {
+    final HistoryModule historyModuleListen = Provider.of<HistoryModule>(context);
     return SingleChildScrollView(
       scrollDirection: Axis.horizontal,
       child: Row(
@@ -163,20 +168,20 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
             child: Container(
               margin: EdgeInsets.only(right: 16),
               padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
-              child: Text(hisFunc.ongoingList[i]['title'], style: TextStyle(color: Provider.of<HistoryModule>(context).checkPressAttention(hisFunc.ongoingList[i]['value']) ? Colors.white : textColor, fontSize: 16)),
               decoration: BoxDecoration(
-                  color: Provider.of<HistoryModule>(context).checkPressAttention(hisFunc.ongoingList[i]['value']) ? primaryColor : Color(0xffF2F8F7),
-                  border: Border.all(color: Provider.of<HistoryModule>(context).checkPressAttention(hisFunc.ongoingList[i]['value']) ? primaryColor : Color(0xffBEC1C1)),
+                  color: historyModuleListen.checkSelectedFilter(hisFunc.ongoingList[i]['value']) ? primaryColor : Color(0xffF2F8F7),
+                  border: Border.all(color: historyModuleListen.checkSelectedFilter(hisFunc.ongoingList[i]['value']) ? primaryColor : Color(0xffBEC1C1)),
                   borderRadius: BorderRadius.all(Radius.circular(50))),
+              child: Text(hisFunc.ongoingList[i]['title'], style: TextStyle(color: historyModuleListen.checkSelectedFilter(hisFunc.ongoingList[i]['value']) ? Colors.white : textColor, fontSize: 16)),
             ),
             onTap: () {
-              if (!Provider.of<HistoryModule>(context, listen: false).isLoadHistory()) {
-                if (Provider.of<HistoryModule>(context, listen: false).checkPressAttention(hisFunc.ongoingList[i]['value'])) {
-                  Provider.of<HistoryModule>(context, listen: false).setPressAttention(0);
-                  hisFunc.onRefresh(context);
+              if (!historyModule.isLoadHistory()) {
+                if (historyModule.checkSelectedFilter(hisFunc.ongoingList[i]['value'])) {
+                  historyModule.setSelectedFilter(0);
+                  hisFunc.onRefresh();
                 } else {
-                  Provider.of<HistoryModule>(context, listen: false).setPressAttention(hisFunc.ongoingList[i]['value']);
-                  hisFunc.onRefresh(context);
+                  historyModule.setSelectedFilter(hisFunc.ongoingList[i]['value']);
+                  hisFunc.onRefresh();
                 }
               }
             },
@@ -187,11 +192,12 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
   }
 
   Widget ongoingContainer() {
+    final HistoryModule historyModuleListen = Provider.of<HistoryModule>(context);
     return SingleChildScrollView(
       child: Column(
         children: [
           U.newServerVersion(1709864293) && !U.getInternetStatus() ? Column(
-            children: List.generate(Provider.of<HistoryModule>(context).dataPending().length, (i) {
+            children: List.generate(historyModuleListen.dataPending().length, (i) {
               return GestureDetector(
                 child: Container(
                   width: double.infinity,
@@ -203,7 +209,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                       Row(
                         mainAxisAlignment: MainAxisAlignment.spaceBetween,
                         children: [
-                          Text(convertDate(Provider.of<HistoryModule>(context).dataPending()[i]['datetimeRequest'], context.locale.toString()), style: TextStyle(color: primaryColor, fontSize: 14)),
+                          Text(convertDate(historyModuleListen.dataPending()[i]['datetimeRequest'], context.locale.toString()), style: TextStyle(color: primaryColor, fontSize: 14)),
                           renderStatus('sending'.tr(), Color(0xff02C539).withValues(alpha: 0.2))
                         ],
                       ),
@@ -212,22 +218,22 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                       SizedBox(height: 10),
                       Row(
                         children: [
-                          imageTiles(imageUrl: Provider.of<HistoryModule>(context).dataPending()[i]['image'] ?? "null"),
+                          imageTiles(imageUrl: historyModuleListen.dataPending()[i]['image'] ?? "null"),
                           SizedBox(width: 25),
                           Expanded(
                             child: Column(
                               crossAxisAlignment: CrossAxisAlignment.start,
                               children: [
-                                Text(Provider.of<HistoryModule>(context).dataPending()[i]['title'], style: TextStyle(color: textColor, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
+                                Text(historyModuleListen.dataPending()[i]['title'], style: TextStyle(color: textColor, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
                                 SizedBox(height: 10),
-                                Text(Provider.of<HistoryModule>(context).dataPending()[i][U.langColumn(context, 'sub')], style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
+                                Text(historyModuleListen.dataPending()[i][U.langColumn(context, 'sub')], style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
                                 SizedBox(height: 5),
-                                Text(Provider.of<HistoryModule>(context).dataPending()[i][U.langColumn(context, 'desc')], style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
+                                Text(historyModuleListen.dataPending()[i][U.langColumn(context, 'desc')], style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
                                 dashed(top: 10, bottom: 10),
                                 Row(
                                   mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                   children: [
-                                    Text('location'.tr()+': '+Provider.of<HistoryModule>(context).dataPending()[i]['location'], style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
+                                    Text('${'location'.tr()}: ${historyModuleListen.dataPending()[i]['location']}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                                     Row(
                                       children: [
                                         Container(
@@ -250,13 +256,13 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                 ),
                 onTap: () {
                   if(!U.getInternetStatus()){
-                    // navigateTo(context, MobHistoryDetailPendingPage(data: Provider.of<HistoryModule>(context, listen: false).dataPending()[i])).then((val) {
+                    // navigateTo(context, MobHistoryDetailPendingPage(data: historyModule.dataPending()[i])).then((val) {
                     //   val = val??false;
                     //   if(val) hisFunc.onRefresh(context);
                     // });
                   }
                   else{
-                    hisFunc.onRefresh(context);
+                    hisFunc.onRefresh();
                   }
                 },
               );
@@ -264,7 +270,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
           ) : Container(),
 
           Column(
-            children: List.generate(Provider.of<HistoryModule>(context).dataMisi().length, (i) {
+            children: List.generate(historyModuleListen.dataRequests().length, (i) {
               return GestureDetector(
                 child: Container(
                   width: double.infinity,
@@ -275,27 +281,25 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                     children: [
                       Row(
                         children: [
-                          Text(convertDate(Provider.of<HistoryModule>(context).dataMisi()[i]['datetimeRequest'], context.locale.toString()), style: TextStyle(color: primaryColor, fontSize: 14)),
+                          Text(convertDate(historyModuleListen.dataRequests()[i]['datetimeRequest'], context.locale.toString()), style: TextStyle(color: primaryColor, fontSize: 14)),
                           SizedBox(width: 15),
                           Expanded(
-                            child: Text('ticketNumber'.tr()+': '+Provider.of<HistoryModule>(context).dataMisi()[i]['ticketNo'], style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
+                            child: Text('${'ticketNumber'.tr()}: ${historyModuleListen.dataRequests()[i]['ticketNo']}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                           ),
-                          Provider.of<HistoryModule>(context).dataMisi()[i]['currentState'] == 'DIMULAI' ? Container(
-                            child: Row(
-                              children: [
-                                Container(
-                                  width: 25, height: 25,
-                                  decoration: BoxDecoration(
-                                      border: Border.all(color: primaryColor), borderRadius: BorderRadius.all(Radius.circular(25)),
-                                      image: DecorationImage(image: AssetImage('assets/image/general/Avatar.jpg'), fit: BoxFit.cover)
-                                  ),
+                          historyModuleListen.dataRequests()[i]['currentState'] == 'DIMULAI' ? Row(
+                            children: [
+                              Container(
+                                width: 25, height: 25,
+                                decoration: BoxDecoration(
+                                    border: Border.all(color: primaryColor), borderRadius: BorderRadius.all(Radius.circular(25)),
+                                    image: DecorationImage(image: AssetImage('assets/image/general/Avatar.jpg'), fit: BoxFit.cover)
                                 ),
-                                SizedBox(width: 8),
-                                Text(Provider.of<HistoryModule>(context).dataMisi()[i]['servantNameStart'] + 'isWorking'.tr(), style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis)
-                              ],
-                            ),
+                              ),
+                              SizedBox(width: 8),
+                              Text(historyModuleListen.dataRequests()[i]['servantNameStart'] + 'isWorking'.tr(), style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis)
+                            ],
                           ) : Container(),
-                          Provider.of<HistoryModule>(context).dataMisi()[i]['_activeHoldRequest'] != null?renderStatus('hold'.tr(), Color(0xffD3D3D3)):Provider.of<HistoryModule>(context).dataMisi()[i]['currentState'] == 'DIMULAI'?renderStatus('onProgress'.tr(), Color(0xffCCA600).withValues(alpha: 0.2)):renderStatus('queued'.tr(), Color(0xff02C539).withValues(alpha: 0.2))
+                          historyModuleListen.dataRequests()[i]['_activeHoldRequest'] != null?renderStatus('hold'.tr(), Color(0xffD3D3D3)):historyModuleListen.dataRequests()[i]['currentState'] == 'DIMULAI'?renderStatus('onProgress'.tr(), Color(0xffCCA600).withValues(alpha: 0.2)):renderStatus('queued'.tr(), Color(0xff02C539).withValues(alpha: 0.2))
                         ],
                       ),
                       SizedBox(height: 10),
@@ -303,42 +307,40 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                       SizedBox(height: 10),
                       Row(
                         children: [
-                          imageTiles(imageUrl: Provider.of<HistoryModule>(context).dataMisi()[i]['_requestImage'] ?? "null", width: 150, height: 120),
+                          imageTiles(imageUrl: historyModuleListen.dataRequests()[i]['_requestImage'] ?? "null", width: 150, height: 120),
                           SizedBox(width: 25),
                           Expanded(
                             child: Column(
                               crossAxisAlignment: CrossAxisAlignment.start,
                               children: [
-                                Text(Provider.of<HistoryModule>(context).dataMisi()[i][U.langColumn(context, 'requestGroupDescription')], style: TextStyle(color: textColor, fontSize: 16), overflow: TextOverflow.ellipsis),
+                                Text(historyModuleListen.dataRequests()[i][U.langColumn(context, 'requestGroupDescription')], style: TextStyle(color: textColor, fontSize: 16), overflow: TextOverflow.ellipsis),
                                 SizedBox(height: 10),
-                                Text(Provider.of<HistoryModule>(context).dataMisi()[i][U.langColumn(context, 'requestSubject')], style: TextStyle(color: textColor, fontWeight: FontWeight.w600), overflow: TextOverflow.ellipsis),
+                                Text(historyModuleListen.dataRequests()[i][U.langColumn(context, 'requestSubject')], style: TextStyle(color: textColor, fontWeight: FontWeight.w600), overflow: TextOverflow.ellipsis),
                                 SizedBox(height: 5),
-                                Text(Provider.of<HistoryModule>(context).dataMisi()[i][U.langColumn(context, '_subjectDescription')]??'', style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
+                                Text(historyModuleListen.dataRequests()[i][U.langColumn(context, '_subjectDescription')]??'', style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
                                 dashed(top: 10, bottom: 10),
-                                renderRequested(Provider.of<HistoryModule>(context).dataMisi()[i]),
+                                renderRequested(historyModuleListen.dataRequests()[i]),
                                 Row(
                                   mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                   crossAxisAlignment: CrossAxisAlignment.center ,
                                   children: [
-                                    Text('location'.tr()+': '+Provider.of<HistoryModule>(context).dataMisi()[i]['ipphoneExtLocation'], style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
-                                    Provider.of<HistoryModule>(context).dataMisi()[i]['_activeHoldRequest'] != null ? Container(
-                                      child: Row(
-                                        children: [
-                                          Container(
-                                            width: 25, height: 25,
-                                            decoration: BoxDecoration(image: DecorationImage(image: AssetImage('assets/image/general/Watch.png'), fit: BoxFit.cover)),
-                                          ),
-                                          SizedBox(width: 8),
-                                          Text(Provider.of<HistoryModule>(context).dataMisi()[i]['_activeHoldRequest']['description'], style: TextStyle(color: textColor, fontSize: 14))
-                                        ],
-                                      ),
-                                    ) : Provider.of<HistoryModule>(context).dataMisi()[i]['_hasForum'] && Provider.of<HistoryModule>(context).dataMisi()[i]['_forumMsg'] != null ? ConstrainedBox(
+                                    Text('${'location'.tr()}: ${historyModuleListen.dataRequests()[i]['ipphoneExtLocation']}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
+                                    historyModuleListen.dataRequests()[i]['_activeHoldRequest'] != null ? Row(
+                                      children: [
+                                        Container(
+                                          width: 25, height: 25,
+                                          decoration: BoxDecoration(image: DecorationImage(image: AssetImage('assets/image/general/Watch.png'), fit: BoxFit.cover)),
+                                        ),
+                                        SizedBox(width: 8),
+                                        Text(historyModuleListen.dataRequests()[i]['_activeHoldRequest']['description'], style: TextStyle(color: textColor, fontSize: 14))
+                                      ],
+                                    ) : historyModuleListen.dataRequests()[i]['_hasForum'] && historyModuleListen.dataRequests()[i]['_forumMsg'] != null ? ConstrainedBox(
                                       constraints: BoxConstraints(minWidth: 10),
                                       child: Row(
                                         children: [
                                           FadeTransition(opacity: _animationController, child: U.iconsax('messages-3', color: primaryColor)),
                                           SizedBox(width: 8),
-                                          Text('“${Provider.of<HistoryModule>(context).dataMisi()[i]['_forumMsg']}”', style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 14, fontStyle: FontStyle.italic))
+                                          Text('“${historyModuleListen.dataRequests()[i]['_forumMsg']}”', style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 14, fontStyle: FontStyle.italic))
                                         ],
                                       ),
                                     ) : Container(),
@@ -354,14 +356,14 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                 ),
                 onTap: () => navigateTo(context, WebHistoryDetailPage(index: i)).then((val) {
                   val = val??false;
-                  if(val) Provider.of<HistoryModule>(context, listen: false).cancelMisi(i);
+                  if(val) historyModule.cancelMisi(i);
                 }),
               );
             }),
           ),
-          !Provider.of<HistoryModule>(context).stopLoadHistory() || (Provider.of<HistoryModule>(context).isLoadHistory() && Provider.of<HistoryModule>(context).page() > 0)
-              ? Container(margin: EdgeInsets.only(top: Provider.of<HistoryModule>(context).page() > 0 ? 0 : 50), child: loadingTemplateNoVoid())
-              : Provider.of<HistoryModule>(context).dataMisi().isEmpty && Provider.of<HistoryModule>(context).dataPending().isEmpty
+          !historyModuleListen.stopLoadHistory() || (historyModuleListen.isLoadHistory() && historyModuleListen.page() > 0)
+              ? Container(margin: EdgeInsets.only(top: historyModuleListen.page() > 0 ? 0 : 50), child: loadingTemplateNoVoid())
+              : historyModuleListen.dataRequests().isEmpty && historyModuleListen.dataPending().isEmpty
               ? NoDataPage()
               : Container(),
         ],
@@ -370,6 +372,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
   }
 
   Widget doneFilter() {
+    final HistoryModule historyModuleListen = Provider.of<HistoryModule>(context);
     return Row(
       children: List.generate(hisFunc.doneList.length, (i) {
         return GestureDetector(
@@ -377,19 +380,19 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
             margin: EdgeInsets.only(right: 16),
             padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
             decoration: BoxDecoration(
-                color: Provider.of<HistoryModule>(context).checkPressAttention(hisFunc.doneList[i]['value']) ? primaryColor : Color(0xffF2F8F7),
-                border: Border.all(color: Provider.of<HistoryModule>(context).checkPressAttention(hisFunc.doneList[i]['value']) ? primaryColor : Color(0xffBEC1C1)),
+                color: historyModuleListen.checkSelectedFilter(hisFunc.doneList[i]['value']) ? primaryColor : Color(0xffF2F8F7),
+                border: Border.all(color: historyModuleListen.checkSelectedFilter(hisFunc.doneList[i]['value']) ? primaryColor : Color(0xffBEC1C1)),
                 borderRadius: BorderRadius.all(Radius.circular(50))),
-            child: Text(hisFunc.doneList[i]['title'], style: TextStyle(color: Provider.of<HistoryModule>(context).checkPressAttention(hisFunc.doneList[i]['value']) ? Colors.white : textColor, fontSize: 16)),
+            child: Text(hisFunc.doneList[i]['title'], style: TextStyle(color: historyModuleListen.checkSelectedFilter(hisFunc.doneList[i]['value']) ? Colors.white : textColor, fontSize: 16)),
           ),
           onTap: () {
-            if (!Provider.of<HistoryModule>(context, listen: false).isLoadHistory()) {
-              if (Provider.of<HistoryModule>(context, listen: false).checkPressAttention(hisFunc.doneList[i]['value'])) {
-                Provider.of<HistoryModule>(context, listen: false).setPressAttention(0);
-                hisFunc.onRefresh(context);
+            if (!historyModule.isLoadHistory()) {
+              if (historyModule.checkSelectedFilter(hisFunc.doneList[i]['value'])) {
+                historyModule.setSelectedFilter(0);
+                hisFunc.onRefresh();
               } else {
-                Provider.of<HistoryModule>(context, listen: false).setPressAttention(hisFunc.doneList[i]['value']);
-                hisFunc.onRefresh(context);
+                historyModule.setSelectedFilter(hisFunc.doneList[i]['value']);
+                hisFunc.onRefresh();
               }
             }
           },
@@ -399,11 +402,12 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
   }
 
   Widget doneContainer() {
+    final HistoryModule historyModuleListen = Provider.of<HistoryModule>(context);
     return SingleChildScrollView(
       child: Column(
         children: [
           Column(
-              children: List.generate(Provider.of<HistoryModule>(context).dataMisi().length, (i) {
+              children: List.generate(historyModuleListen.dataRequests().length, (i) {
                 return GestureDetector(
                   child: Container(
                     width: double.infinity,
@@ -414,12 +418,12 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                       children: [
                         Row(
                           children: [
-                            Text(convertDate(Provider.of<HistoryModule>(context).dataMisi()[i]['datetimeRequest'], context.locale.toString()), style: TextStyle(color: primaryColor, fontSize: 14)),
+                            Text(convertDate(historyModuleListen.dataRequests()[i]['datetimeRequest'], context.locale.toString()), style: TextStyle(color: primaryColor, fontSize: 14)),
                             SizedBox(width: 15),
                             Expanded(
-                              child: Text('ticketNumber'.tr()+': '+Provider.of<HistoryModule>(context).dataMisi()[i]['ticketNo'], style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
+                              child: Text('${'ticketNumber'.tr()}: ${historyModuleListen.dataRequests()[i]['ticketNo']}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                             ),
-                            Provider.of<HistoryModule>(context).dataMisi()[i]['currentState'] == 'DISELESAIKAN' || Provider.of<HistoryModule>(context).dataMisi()[i]['currentState'] == 'TUNTAS' ? renderStatus('stateFinish'.tr(), primaryColor.withValues(alpha: 0.4)) : renderStatus('stateCancel'.tr(), Color(0xffD81010).withValues(alpha: 0.4))
+                            historyModuleListen.dataRequests()[i]['currentState'] == 'DISELESAIKAN' || historyModuleListen.dataRequests()[i]['currentState'] == 'TUNTAS' ? renderStatus('stateFinish'.tr(), primaryColor.withValues(alpha: 0.4)) : renderStatus('stateCancel'.tr(), Color(0xffD81010).withValues(alpha: 0.4))
                           ],
                         ),
                         SizedBox(height: 10),
@@ -427,20 +431,20 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                         SizedBox(height: 10),
                         Row(
                           children: [
-                            imageTiles(imageUrl: Provider.of<HistoryModule>(context).dataMisi()[i]['_requestImage'] ?? "null", width: 150, height: 120),
+                            imageTiles(imageUrl: historyModuleListen.dataRequests()[i]['_requestImage'] ?? "null", width: 150, height: 120),
                             SizedBox(width: 25),
                             Expanded(
                               child: Column(
                                 crossAxisAlignment: CrossAxisAlignment.start,
                                 children: [
-                                  Text(Provider.of<HistoryModule>(context).dataMisi()[i][U.langColumn(context, 'requestGroupDescription')], style: TextStyle(color: textColor, fontSize: 16), overflow: TextOverflow.ellipsis),
+                                  Text(historyModuleListen.dataRequests()[i][U.langColumn(context, 'requestGroupDescription')], style: TextStyle(color: textColor, fontSize: 16), overflow: TextOverflow.ellipsis),
                                   SizedBox(height: 10),
-                                  Text(Provider.of<HistoryModule>(context).dataMisi()[i][U.langColumn(context, 'requestSubject')], style: TextStyle(color: textColor, fontWeight: FontWeight.w600), overflow: TextOverflow.ellipsis),
+                                  Text(historyModuleListen.dataRequests()[i][U.langColumn(context, 'requestSubject')], style: TextStyle(color: textColor, fontWeight: FontWeight.w600), overflow: TextOverflow.ellipsis),
                                   SizedBox(height: 5),
-                                  Text(Provider.of<HistoryModule>(context).dataMisi()[i][U.langColumn(context, '_subjectDescription')]??'', style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
+                                  Text(historyModuleListen.dataRequests()[i][U.langColumn(context, '_subjectDescription')]??'', style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
                                   dashed(top: 10, bottom: 10),
-                                  renderRequested(Provider.of<HistoryModule>(context).dataMisi()[i]),
-                                  Text('location'.tr()+': '+Provider.of<HistoryModule>(context).dataMisi()[i]['ipphoneExtLocation'], style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
+                                  renderRequested(historyModuleListen.dataRequests()[i]),
+                                  Text('${'location'.tr()}: ${historyModuleListen.dataRequests()[i]['ipphoneExtLocation']}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                                 ],
                               ),
                             )
@@ -452,31 +456,31 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                         Row(
                           mainAxisAlignment: MainAxisAlignment.spaceBetween,
                           children: [
-                            !Provider.of<HistoryModule>(context).dataMisi()[i]['autoResponse'] && Provider.of<HistoryModule>(context).dataMisi()[i]['servantIdentityStart'] != '#system' && (Provider.of<HistoryModule>(context).dataMisi()[i]['currentState'] == 'TUNTAS' || Provider.of<HistoryModule>(context).dataMisi()[i]['currentState'] == 'DISELESAIKAN') ? Container(
-                              child: hisFunc.askForRate(Provider.of<HistoryModule>(context, listen: true).dataMisi()[i]) ? GestureDetector(
+                            !historyModuleListen.dataRequests()[i]['autoResponse'] && historyModuleListen.dataRequests()[i]['servantIdentityStart'] != '#system' && (historyModuleListen.dataRequests()[i]['currentState'] == 'TUNTAS' || historyModuleListen.dataRequests()[i]['currentState'] == 'DISELESAIKAN') ? Container(
+                              child: hisFunc.askForRate(Provider.of<HistoryModule>(context, listen: true).dataRequests()[i]) ? GestureDetector(
                                 child: Container(
                                   padding: EdgeInsets.symmetric(vertical: 4, horizontal: 16),
-                                  child: Text('rateReq'.tr(), style: TextStyle(color: secondaryColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                                   decoration: BoxDecoration(color: Colors.white, border: Border.all(color: secondaryColor), borderRadius: BorderRadius.all(Radius.circular(50))),
+                                  child: Text('rateReq'.tr(), style: TextStyle(color: secondaryColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                                 ),
                                 onTap: () {
-                                  U.newServerVersion(1716279633) ? rateMissionNew(context, Provider.of<HistoryModule>(context, listen: false).dataMisi()[i], i) : rateMission(context, Provider.of<HistoryModule>(context, listen: false).dataMisi()[i], i);
+                                  U.newServerVersion(1716279633) ? rateMissionNew(context, historyModule.dataRequests()[i], i) : rateMission(context, historyModule.dataRequests()[i], i);
                                 },
-                              ) : !hisFunc.isAutoResponse(Provider.of<HistoryModule>(context).dataMisi()[i]) ? Row(
+                              ) : !hisFunc.isAutoResponse(historyModuleListen.dataRequests()[i]) ? Row(
                                 children: [
-                                  Image(image: AssetImage(rating[Provider.of<HistoryModule>(context).dataMisi()[i]['satisfactionRate'] - 1]['image'].toString()), width: 25),
+                                  Image(image: AssetImage(rating[historyModuleListen.dataRequests()[i]['satisfactionRate'] - 1]['image'].toString()), width: 25),
                                   SizedBox(width: 5),
-                                  Text(rating[Provider.of<HistoryModule>(context).dataMisi()[i]['satisfactionRate'] - 1]['label'].toString(), style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
+                                  Text(rating[historyModuleListen.dataRequests()[i]['satisfactionRate'] - 1]['label'].toString(), style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                                 ],
                               ) : Container(),
-                            ) : message(Provider.of<HistoryModule>(context).dataMisi()[i]['_hasForum'], Provider.of<HistoryModule>(context).dataMisi()[i]['_forumMsg'] ?? "null"),
+                            ) : message(historyModuleListen.dataRequests()[i]['_hasForum'], historyModuleListen.dataRequests()[i]['_forumMsg'] ?? "null"),
                             GestureDetector(
                               child: Container(
                                 padding: EdgeInsets.symmetric(vertical: 4, horizontal: 16),
-                                child: Text('reqAgain'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)),
                                 decoration: BoxDecoration(color: Colors.white, border: Border.all(color: primaryColor), borderRadius: BorderRadius.all(Radius.circular(50))),
+                                child: Text('reqAgain'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)),
                               ),
-                              onTap: () => hisFunc.requestAgainAction(context, i),
+                              onTap: () => hisFunc.requestAgainAction(i),
                             )
                           ],
                         ),
@@ -484,7 +488,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                     ),
                   ),
                   onTap: () {
-                    if (Provider.of<HistoryModule>(context, listen: false).multiSelectMode()) {
+                    if (historyModule.multiSelectMode()) {
                       hisFunc.selectedController(context, i);
                     } else {
                       navigateTo(context, WebHistoryDetailPage(index: i));
@@ -494,9 +498,9 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                 );
               })
           ),
-          !Provider.of<HistoryModule>(context).stopLoadHistory() || (Provider.of<HistoryModule>(context).isLoadHistory() && Provider.of<HistoryModule>(context).page() > 0)
-              ? Container(margin: EdgeInsets.only(top: Provider.of<HistoryModule>(context).page() > 0 ? 0 : 50), child: loadingTemplateNoVoid())
-              : Provider.of<HistoryModule>(context).dataMisi().length == 0 ? NoDataPage() : Container(),
+          !historyModuleListen.stopLoadHistory() || (historyModuleListen.isLoadHistory() && historyModuleListen.page() > 0)
+              ? Container(margin: EdgeInsets.only(top: historyModuleListen.page() > 0 ? 0 : 50), child: loadingTemplateNoVoid())
+              : historyModuleListen.dataRequests().isEmpty ? NoDataPage() : Container(),
         ],
       ),
     );
@@ -504,7 +508,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
 
   Widget renderRequested(list){
     if(list['receptionistId'] != null){
-      var user = Provider.of<UserModule>(context, listen: false).user();
+      var user = userModule.user();
       if(user['roomAttendant'] && user['userId'] != list['informantUserId'] && user['userId'] == list['receptionistId']){
         return Column(
           crossAxisAlignment: CrossAxisAlignment.start,
@@ -529,14 +533,14 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
     return Container();
   }
 
-  Widget message(bool _hasForum, String _forumMsg, {double top = 0}) {
-    return _hasForum ? Container(
+  Widget message(bool hasForum, String forumMsg, {double top = 0}) {
+    return hasForum ? Container(
       margin: EdgeInsets.only(top: top),
       child: Row(
         children: [
           FadeTransition(opacity: _animationController, child: U.iconsax('messages-3', color: primaryColor)),
           SizedBox(width: 8),
-          Text('“$_forumMsg”', style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 14, fontStyle: FontStyle.italic), overflow: TextOverflow.ellipsis),
+          Text('“$forumMsg”', style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 14, fontStyle: FontStyle.italic), overflow: TextOverflow.ellipsis),
         ],
       ),
     ) : Container();
@@ -546,10 +550,10 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
     return Container(
       padding: EdgeInsets.symmetric(vertical: 3, horizontal: 10),
       margin: EdgeInsets.only(left: 10),
-      child: Text(text, style: TextStyle(fontSize: 12, color: textColor)),
       decoration: BoxDecoration(
         color: color, borderRadius: BorderRadius.all(Radius.circular(3))
       ),
+      child: Text(text, style: TextStyle(fontSize: 12, color: textColor)),
     );
   }
 
@@ -631,9 +635,9 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                                     if (tempRating > 0) {
                                       var data = {"ticketNo": list['ticketNo'], "rateSatisfy": tempRating};
 
-                                      var res = await ApiAuthProvider() .postData('/api/requestHistories/rateSatisfy', null, data, contexts);
+                                      var res = await ApiAuthProvider() .postData('/api/requestHistories/rateSatisfy', null, data);
                                       if (res != null) {
-                                        Provider.of<HistoryModule>(context, listen: false).setSatisfaction(index, tempRating);
+                                        historyModule.setSatisfaction(index, tempRating);
                                         navigateBack(contexts);
                                       }
                                     }
@@ -664,7 +668,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
           String description = '';
           String ratingAspect = '';
           TextEditingController controllerOptOther = new TextEditingController();
-          List aspectList = list['_ratingAspect'+locale[0].toUpperCase()+locale[1]] != null && list['_ratingAspect'+locale[0].toUpperCase()+locale[1]].trim() != '' ? list['_ratingAspect'+locale[0].toUpperCase()+locale[1]].split(';') : [];
+          List aspectList = list['_ratingAspect${locale[0].toUpperCase()}${locale[1]}'] != null && list['_ratingAspect'+locale[0].toUpperCase()+locale[1]].trim() != '' ? list['_ratingAspect'+locale[0].toUpperCase()+locale[1]].split(';') : [];
           if(aspectList.isNotEmpty){
             aspectList.add('OTHER_OPTION');
           }
@@ -693,7 +697,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                             Row(
                               mainAxisAlignment: MainAxisAlignment.spaceBetween,
                               children: [
-                                Text('ticketNumber'.tr()+': '+list['ticketNo'], style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w300)),
+                                Text('${'ticketNumber'.tr()}: '+list['ticketNo'], style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w300)),
                                 Text(convertDate(list['datetimeRequest'], context.locale.toString()), style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w300)),
                               ],
                             ),
@@ -735,7 +739,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                             tempRating != 0 ? Column(
                               crossAxisAlignment: CrossAxisAlignment.start,
                               children: [
-                                Text('whatMakesYou'.tr()+' '+description+'?', style: TextStyle(fontSize: 16)),
+                                Text('${'whatMakesYou'.tr()} $description?', style: TextStyle(fontSize: 16)),
                                 SizedBox(height: 12),
                                 Wrap(
                                   runSpacing: 10, spacing: 10,
@@ -799,9 +803,9 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                                         data['aspect'] = controllerOptOther.text;
                                       }
 
-                                      var res = await ApiAuthProvider() .postData('/api/requestHistories/rateSatisfy', null, data, contexts);
+                                      var res = await ApiAuthProvider() .postData('/api/requestHistories/rateSatisfy', null, data);
                                       if (res != null) {
-                                        Provider.of<HistoryModule>(context, listen: false).setSatisfaction(index, tempRating);
+                                        historyModule.setSatisfaction(index, tempRating);
                                         navigateBack(contexts);
                                       }
                                     }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 12 - 12
lib/src/layouts/web/menu_home.dart


+ 4 - 4
lib/src/layouts/web/message_broadcast.dart

@@ -9,9 +9,9 @@ import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 
 class WebMessageBroadcastPage extends StatefulWidget {
-  Map<String, dynamic> user;
-  List tenants;
-  WebMessageBroadcastPage(this.user, this.tenants, {super.key});
+  final Map<String, dynamic> user;
+  final List tenants;
+  const WebMessageBroadcastPage(this.user, this.tenants, {super.key});
 
   @override
   State<WebMessageBroadcastPage> createState() => _WebMessageBroadcastPageState();
@@ -67,7 +67,7 @@ class _WebMessageBroadcastPageState extends State<WebMessageBroadcastPage> {
     setState(() {
       isSending = true;
     });
-    var res = await ApiAuthProvider().postData('/api/messages/broadcast', params, data, context);
+    var res = await ApiAuthProvider().postData('/api/messages/broadcast', params, data);
     if (res != null) {
       Navigator.pop(context, 'pop');
     }

+ 148 - 149
lib/src/layouts/web/message_list.dart

@@ -22,11 +22,10 @@ import 'dart:ui' as ui;
 import 'package:uuid/uuid.dart';
 
 import '../components/photo_chat.dart';
-import '../mobile/message_chat.dart';
 
 class WebMessageListPage extends StatefulWidget {
-  Map<String, dynamic>? user;
-  WebMessageListPage(this.user, {super.key});
+  final Map<String, dynamic>? user;
+  const WebMessageListPage(this.user, {super.key});
 
   @override
   State<WebMessageListPage> createState() => _WebMessageListPageState();
@@ -109,7 +108,7 @@ class _WebMessageListPageState extends State<WebMessageListPage> {
 
       scrollController.addListener(() => scrollListener());
       if (!isMe) {
-        await apiAuthProvider.patchData('/api/messages/' + data['id'].toString(), {'readStatus': 'READ'}, context);
+        await apiAuthProvider.patchData('/api/messages/' + data['id'].toString(), {'readStatus': 'READ'});
       }
 
       getMessage();
@@ -145,7 +144,7 @@ class _WebMessageListPageState extends State<WebMessageListPage> {
     }
     if (!isLoad && !stopLoad) {
       isLoad = true;
-      var mymess = await apiAuthProvider.getData('/api/messages/search/$url', {'isPaged': 'true', 'page': page.toString(), 'size': '20', 'filter': filter, 'tenant': tenant}, context);
+      var mymess = await apiAuthProvider.getData('/api/messages/search/$url', {'isPaged': 'true', 'page': page.toString(), 'size': '20', 'filter': filter, 'tenant': tenant});
       if (mymess.containsKey('_embedded') && mounted) {
         List data = mymess['_embedded']['myMessages'];
         for (int i = 0; i < data.length; i++) {
@@ -189,7 +188,7 @@ class _WebMessageListPageState extends State<WebMessageListPage> {
     try{
       var mymess = await apiAuthProvider.getData(
           '/api/messages/search/myMessages/${forumChatId}',
-          {'isPaged': 'true', 'page': page.toString(), 'size': '20'}, context);
+          {'isPaged': 'true', 'page': page.toString(), 'size': '20'});
 
       if (mymess.containsKey('_embedded')) {
         List data = mymess['_embedded']['myMessages'];
@@ -436,147 +435,147 @@ class _WebMessageListPageState extends State<WebMessageListPage> {
       );
     }
 
-    Widget chat(){
-      final messageModule = Provider.of<MessageModule>(context);
-      final dataList = messageModule.data();
-      final currentUser = messageModule.user();
-      final forumList = messageModule.forum();
-
-      return dataList.isEmpty ? Expanded(
-        child: Center(
-          child: Text('noMessageText'.tr()),
-        ),
-      ) : SingleChildScrollView(
-        padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
-        child: Column(
-          mainAxisSize: MainAxisSize.max,
-          children: List.generate(dataList.length, (i) {
-            final item = dataList[i];
-            final isMe = item['userId'] == currentUser['userId'];
-            final avatar = item['senderAvatar'];
-            final chatIdMatch = idChat != null && idChat == item['chatId'];
-            final recipientColor = U.getColor(isMe ? item['recipient'] : item['recipientId']);
-            final recipientName = item['recipientName'];
-            final senderName = item['senderName'];
-            final lastText = item['lastText'] ?? '';
-            final isImage = item['isImage'] ?? false;
-            final fileType = item['fileType'] ?? '';
-            final lastDate = item['lastDateTimeSend'];
-            final lastReadStatus = item['lastReadStatus'];
-            final forumReadStatus = forumList[i]['readStatus'];
-
-            return GestureDetector(
-              onTap: () => selectMessage(item, isMe),
-              child: Container(
-                padding: EdgeInsets.all(10),
-                decoration: BoxDecoration(
-                  color: chatIdMatch ? Color(0xff26DA17).withAlpha(50) : Colors.white,
-                  borderRadius: BorderRadius.circular(12),
-                ),
-                child: Row(
-                  children: [
-                    avatar != null && avatar != ''
-                        ? CircleAvatar(
-                      backgroundImage: NetworkImage(avatar),
-                      radius: 24,
-                    )
-                        : Container(
-                      height: 48,
-                      width: 48,
-                      decoration: BoxDecoration(
-                        shape: BoxShape.circle,
-                        color: Color(recipientColor),
-                      ),
-                      child: Center(
-                        child: Text(
-                          isMe
-                              ? recipientName == "all_informants"
-                              ? "allInformants".tr()[0]
-                              : recipientName[0]
-                              : senderName[0],
-                          style: TextStyle(
-                            color: Colors.white,
-                            fontWeight: FontWeight.bold,
-                            fontSize: 18,
-                          ),
-                        ),
-                      ),
-                    ),
-                    SizedBox(width: 20),
-                    Expanded(
-                      child: Column(
-                        crossAxisAlignment: CrossAxisAlignment.start,
-                        children: [
-                          Text(
-                            isMe
-                                ? recipientName == "all_informants"
-                                ? "allInformants".tr()
-                                : recipientName
-                                : senderName,
-                            style: TextStyle(fontWeight: FontWeight.w600),
-                          ),
-                          SizedBox(height: 5),
-                          Row(
-                            children: [
-                              if (isMe)
-                                Padding(
-                                  padding: EdgeInsets.only(right: 5),
-                                  child: Icon(Icons.done_all, size: 18, color: Colors.blueGrey),
-                                ),
-                              if (isImage)
-                                Row(
-                                  children: [
-                                    Icon(
-                                      fileType == 'pdf' ? Icons.picture_as_pdf : Icons.image,
-                                      color: Colors.black45,
-                                      size: 16,
-                                    ),
-                                    SizedBox(width: 6),
-                                    if (lastText == '')
-                                      Text(fileType == 'pdf' ? 'pdfFile'.tr() : 'photo'.tr()),
-                                  ],
-                                ),
-                              Expanded(
-                                child: Text(
-                                  lastText,
-                                  style: TextStyle(fontSize: 13),
-                                  maxLines: 1,
-                                  overflow: TextOverflow.ellipsis,
-                                ),
-                              ),
-                            ],
-                          ),
-                        ],
-                      ),
-                    ),
-                    SizedBox(width: 20),
-                    Column(
-                      crossAxisAlignment: CrossAxisAlignment.end,
-                      children: [
-                        Text(
-                          convertDate(lastDate, context.locale.toString()),
-                          style: TextStyle(
-                            fontSize: 11,
-                            fontWeight: forumReadStatus == 'UNREAD' ? FontWeight.w600 : FontWeight.w300,
-                            color: lastReadStatus == 'UNREAD' && !isMe ? primaryColor : Colors.black45,
-                          ),
-                        ),
-                        SizedBox(height: 12),
-                        Icon(
-                          Icons.circle,
-                          size: 12,
-                          color: primaryColor.withAlpha(lastReadStatus == 'UNREAD' && !isMe ? 1 : 0),
-                        ),
-                      ],
-                    ),
-                  ],
-                ),
-              ),
-            );
-          }),
-        ),
-      );
-    }
+    // Widget chat(){
+    //   final messageModule = Provider.of<MessageModule>(context);
+    //   final dataList = messageModule.data();
+    //   final currentUser = messageModule.user();
+    //   final forumList = messageModule.forum();
+    //
+    //   return dataList.isEmpty ? Expanded(
+    //     child: Center(
+    //       child: Text('noMessageText'.tr()),
+    //     ),
+    //   ) : SingleChildScrollView(
+    //     padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
+    //     child: Column(
+    //       mainAxisSize: MainAxisSize.max,
+    //       children: List.generate(dataList.length, (i) {
+    //         final item = dataList[i];
+    //         final isMe = item['userId'] == currentUser['userId'];
+    //         final avatar = item['senderAvatar'];
+    //         final chatIdMatch = idChat != null && idChat == item['chatId'];
+    //         final recipientColor = U.getColor(isMe ? item['recipient'] : item['recipientId']);
+    //         final recipientName = item['recipientName'];
+    //         final senderName = item['senderName'];
+    //         final lastText = item['lastText'] ?? '';
+    //         final isImage = item['isImage'] ?? false;
+    //         final fileType = item['fileType'] ?? '';
+    //         final lastDate = item['lastDateTimeSend'];
+    //         final lastReadStatus = item['lastReadStatus'];
+    //         final forumReadStatus = forumList[i]['readStatus'];
+    //
+    //         return GestureDetector(
+    //           onTap: () => selectMessage(item, isMe),
+    //           child: Container(
+    //             padding: EdgeInsets.all(10),
+    //             decoration: BoxDecoration(
+    //               color: chatIdMatch ? Color(0xff26DA17).withAlpha(50) : Colors.white,
+    //               borderRadius: BorderRadius.circular(12),
+    //             ),
+    //             child: Row(
+    //               children: [
+    //                 avatar != null && avatar != ''
+    //                     ? CircleAvatar(
+    //                   backgroundImage: NetworkImage(avatar),
+    //                   radius: 24,
+    //                 )
+    //                     : Container(
+    //                   height: 48,
+    //                   width: 48,
+    //                   decoration: BoxDecoration(
+    //                     shape: BoxShape.circle,
+    //                     color: Color(recipientColor),
+    //                   ),
+    //                   child: Center(
+    //                     child: Text(
+    //                       isMe
+    //                           ? recipientName == "all_informants"
+    //                           ? "allInformants".tr()[0]
+    //                           : recipientName[0]
+    //                           : senderName[0],
+    //                       style: TextStyle(
+    //                         color: Colors.white,
+    //                         fontWeight: FontWeight.bold,
+    //                         fontSize: 18,
+    //                       ),
+    //                     ),
+    //                   ),
+    //                 ),
+    //                 SizedBox(width: 20),
+    //                 Expanded(
+    //                   child: Column(
+    //                     crossAxisAlignment: CrossAxisAlignment.start,
+    //                     children: [
+    //                       Text(
+    //                         isMe
+    //                             ? recipientName == "all_informants"
+    //                             ? "allInformants".tr()
+    //                             : recipientName
+    //                             : senderName,
+    //                         style: TextStyle(fontWeight: FontWeight.w600),
+    //                       ),
+    //                       SizedBox(height: 5),
+    //                       Row(
+    //                         children: [
+    //                           if (isMe)
+    //                             Padding(
+    //                               padding: EdgeInsets.only(right: 5),
+    //                               child: Icon(Icons.done_all, size: 18, color: Colors.blueGrey),
+    //                             ),
+    //                           if (isImage)
+    //                             Row(
+    //                               children: [
+    //                                 Icon(
+    //                                   fileType == 'pdf' ? Icons.picture_as_pdf : Icons.image,
+    //                                   color: Colors.black45,
+    //                                   size: 16,
+    //                                 ),
+    //                                 SizedBox(width: 6),
+    //                                 if (lastText == '')
+    //                                   Text(fileType == 'pdf' ? 'pdfFile'.tr() : 'photo'.tr()),
+    //                               ],
+    //                             ),
+    //                           Expanded(
+    //                             child: Text(
+    //                               lastText,
+    //                               style: TextStyle(fontSize: 13),
+    //                               maxLines: 1,
+    //                               overflow: TextOverflow.ellipsis,
+    //                             ),
+    //                           ),
+    //                         ],
+    //                       ),
+    //                     ],
+    //                   ),
+    //                 ),
+    //                 SizedBox(width: 20),
+    //                 Column(
+    //                   crossAxisAlignment: CrossAxisAlignment.end,
+    //                   children: [
+    //                     Text(
+    //                       convertDate(lastDate, context.locale.toString()),
+    //                       style: TextStyle(
+    //                         fontSize: 11,
+    //                         fontWeight: forumReadStatus == 'UNREAD' ? FontWeight.w600 : FontWeight.w300,
+    //                         color: lastReadStatus == 'UNREAD' && !isMe ? primaryColor : Colors.black45,
+    //                       ),
+    //                     ),
+    //                     SizedBox(height: 12),
+    //                     Icon(
+    //                       Icons.circle,
+    //                       size: 12,
+    //                       color: primaryColor.withAlpha(lastReadStatus == 'UNREAD' && !isMe ? 1 : 0),
+    //                     ),
+    //                   ],
+    //                 ),
+    //               ],
+    //             ),
+    //           ),
+    //         );
+    //       }),
+    //     ),
+    //   );
+    // }
 
     Widget webChat(BuildContext context) {
       final messageModule = Provider.of<MessageModule>(context);
@@ -607,7 +606,7 @@ class _WebMessageListPageState extends State<WebMessageListPage> {
             final chatId = item['chatId'];
             final recipientId = item['recipientId'];
             final recipient = item['recipient'];
-            final id = item['id'];
+            // final id = item['id'];
 
             return GestureDetector(
               onTap: () {
@@ -997,7 +996,7 @@ class _WebMessageListPageState extends State<WebMessageListPage> {
       });
       scrollBottom = true;
 
-      var res = await apiAuthProvider.postData('/api/messages', null, data, context);
+      var res = await apiAuthProvider.postData('/api/messages', null, data);
       if (res != null) {
         int index = forumData.indexWhere((element) => element['uniqueId'] == data['uniqueId']);
         forumData[index]['read'] = false;

+ 4 - 4
lib/src/layouts/web/password.dart

@@ -11,8 +11,8 @@ import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferenc
 import 'package:telnow_mobile_new/src/utils/U.dart';
 
 class WebPasswordPage extends StatefulWidget {
-  Map<String, dynamic> user;
-  WebPasswordPage({required this.user, super.key});
+  final Map<String, dynamic> user;
+  const WebPasswordPage({required this.user, super.key});
 
   @override
   State<WebPasswordPage> createState() => _WebPasswordPageState();
@@ -57,6 +57,7 @@ class _WebPasswordPageState extends State<WebPasswordPage> {
                     alignment: Alignment.topLeft,
                     width: MediaQuery.of(context).size.width/3,
                     padding: EdgeInsets.all(20),
+                    decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
                     child: Column(
                       crossAxisAlignment: CrossAxisAlignment.start,
                       children: [
@@ -94,7 +95,7 @@ class _WebPasswordPageState extends State<WebPasswordPage> {
                                 'password':controllerNew.trim()
                               };
 
-                              var res = await apiAuthProvider.patchData('/api/informants/'+widget.user['id'].toString()+'/changePassword', data, context);
+                              var res = await apiAuthProvider.patchData('/api/informants/'+widget.user['id'].toString()+'/changePassword', data);
                               if(res != null){
                                 String username = widget.user['_isRelated']?widget.user['relatedTo']:widget.user['userId'];
                                 String? password = widget.user['_isRelated'] && widget.user['tenantCode']!=null && widget.user['tenantCode']!=''?widget.user['tenantCode']+' '+data['password']:data['password'];
@@ -122,7 +123,6 @@ class _WebPasswordPageState extends State<WebPasswordPage> {
                         })
                       ],
                     ),
-                    decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
                   ),
                   Expanded(child: Container())
                 ],

+ 3 - 3
lib/src/layouts/web/request_create.dart

@@ -14,10 +14,10 @@ import 'package:telnow_mobile_new/src/utils/provider.dart';
 import 'package:toggle_switch/toggle_switch.dart';
 
 class WebReqCreatePage extends StatefulWidget {
-  Map<String, dynamic> user;
-  Map<String, dynamic> request;
+  final Map<String, dynamic> user;
+  final Map<String, dynamic> request;
   final bool fromSearch;
-  WebReqCreatePage({required this.user, required this.request, this.fromSearch = false, super.key});
+  const WebReqCreatePage({required this.user, required this.request, this.fromSearch = false, super.key});
 
   @override
   State<WebReqCreatePage> createState() => _WebReqCreatePageState();

+ 9 - 9
lib/src/layouts/web/request_select.dart

@@ -10,13 +10,13 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
 
 class WebReqSelectPage extends StatefulWidget {
-  String title;
-  String? groupCode;
-  String scope;
-  String? tenantCode;
-  String? placeholder;
-  Map<String, dynamic> user;
-  WebReqSelectPage({required this.title, this.groupCode, required this.scope, this.tenantCode, required this.user, this.placeholder, super.key});
+  final String title;
+  final String? groupCode;
+  final String scope;
+  final String? tenantCode;
+  final String? placeholder;
+  final Map<String, dynamic> user;
+  const WebReqSelectPage({required this.title, this.groupCode, required this.scope, this.tenantCode, required this.user, this.placeholder, super.key});
 
   @override
   State<WebReqSelectPage> createState() => _WebReqSelectPageState();
@@ -87,7 +87,7 @@ class _WebReqSelectPageState extends State<WebReqSelectPage> {
                 SizedBox(
                   height: 40, width: 100,
                   child: ElevatedButton(
-                    style: ButtonStyle(elevation: MaterialStateProperty.all<double>(0), backgroundColor: MaterialStateProperty.all<Color>(primaryColor), shape: MaterialStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(50))), side: MaterialStateProperty.all<BorderSide>(BorderSide(color: primaryColor))),
+                    style: ButtonStyle(elevation: WidgetStateProperty.all<double>(0), backgroundColor: WidgetStateProperty.all<Color>(primaryColor), shape: WidgetStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(50))), side: WidgetStateProperty.all<BorderSide>(BorderSide(color: primaryColor))),
                     onPressed: ()=>reqFunc.getData(context, searchController.text.trim()==''?null:searchController.text.trim(), widget),
                     child: Text('searchButton'.tr(), style: TextStyle(fontSize: 14, color: Colors.white, fontWeight: FontWeight.w600)),
                   ),
@@ -138,6 +138,7 @@ class _WebReqSelectPageState extends State<WebReqSelectPage> {
                             constraints: BoxConstraints(minWidth: minWidth, minHeight: minHeight),
                             child: Container(
                               width: size, height: size*1.4,
+                              decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
                               child: Column(
                                 crossAxisAlignment: CrossAxisAlignment.start,
                                 children: [
@@ -156,7 +157,6 @@ class _WebReqSelectPageState extends State<WebReqSelectPage> {
                                   )
                                 ],
                               ),
-                              decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
                             ),
                           ),
                           onTap: ()=>navigateTo(context, WebReqCreatePage(user: widget.user, request: Provider.of<RequestModule>(context, listen: false).data()[i], fromSearch: true)),

+ 5 - 5
lib/src/layouts/web/request_success.dart

@@ -11,10 +11,10 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:url_launcher/url_launcher.dart';
 
 class WebReqSuccessPage extends StatefulWidget {
-  Map<String, dynamic> user;
+  final Map<String, dynamic> user;
   final String ticketNo;
   final bool fromSearch;
-  WebReqSuccessPage({required this.user, required this.ticketNo, this.fromSearch = false, super.key});
+  const WebReqSuccessPage({required this.user, required this.ticketNo, this.fromSearch = false, super.key});
 
   @override
   State<WebReqSuccessPage> createState() => _WebReqSuccessPageState();
@@ -33,7 +33,7 @@ class _WebReqSuccessPageState extends State<WebReqSuccessPage> {
   }
 
   getMission() async{
-    var data = await apiAuthProvider.getData('/api/requestHistories/'+widget.ticketNo, null, context);
+    var data = await apiAuthProvider.getData('/api/requestHistories/'+widget.ticketNo, null);
     if(data != null) {
       if (data['datetimeScheduled'] != null && data['datetimeScheduled'] != '' && data['noteFormat'] == 'DATE') {
         var date = data['datetimeScheduled'];
@@ -276,9 +276,9 @@ class _WebReqSuccessPageState extends State<WebReqSuccessPage> {
 // -----------------------------------------------------------------------------------------------------------------------------
 
 class WebReqSuccessPendingPage extends StatefulWidget {
-  Map<String, dynamic> data;
+  final Map<String, dynamic> data;
   final bool fromSearch;
-  WebReqSuccessPendingPage({required this.data, this.fromSearch = false, super.key});
+  const WebReqSuccessPendingPage({required this.data, this.fromSearch = false, super.key});
 
   @override
   State<WebReqSuccessPendingPage> createState() => _WebReqSuccessPendingPageState();

+ 1 - 1
lib/src/model/token/token.g.dart

@@ -6,7 +6,7 @@ part of 'token.dart';
 // JsonSerializableGenerator
 // **************************************************************************
 
-Token _$TokenFromJson(Map<String?, dynamic?> json) {
+Token _$TokenFromJson(Map<String?, dynamic> json) {
   return Token(
     json['access_token'] as String,
     json['token_type'] as String,

+ 18 - 7
lib/src/utils/U.dart

@@ -18,7 +18,6 @@ import 'package:telnow_mobile_new/src/injector/injector.dart';
 import 'package:telnow_mobile_new/src/model/refreshtoken/refresh_token_body.dart';
 import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
-import 'package:synchronized/synchronized.dart';
 
 import 'cache_manager.dart';
 import 'C.dart';
@@ -46,7 +45,7 @@ class U {
   static final Codec<String, String> stringToBase64Url = utf8.fuse(base64Url);
   static final JwtToken token = JwtToken();
 
-  static final _lockAuth = Lock();
+  // static final _lockAuth = Lock();
   static int _authLastMs = 0;
   static Token? _lastReqToken;
 
@@ -87,6 +86,7 @@ class U {
         Future.error(error);
       }
     }
+    return null;
   }
 
   static Future waitForRefreshAuth() async {
@@ -110,7 +110,7 @@ class U {
       var nw = DateTime.now().millisecondsSinceEpoch;
       if (_authLastMs == 0 || nw - _authLastMs >= 5000) {
         // print("refresh token request");
-        ApiAuthRepository apiAuthRepository = ApiAuthRepository();
+        // ApiAuthRepository apiAuthRepository = ApiAuthRepository();
         var token = await _tryRefreshAuth(refreshTokenBody);
         _authLastMs = DateTime.now().millisecondsSinceEpoch;
         _lastReqToken = token;
@@ -218,7 +218,7 @@ class U {
   static Future<dynamic> getUserData(context) async {
     if (_userData == null) {
       try {
-        _userData = await token.getUserData(context);
+        _userData = await token.getUserData();
       } catch (error) {
         print(error.toString());
         U.showDebugToast(error.toString());
@@ -392,7 +392,7 @@ class U {
   static Map _otherLabelList = {};
   static getOtherLabelList(BuildContext context) async{
     if(newServerVersion(1725245109)){
-      var res = await ApiAuthProvider().getData('/api/systemSettings/search/additional', {'key': 'request-for-other'}, context);
+      var res = await ApiAuthProvider().getData('/api/systemSettings/search/additional', {'key': 'request-for-other'});
       if(res != null){
         _otherLabelList = res;
       }
@@ -521,6 +521,17 @@ class U {
       ];
   }
 
+  static List<Color> loadingIndicatorColors() {
+      return [
+        Color(0xFF17BC91),
+        Color(0xFF72D3B7),
+        Color(0xFF3FE365),
+        Color(0xFFBAE33F),
+        Color(0xFFE3D53F),
+        Color(0xFFE33F3F)
+      ];
+  }
+
   static Widget iconsax(String name, {Color color = Colors.black, double size = 24}){
     return SvgPicture.asset(
       'assets/iconsax/${name}.svg',
@@ -587,7 +598,7 @@ class U {
         if(element['others']){
           data['req'][0]['uniqueId'] = element['uniqueId'];
           data['req'][0]['images'] = element['imageList'];
-          var res = await ApiAuthProvider().postData('/api/receptionists/send/request', null, data, context);
+          var res = await ApiAuthProvider().postData('/api/receptionists/send/request', null, data);
           if (res != null) {
             uniqueId.add(res['uniqueId']);
             // print('success send pending request others');
@@ -596,7 +607,7 @@ class U {
         else{
           data['uniqueId'] = element['uniqueId'];
           data['images'] = element['imageList'];
-          var res = await ApiAuthProvider().postData('/api/requestHistories/search/request/' + element['type'] + '/' + element['id'].toString() + '/' + element['noteFormat'] + '/submit', null, data, context);
+          var res = await ApiAuthProvider().postData('/api/requestHistories/search/request/${element['type']}/${element['id'].toString()}/${element['noteFormat']}/submit', null, data);
           if (res != null) {
             uniqueId.add(res['uniqueId']);
             // print('success send pending request');

+ 0 - 1
lib/src/utils/cache_manager.dart

@@ -1,7 +1,6 @@
 import 'package:cache_manager/cache_manager.dart';
 import 'package:flutter_cache_manager/flutter_cache_manager.dart';
 import 'package:shared_preferences/shared_preferences.dart';
-import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 
 class CacheMan {
   static void writeData(String key, dynamic data) {

+ 7 - 16
lib/src/utils/dio_logging_interceptors.dart

@@ -1,8 +1,8 @@
 import 'package:dio/dio.dart';
+import 'package:flutter/cupertino.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
 import 'package:telnow_mobile_new/src/model/refreshtoken/refresh_token_body.dart';
 import 'package:telnow_mobile_new/src/model/token/token.dart';
-import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/u.dart';
 import 'package:event_bus/event_bus.dart';
@@ -33,31 +33,22 @@ class DioLoggingInterceptors extends InterceptorsWrapper {
   }
 
   @override
-  void onError(DioError dioError, ErrorInterceptorHandler handler) async {
-    // print(dioError.response?.data.toString());
-    // print(dioError.response?.realUri.toString());
+  void onError(DioException err, ErrorInterceptorHandler handler) async {
     try {
-      if (dioError.response?.statusCode == 401) {
-        // print("auth error");
+      if (err.response?.statusCode == 401) {
         bool? checkRefreshCount = _sharedPreferencesManager.isKeyExists(SharedPreferencesManager.keyCountRefreshToken);
         int? refreshCount = _sharedPreferencesManager.getInt(SharedPreferencesManager.keyCountRefreshToken);
         if(checkRefreshCount! && refreshCount! > 2){
-          // handlingError(NavigationService.navigatorKey.currentContext, 3);
           eventBus.fire("logout");
           return;
         }
 
-        // if (dioError.response!.data.toString().contains("Invalid refresh token")) {
-        //   handlingError(NavigationService.navigatorKey.currentContext, 3);
-        // }
-        if (dioError.response!.data.toString().contains("Access token expired")) {
-          // print("token expired");
-
+        if (err.response!.data.toString().contains("Access token expired")) {
           String? refreshToken = _sharedPreferencesManager.getString(SharedPreferencesManager.keyRefreshToken)!;
           RefreshTokenBody? refreshTokenBody = RefreshTokenBody('refresh_token', refreshToken);
           Token? token = await U.refreshAuth(refreshTokenBody);
           if (token != null) {
-            RequestOptions? options = dioError.response?.requestOptions;
+            RequestOptions? options = err.response?.requestOptions;
             options?.headers.addAll({'requirestoken': true});
             try {
               var r = await _dio.fetch(options!);
@@ -65,7 +56,7 @@ class DioLoggingInterceptors extends InterceptorsWrapper {
               await _sharedPreferencesManager.putInt(SharedPreferencesManager.keyCountRefreshToken, 0);
               return;
             } catch (error2) {
-              print(error2);
+              debugPrint(error2.toString());
             }
           }
         }
@@ -76,6 +67,6 @@ class DioLoggingInterceptors extends InterceptorsWrapper {
       eventBus.fire("logout");
       // handlingError(NavigationService.navigatorKey.currentContext, 3);
     }
-    super.onError(dioError, handler);
+    super.onError(err, handler);
   }
 }

+ 45 - 29
lib/src/utils/provider.dart

@@ -3,7 +3,7 @@ import 'dart:typed_data';
 
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
-import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/layouts/functions/history.dart';
 
 class UserModule with ChangeNotifier {
   static String _serialNumber = '';
@@ -302,29 +302,31 @@ class ServiceModule with ChangeNotifier {
 
 class HistoryModule with ChangeNotifier {
   static int _page = 0;
-  static bool _isLoadHistory = false;
+  static Map<String, bool> _isLoadHistory = {};
   static bool _stopLoadHistory = false;
   static bool _multiSelectMode = false;
   static bool _setLoading = false;
   static List _selectedIndexes = [];
   static List _activeForum = [];
 
-  static List _data = [];
+  static Map<String, List> _data = {};
   static List _pendingData = [];
-  static int _pressAttention = 0;
-  static int _selectedFilter = 1;
+  static String _selectedFilter = 'none';
+  static String _currentPage = 'ongoing::none';
+  static HistoryTab _activeTab = HistoryTab.ongoing;
 
-  List dataMisi() => _data;
+  List dataRequests({String state = ''}) => _data[state != '' ? state : _currentPage]!;
   List dataPending() => _pendingData;
-  int pressAttention() => _pressAttention;
-  int selectedFilter() => _selectedFilter;
+  String selectedFilter() => _selectedFilter;
+  HistoryTab activeTab() => _activeTab;
   int page() => _page;
-  bool isLoadHistory() => _isLoadHistory;
+  bool isLoadHistory({String state = ''}) => _isLoadHistory[state != '' ? state : _currentPage]!;
   bool stopLoadHistory() => _stopLoadHistory;
   bool multiSelectMode() => _multiSelectMode;
   bool setLoading() => _setLoading;
   List selectedIndexes() => _selectedIndexes;
   List activeForum() => _activeForum;
+  String currentPage() => _currentPage;
 
   void addSelectedIndexes(i) {
     _selectedIndexes.add(i);
@@ -341,16 +343,21 @@ class HistoryModule with ChangeNotifier {
     notifyListeners();
   }
 
-  void setPressAttention(value) {
-    _pressAttention = value;
+  void setSelectedFilter(value) {
+    _selectedFilter = value;
     notifyListeners();
   }
 
-  void setSelectedFilter(value) {
-    _selectedFilter = value;
+  void setActiveTab(HistoryTab value) {
+    _activeTab = value;
     notifyListeners();
   }
 
+  void setCurrentPage() {
+    // debugPrint('called to set current page with value: ${_activeTab.name}::$_selectedFilter');
+    _currentPage = '${_activeTab.name}::$_selectedFilter';
+  }
+
   void setPage(value) {
     _page = value;
     notifyListeners();
@@ -361,8 +368,8 @@ class HistoryModule with ChangeNotifier {
     notifyListeners();
   }
 
-  void setLoadHistory(value) {
-    _isLoadHistory = value;
+  void setLoadHistory(bool value, String state) {
+    _isLoadHistory[state] = value;
     notifyListeners();
   }
 
@@ -381,13 +388,22 @@ class HistoryModule with ChangeNotifier {
     notifyListeners();
   }
 
-  void setMisi(list) {
-    _data.addAll(list);
+  void setDataRequests(String keyState, List list) {
+    // debugPrint('set data request => $keyState: ${list.length} data');
+    _data[keyState]!.addAll(list);
     notifyListeners();
   }
 
-  void clearMisi() {
-    _data.clear();
+  void initDataPages(Map<String, List> data){
+    _data = data;
+  }
+
+  void initIsLoadPages(Map<String, bool> data){
+    _isLoadHistory = data;
+  }
+
+  void clearDataRequests() {
+    _data[_currentPage]!.clear();
     notifyListeners();
   }
 
@@ -401,39 +417,39 @@ class HistoryModule with ChangeNotifier {
     notifyListeners();
   }
 
-  bool checkPressAttention(value){
-    return _pressAttention == value;
+  bool checkSelectedFilter(value){
+    return _selectedFilter == value;
   }
 
   int getMisiLength(){
-    return _data.length;
+    return _data[_currentPage]!.length;
   }
 
   void setSatisfaction(index, rate) {
-    _data[index]['satisfactionRate'] = rate;
+    _data[_currentPage]![index]['satisfactionRate'] = rate;
     notifyListeners();
   }
 
   void cancelMisi(index) {
-    _data.removeAt(index);
+    _data[_currentPage]!.removeAt(index);
     notifyListeners();
   }
 
   void setForumFalse(index) {
-    _data[index]['_hasForum'] = false;
+    _data[_currentPage]![index]['_hasForum'] = false;
     notifyListeners();
   }
 
   void reset(){
     _page = 0;
-    _isLoadHistory = false;
+    _isLoadHistory = {};
     _stopLoadHistory = false;
     _multiSelectMode = false;
     _setLoading = false;
-    _data = [];
+    _data = {};
     _pendingData = [];
-    _pressAttention = 0;
-    _selectedFilter = 1;
+    _selectedFilter = 'none';
+    _activeTab = HistoryTab.ongoing;
     _selectedIndexes = [];
     _activeForum = [];
   }

+ 297 - 0
lib/src/utils/ui_service.dart

@@ -0,0 +1,297 @@
+import 'dart:convert';
+import 'package:another_flushbar/flushbar.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:lottie/lottie.dart';
+import 'package:page_transition/page_transition.dart';
+import 'package:telnow_mobile_new/src/injector/injector.dart';
+import 'package:telnow_mobile_new/src/layouts/auth/qr.dart';
+import 'package:telnow_mobile_new/src/layouts/mobile/history_forum.dart';
+import 'package:telnow_mobile_new/src/layouts/mobile/message_list.dart';
+import 'U.dart';
+import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
+import 'package:telnow_mobile_new/src/api/jwt_token.dart';
+
+enum ErrorType {
+  noInternet, serverError, connectionError, invalidAccount, expiredAccount
+}
+final SharedPreferencesManager _sharedPreferencesManager = locator<SharedPreferencesManager>();
+final JwtToken token = JwtToken();
+
+class UIService {
+  static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
+
+  static BuildContext? get context => navigatorKey.currentContext;
+
+  static void goNotification(Map<String, dynamic> list) {
+    final ctx = context;
+    if (ctx == null) return;
+
+    if (list['type'] == 'MESSAGE') {
+      Navigator.push(
+        ctx,
+        PageTransition(
+          type: PageTransitionType.rightToLeft,
+          child: MobMessageListPage(null),
+        ),
+      );
+    } else if (list['type'] == 'FORUM') {
+      Navigator.push(
+        ctx,
+        PageTransition(
+          type: PageTransitionType.rightToLeft,
+          child: MobHistoryForumPage(
+            data: jsonDecode(list['requestHistory']),
+          ),
+        ),
+      );
+    } else {
+      final pid = U.getPidFromUrl(ctx.router.currentUrl);
+      ctx.router.removeLast();
+      ctx.navigateToPath("/app/$pid/menu/history");
+    }
+  }
+
+  static void navigateTo(Widget child) {
+    final ctx = context;
+    if (ctx != null) {
+      Navigator.push(ctx, PageTransition(type: PageTransitionType.rightToLeft, child: child));
+    }
+  }
+
+  static void navigateNamed(String routePath) {
+    final ctx = context;
+    if (ctx != null) {
+      AutoRouter.of(ctx).removeLast();
+      AutoRouter.of(ctx).navigatePath(routePath);
+    }
+  }
+
+  static void navigateBack({exc = false}){
+    final ctx = context;
+    if (ctx != null) {
+      Navigator.of(ctx).pop(exc);
+    }
+  }
+
+  static void showError(String message) {
+    final ctx = context;
+    if (ctx != null) {
+      Flushbar(
+        message: message,
+        icon: Icon(
+          Icons.info_outline,
+          size: 28.0,
+          color: Colors.red,
+        ),
+        duration: Duration(seconds: 5),
+        flushbarPosition: FlushbarPosition.BOTTOM,
+        margin: EdgeInsets.all(8),
+        borderRadius: BorderRadius.all(Radius.circular(8)),
+      ).show(ctx);
+    }
+  }
+
+  static void showSuccess(message) {
+    final ctx = context;
+    if (ctx != null) {
+      Flushbar(
+        message: message,
+        icon: Icon(
+          Icons.info_outline,
+          size: 28.0,
+          color: Colors.green,
+        ),
+        duration: Duration(seconds: 5),
+        flushbarPosition: FlushbarPosition.BOTTOM,
+        margin: EdgeInsets.all(8),
+        borderRadius: BorderRadius.all(Radius.circular(8)),
+      ).show(ctx);
+    }
+  }
+
+  static bool get isCurrentRouteInactive {
+    final ctx = context;
+    final route = ModalRoute.of(ctx ?? navigatorKey.currentState?.context ?? ctx!);
+    return route?.isCurrent != true;
+  }
+
+  static void pop() {
+    navigatorKey.currentState?.maybePop();
+  }
+
+  static void setLocale(code) {
+    final ctx = context;
+    if(ctx == null) return;
+    ctx.setLocale(code);
+  }
+
+  static String getLocale() {
+    return context!.locale.toString();
+  }
+
+  static void showLoading({String? text, String? lottie}){
+    final ctx = context;
+    if(ctx == null) return;
+    showDialog(
+      context: ctx,
+      barrierDismissible: false,
+      barrierColor: lottie != null ? Colors.white : null,
+        builder: (BuildContext context) {
+          return WillPopScope(
+            onWillPop: () => Future.value(false),
+            child: Material(
+              type: MaterialType.transparency,
+              child: Center(
+                child: Column(
+                  mainAxisSize: MainAxisSize.min,
+                  children: [
+                    lottie != null
+                        ? Lottie.asset('assets/image/lottie/$lottie', width: 250, height: 250, fit: BoxFit.fill)
+                        : CircularProgressIndicator(color: primaryColor),
+                    SizedBox(height: 5),
+                    Text(
+                      text ?? 'inProcess'.tr(),
+                      style: TextStyle(color: lottie != null ? Colors.black : Colors.white),
+                    ),
+                  ],
+                ),
+              ),
+            ),
+          );
+        }
+    );
+  }
+
+  static void closeLoading(){
+    Navigator.of(navigatorKey.currentContext!, rootNavigator: true).pop();
+  }
+
+  static void handlingError(ErrorType type) {
+    var data = [
+      {
+        'image': 'NoInternet.png',
+        'title': 'noInternetTitle'.tr(),
+        'description': 'noInternetDesc'.tr()
+      },
+      {
+        'image': 'ErrorServer.png',
+        'title': 'errorServerTitle'.tr(),
+        'description': 'errorServerDesc'.tr()
+      },
+      {
+        'image': 'ErrorConnection.png',
+        'title': 'errorConnectTitle'.tr(),
+        'description': 'errorConnectDesc'.tr()
+      },
+      {
+        'image': 'ErrorAuth.png',
+        'title': 'invalidAccountTitle'.tr(),
+        'description': 'invalidAccountDesc'.tr()
+      },
+      {
+        'image': 'ErrorAuth.png',
+        'title': 'expAccountTitle'.tr(),
+        'description': 'expAccountDesc'.tr()
+      }
+    ];
+    int idx = type.index;
+
+    final ctx = context;
+    if (ctx == null) return;
+
+    void clearSharedPreferencesKey(){
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyAccessCode);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keySerialCode);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyAccessToken);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyRefreshToken);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyUsername);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyIsLogin);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyScoope);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.debugString);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyHistoryMark);
+    }
+
+    showModalBottomSheet<void>(
+      context: ctx,
+      backgroundColor: Colors.white,
+      isDismissible: false,
+      enableDrag: false,
+      builder: (BuildContext context) {
+        return SingleChildScrollView(
+          child: Column(
+            mainAxisSize: MainAxisSize.min,
+            children: <Widget>[
+              Container(
+                padding: const EdgeInsets.fromLTRB(15, 10, 15, 0),
+                alignment: Alignment.centerRight,
+                child: GestureDetector(
+                  child: Icon(Icons.clear),
+                  onTap: () => Navigator.of(context).pop(),
+                ),
+              ),
+              Container(
+                width: 200,
+                padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
+                child: Image(
+                  image: AssetImage('assets/image/error/${data[idx]['image']!}'))),
+              Padding(
+                padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
+                child: Text(data[idx]['title']!,
+                  style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
+                  textAlign: TextAlign.center),
+              ),
+              Padding(
+                padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
+                child: Text(data[idx]['description']!,
+                  style: TextStyle(fontSize: 14),
+                  textAlign: TextAlign.center),
+              ),
+              if (idx > 2)
+              Padding(
+                padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
+                child: SizedBox(
+                  width: double.infinity,
+                  height: 45,
+                  child: TextButton(
+                    style: ButtonStyle(
+                      backgroundColor:
+                      WidgetStateProperty.all<Color>(primaryColor),
+                      shape: WidgetStateProperty.all<
+                        RoundedRectangleBorder>(
+                        RoundedRectangleBorder(
+                          borderRadius: BorderRadius.circular(5),
+                        ))),
+                    child: Text('logout'.tr().toUpperCase(),
+                      style: TextStyle(color: Colors.white),
+                      textAlign: TextAlign.center),
+                    onPressed: () {
+                      if (idx == 4) {
+                        clearSharedPreferencesKey();
+                        Navigator.pushAndRemoveUntil(
+                          context,
+                          MaterialPageRoute(
+                            builder: (context) => NewQrPage()),
+                          ModalRoute.withName("/home"));
+                      } else {
+                        var pid = U.getPidFromUrl(context.router.currentUrl);
+                        token.logout();
+                        context.router.removeLast();
+                        context.navigateToPath("/app/$pid/login");
+                      }
+                    },
+                  )),
+              )
+            ],
+          ),
+        );
+      });
+  }
+
+  static String getCurrentUrl(){
+    final ctx = context;
+    if(ctx == null) return '';
+    return ctx.router.currentUrl;
+  }
+}

+ 45 - 37
pubspec.lock

@@ -277,10 +277,10 @@ packages:
     dependency: transitive
     description:
       name: characters
-      sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
+      sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
       url: "https://pub.dev"
     source: hosted
-    version: "1.3.0"
+    version: "1.4.0"
   checked_yaml:
     dependency: transitive
     description:
@@ -293,10 +293,10 @@ packages:
     dependency: transitive
     description:
       name: clock
-      sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+      sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
       url: "https://pub.dev"
     source: hosted
-    version: "1.1.1"
+    version: "1.1.2"
   cloud_firestore:
     dependency: "direct main"
     description:
@@ -333,10 +333,10 @@ packages:
     dependency: transitive
     description:
       name: collection
-      sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
+      sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
       url: "https://pub.dev"
     source: hosted
-    version: "1.19.0"
+    version: "1.19.1"
   convert:
     dependency: transitive
     description:
@@ -493,10 +493,10 @@ packages:
     dependency: transitive
     description:
       name: fake_async
-      sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
+      sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
       url: "https://pub.dev"
     source: hosted
-    version: "1.3.1"
+    version: "1.3.3"
   ffi:
     dependency: transitive
     description:
@@ -814,13 +814,13 @@ packages:
     source: hosted
     version: "0.15.5"
   http:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: http
-      sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
+      sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
       url: "https://pub.dev"
     source: hosted
-    version: "1.3.0"
+    version: "1.5.0"
   http_multi_server:
     dependency: transitive
     description:
@@ -913,10 +913,10 @@ packages:
     dependency: transitive
     description:
       name: intl
-      sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
+      sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
       url: "https://pub.dev"
     source: hosted
-    version: "0.19.0"
+    version: "0.20.2"
   io:
     dependency: transitive
     description:
@@ -934,7 +934,7 @@ packages:
     source: hosted
     version: "0.7.1"
   json_annotation:
-    dependency: "direct main"
+    dependency: transitive
     description:
       name: json_annotation
       sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
@@ -949,6 +949,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.0.1"
+  lazy_load_indexed_stack:
+    dependency: "direct main"
+    description:
+      name: lazy_load_indexed_stack
+      sha256: "6026d0f1753ae0427587d6095f823143c1d3e4459c8640ebf0a808044ad8de1c"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.2.1"
   lazy_load_scrollview:
     dependency: "direct main"
     description:
@@ -961,26 +969,26 @@ packages:
     dependency: transitive
     description:
       name: leak_tracker
-      sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06"
+      sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
       url: "https://pub.dev"
     source: hosted
-    version: "10.0.7"
+    version: "11.0.2"
   leak_tracker_flutter_testing:
     dependency: transitive
     description:
       name: leak_tracker_flutter_testing
-      sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379"
+      sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.8"
+    version: "3.0.10"
   leak_tracker_testing:
     dependency: transitive
     description:
       name: leak_tracker_testing
-      sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
+      sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.1"
+    version: "3.0.2"
   linkify:
     dependency: transitive
     description:
@@ -1025,10 +1033,10 @@ packages:
     dependency: transitive
     description:
       name: matcher
-      sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
+      sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
       url: "https://pub.dev"
     source: hosted
-    version: "0.12.16+1"
+    version: "0.12.17"
   material_color_utilities:
     dependency: transitive
     description:
@@ -1041,10 +1049,10 @@ packages:
     dependency: transitive
     description:
       name: meta
-      sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
+      sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
       url: "https://pub.dev"
     source: hosted
-    version: "1.15.0"
+    version: "1.16.0"
   mime:
     dependency: transitive
     description:
@@ -1185,10 +1193,10 @@ packages:
     dependency: transitive
     description:
       name: path
-      sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
+      sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
       url: "https://pub.dev"
     source: hosted
-    version: "1.9.0"
+    version: "1.9.1"
   path_drawing:
     dependency: transitive
     description:
@@ -1409,10 +1417,10 @@ packages:
     dependency: "direct main"
     description:
       name: searchfield
-      sha256: "9d091c2731868926e2aeb9ac551d1b9116a4533a424373119509d754ae0d0f45"
+      sha256: c06d4e7b916472c60926a2b79656e0c55480d60a06aef542d1e8708567da90ad
       url: "https://pub.dev"
     source: hosted
-    version: "1.2.4"
+    version: "1.3.5"
   selectable_autolink_text:
     dependency: "direct main"
     description:
@@ -1582,18 +1590,18 @@ packages:
     dependency: transitive
     description:
       name: stack_trace
-      sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
+      sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
       url: "https://pub.dev"
     source: hosted
-    version: "1.12.0"
+    version: "1.12.1"
   stream_channel:
     dependency: transitive
     description:
       name: stream_channel
-      sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
+      sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.2"
+    version: "2.1.4"
   stream_transform:
     dependency: transitive
     description:
@@ -1630,10 +1638,10 @@ packages:
     dependency: transitive
     description:
       name: test_api
-      sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
+      sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
       url: "https://pub.dev"
     source: hosted
-    version: "0.7.3"
+    version: "0.7.6"
   timelines_plus:
     dependency: "direct main"
     description:
@@ -1798,10 +1806,10 @@ packages:
     dependency: transitive
     description:
       name: vector_math
-      sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+      sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.4"
+    version: "2.2.0"
   version:
     dependency: transitive
     description:
@@ -1939,5 +1947,5 @@ packages:
     source: hosted
     version: "3.1.1"
 sdks:
-  dart: ">=3.6.1 <4.0.0"
+  dart: ">=3.8.0-0 <4.0.0"
   flutter: ">=3.27.0"

+ 75 - 48
pubspec.yaml

@@ -1,5 +1,5 @@
 name: telnow_mobile_new
-description: "A new Flutter project."
+description: "TelNow application by Datacom Solusindo."
 # The following line prevents the package from being accidentally published to
 # pub.dev using `flutter pub publish`. This is preferred for private packages.
 publish_to: 'none' # Remove this line if you wish to publish to pub.dev
@@ -34,69 +34,96 @@ dependencies:
 
   # The following adds the Cupertino Icons font to your application.
   # Use with the CupertinoIcons class for iOS style icons.
+
+  # 🌱 Core Flutter & UI
   cupertino_icons: ^1.0.8
-  change_app_package_name: ^1.4.0
-  firebase_core: ^3.11.0
+  flutter_bloc: ^9.0.0
+  flutter_custom_clippers: ^2.1.0
+  flutter_datetime_picker_plus: ^2.2.0
+  flutter_linkify: ^6.0.0
+  flutter_local_notifications: ^19.3.0
+  flutter_native_splash: ^2.4.4
+  flutter_randomcolor: ^1.0.16
+  flutter_svg: ^2.2.0
+  fluttertoast: ^8.2.10
+  loading_indicator: ^3.1.1
+  lottie: ^3.3.1
+  page_transition: ^2.2.1
+  shimmer: ^3.0.0
+  toggle_switch: ^2.3.0
+
+  # 🔥 Firebase & Auth
   firebase_auth: ^5.4.1
+  firebase_core: ^3.11.0
   firebase_messaging: ^15.2.1
   cloud_firestore: ^5.6.2
+
+  # 🌐 Networking & API
   dio: ^5.7.0
-  shared_preferences: ^2.4.0
-  flutter_local_notifications: ^19.3.0
+  http: ^1.5.0
+
+  # 🧠 State Management & Utilities
   get_it: ^8.0.3
-  double_back_to_exit: ^1.2.1
-  page_transition: ^2.2.1
-  badges: ^3.1.2
-  shimmer: ^3.0.0
-  easy_localization: ^3.0.7
-  toggle_switch: ^2.3.0
-  json_annotation: ^4.9.0
-  flutter_randomcolor: ^1.0.16
-  photo_view: ^0.15.0
-  image_picker: ^1.1.2
-  path_provider: ^2.1.5
-  uuid: ^4.5.1
+  provider: ^6.1.2
+  event_bus: ^2.0.1
+  synchronized: ^3.3.0+3
+
+  # 📁 File & Media Handling
   camera: ^0.11.2
   camera_android: ^0.10.10+4
-  another_flushbar: ^1.12.30
-  fluttertoast: ^8.2.10
-  synchronized: ^3.3.0+3
+  file_picker: ^9.0.0
   image: ^4.5.2
-  dotted_line: ^3.2.3
-  provider: ^6.1.2
-  easy_refresh: ^3.4.0
-  upgrader: ^11.3.1
-  qr_flutter: ^4.1.0
-  app_settings: ^5.1.1
-  translator: ^1.0.3+1
-  lottie: ^3.3.1
+  image_picker: ^1.1.2
+  open_file: ^3.5.10
+  path_provider: ^2.1.5
+  photo_view: ^0.15.0
+
+  # 🧭 Navigation & Routing
   auto_route: ^10.0.1
-  permission_handler: ^11.3.1
-  quick_notify_2: ^0.3.0-dev.0
-  timelines_plus: ^1.0.6
-  flutter_bloc: ^9.0.0
-  flutter_custom_clippers: ^2.1.0
-  flutter_datetime_picker_plus: ^2.2.0
-  file_picker: ^9.0.0
-  loading_indicator: ^3.1.1
-  url_launcher: ^6.3.1
+
+  # 🌍 Localization & Translation
+  easy_localization: ^3.0.7
+  translator: ^1.0.3+1
+
+  # 📊 Caching & Persistence
   cache_manager: ^1.0.0+4
-  flutter_cache_manager: ^3.4.1
   cached_network_image: ^3.4.1
-  lazy_load_scrollview: ^1.3.0
-  drag_and_drop_lists: ^0.4.2
+  flutter_cache_manager: ^3.4.1
+  shared_preferences: ^2.4.0
+
+  # 🎨 UI Enhancements
+  badges: ^3.1.2
   carousel_slider: ^5.0.0
-  simple_connection_checker: ^0.3.4
-  flutter_linkify: ^6.0.0
-  flutter_svg: ^2.2.0
+  dotted_line: ^3.2.3
+  drag_and_drop_lists: ^0.4.2
+  lazy_load_scrollview: ^1.3.0
+  searchfield: ^1.2.5
   selectable_autolink_text: ^2.6.0
-  flutter_native_splash: ^2.4.4
-  searchfield: ^1.2.4
-  youtube_player_iframe: ^5.2.1
+  timelines_plus: ^1.0.6
+
+  # 📱 Device & Permissions
+  app_settings: ^5.1.1
   mobile_scanner: ^6.0.6
-  open_file: ^3.5.10
+  permission_handler: ^11.3.1
+  url_launcher: ^6.3.1
+
+  # 🔒 Auth & Identity
   jwt_decoder: ^2.0.1
-  event_bus: ^2.0.1
+  uuid: ^4.5.1
+
+  # 📦 QR & Barcode
+  qr_flutter: ^4.1.0
+
+  # 🧪 Experimental & Misc
+  another_flushbar: ^1.12.30
+  change_app_package_name: ^1.4.0
+  double_back_to_exit: ^1.2.1
+  easy_refresh: ^3.4.0
+  quick_notify_2: ^0.3.0-dev.0
+  simple_connection_checker: ^0.3.4
+  upgrader: ^11.3.1
+  youtube_player_iframe: ^5.2.1
+  lazy_load_indexed_stack: ^1.2.1
 
 dev_dependencies:
   flutter_test: