From eb2cbf1d04333a7bf9d1eef48da51463b03ba3c2 Mon Sep 17 00:00:00 2001 From: Aidaiym Date: Fri, 5 Jul 2024 12:43:41 +0600 Subject: [PATCH 01/11] added remote config --- app/ios/Podfile.lock | 28 +++++++++++++ app/lib/core/core.dart | 1 + .../core/remote_config/mq_remote_config.dart | 39 +++++++++++++++++++ app/lib/main.dart | 2 + app/pubspec.lock | 24 ++++++++++++ app/pubspec.yaml | 1 + 6 files changed, 95 insertions(+) create mode 100644 app/lib/core/remote_config/mq_remote_config.dart diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index 7d16f74f..0320476a 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -28,6 +28,9 @@ PODS: - Firebase/Crashlytics (10.25.0): - Firebase/CoreOnly - FirebaseCrashlytics (~> 10.25.0) + - Firebase/RemoteConfig (10.25.0): + - Firebase/CoreOnly + - FirebaseRemoteConfig (~> 10.25.0) - firebase_analytics (10.10.7): - Firebase/Analytics (= 10.25.0) - firebase_core @@ -43,6 +46,12 @@ PODS: - Firebase/Crashlytics (= 10.25.0) - firebase_core - Flutter + - firebase_remote_config (4.4.7): + - Firebase/RemoteConfig (= 10.25.0) + - firebase_core + - Flutter + - FirebaseABTesting (10.28.0): + - FirebaseCore (~> 10.0) - FirebaseAnalytics (10.25.0): - FirebaseAnalytics/AdIdSupport (= 10.25.0) - FirebaseCore (~> 10.0) @@ -91,6 +100,14 @@ PODS: - GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/UserDefaults (~> 7.8) - PromisesObjC (~> 2.1) + - FirebaseRemoteConfig (10.25.0): + - FirebaseABTesting (~> 10.0) + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - FirebaseRemoteConfigInterop (~> 10.23) + - FirebaseSharedSwift (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" - FirebaseRemoteConfigInterop (10.28.0) - FirebaseSessions (10.28.0): - FirebaseCore (~> 10.5) @@ -101,6 +118,7 @@ PODS: - GoogleUtilities/UserDefaults (~> 7.13) - nanopb (< 2.30911.0, >= 2.30908.0) - PromisesSwift (~> 2.1) + - FirebaseSharedSwift (10.28.0) - Flutter (1.0.0) - google_sign_in_ios (0.0.1): - AppAuth (>= 1.7.4) @@ -209,6 +227,7 @@ DEPENDENCIES: - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) + - firebase_remote_config (from `.symlinks/plugins/firebase_remote_config/ios`) - Flutter (from `Flutter`) - google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`) - just_audio (from `.symlinks/plugins/just_audio/ios`) @@ -223,6 +242,7 @@ SPEC REPOS: trunk: - AppAuth - Firebase + - FirebaseABTesting - FirebaseAnalytics - FirebaseAppCheckInterop - FirebaseAuth @@ -231,8 +251,10 @@ SPEC REPOS: - FirebaseCoreInternal - FirebaseCrashlytics - FirebaseInstallations + - FirebaseRemoteConfig - FirebaseRemoteConfigInterop - FirebaseSessions + - FirebaseSharedSwift - GoogleAppMeasurement - GoogleDataTransport - GoogleSignIn @@ -262,6 +284,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/firebase_core/ios" firebase_crashlytics: :path: ".symlinks/plugins/firebase_crashlytics/ios" + firebase_remote_config: + :path: ".symlinks/plugins/firebase_remote_config/ios" Flutter: :path: Flutter google_sign_in_ios: @@ -292,6 +316,8 @@ SPEC CHECKSUMS: firebase_auth: 59b73550b86d6213a4e9509a9c21b59ea524c647 firebase_core: a626d00494efa398e7c54f25f1454a64c8abf197 firebase_crashlytics: 17e856fabec68d993662abaf2f6fe2413f0abece + firebase_remote_config: 7b05c80210ab558c80f7a756681022b4ee98eea0 + FirebaseABTesting: 589bc28c0ab3e5554336895a34aa262e24276665 FirebaseAnalytics: ec00fe8b93b41dc6fe4a28784b8e51da0647a248 FirebaseAppCheckInterop: 5315f40293191bfec04b2cfab0215760e441540a FirebaseAuth: c0f93dcc570c9da2bffb576969d793e95c344fbb @@ -300,8 +326,10 @@ SPEC CHECKSUMS: FirebaseCoreInternal: 58d07f1362fddeb0feb6a857d1d1d1c5e558e698 FirebaseCrashlytics: 4b96efb0ce73b38b2a85e8b8bd1bd8f63f09d015 FirebaseInstallations: 60c1d3bc1beef809fd1ad1189a8057a040c59f2e + FirebaseRemoteConfig: 9f3935cefecd85d5b312192117f444957de24a75 FirebaseRemoteConfigInterop: 70d200c6956ef3b5c3592a95e824c1210682d785 FirebaseSessions: 20da8500ad66bb12622743e170459bf62a0768e8 + FirebaseSharedSwift: 48de4aec81a6b79bb30404e5e6db43ea74848fed Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 google_sign_in_ios: 07375bfbf2620bc93a602c0e27160d6afc6ead38 GoogleAppMeasurement: 9abf64b682732fed36da827aa2a68f0221fd2356 diff --git a/app/lib/core/core.dart b/app/lib/core/core.dart index e21feb9b..5b98d50d 100644 --- a/app/lib/core/core.dart +++ b/app/lib/core/core.dart @@ -10,3 +10,4 @@ export 'exceptions/convert_exception.dart'; export 'launch/app_launch.dart'; export 'enums/fetch_status.dart'; export 'analytics/mq_analytic.dart'; +export 'remote_config/mq_remote_config.dart'; diff --git a/app/lib/core/remote_config/mq_remote_config.dart b/app/lib/core/remote_config/mq_remote_config.dart new file mode 100644 index 00000000..e70a2e93 --- /dev/null +++ b/app/lib/core/remote_config/mq_remote_config.dart @@ -0,0 +1,39 @@ +import 'dart:developer'; + +import 'package:firebase_remote_config/firebase_remote_config.dart'; +import 'package:flutter/foundation.dart'; +import 'package:my_quran/core/core.dart'; + +@immutable +final class MqRemoteConfig { + const MqRemoteConfig._(); + + static Future initialize() async { + final remoteConfig = FirebaseRemoteConfig.instance; + + try { + await remoteConfig.setDefaults({ + 'version': '1.3.1', + 'build_number': 10, + 'update_required': false, + }); + await remoteConfig.fetchAndActivate(); + + _listenForUpdates(remoteConfig); + } catch (error, stackTrace) { + MqCrashlytics.report(error, stackTrace); + log('MqRemoteConfig initialize error: $error, stackTrace: $stackTrace'); + } + } + + static String get latestVersion => FirebaseRemoteConfig.instance.getString('version'); + static int get buildNumber => FirebaseRemoteConfig.instance.getInt('build_number'); + static bool get updateRequired => FirebaseRemoteConfig.instance.getBool('update_required'); + + static void _listenForUpdates(FirebaseRemoteConfig remoteConfig) { + remoteConfig.onConfigUpdated.listen((event) async { + await remoteConfig.activate(); + log('MqRemoteConfig: Config updated and activated'); + }); + } +} diff --git a/app/lib/main.dart b/app/lib/main.dart index 25f41ac0..e1c7df76 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -26,6 +26,8 @@ Future main({AppConfig? appConfig}) async { ); await FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(true); + await MqRemoteConfig.initialize(); + await MqCrashlytics.setCrashlyticsCollectionEnabled(enabled: kDebugMode); FlutterError.onError = MqCrashlytics.recordFlutterError; diff --git a/app/pubspec.lock b/app/pubspec.lock index 221e11aa..e86274f4 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -425,6 +425,30 @@ packages: url: "https://pub.dev" source: hosted version: "3.6.35" + firebase_remote_config: + dependency: "direct main" + description: + name: firebase_remote_config + sha256: "653bd94b68e2c4e89eca10db90576101f1024151f39f2d4e7c64ae6a90a5f9c5" + url: "https://pub.dev" + source: hosted + version: "4.4.7" + firebase_remote_config_platform_interface: + dependency: transitive + description: + name: firebase_remote_config_platform_interface + sha256: "24a2c445b15de3af7e4582ebceb2aa9a1e3731d0202cb3e7a1e03012440fa07d" + url: "https://pub.dev" + source: hosted + version: "1.4.35" + firebase_remote_config_web: + dependency: transitive + description: + name: firebase_remote_config_web + sha256: "525aa3000fd27cd023841c802010a06515e564aab2f147aa964b35f54abbf449" + url: "https://pub.dev" + source: hosted + version: "1.6.7" fixnum: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 542d5fa3..8b6d6422 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -43,6 +43,7 @@ dependencies: path: ../packages/mq_ci_keys/ firebase_crashlytics: ^3.5.7 firebase_analytics: ^10.10.7 + firebase_remote_config: ^4.4.7 # flutter_native_splash: ^2.4.0 dev_dependencies: From 8b3e221b29467ffa90366f4af987fc172711b568 Mon Sep 17 00:00:00 2001 From: Aidaiym Date: Sat, 6 Jul 2024 19:59:43 +0600 Subject: [PATCH 02/11] integrate remote config --- app/lib/app/presentation/cubit/app_cubit.dart | 19 ++++- app/lib/app/presentation/cubit/app_state.dart | 46 +++++++++-- app/lib/app/presentation/presenation.dart | 1 + .../app/presentation/view/app_listener.dart | 80 +++++++++++++++++++ app/lib/app/presentation/view/app_view.dart | 49 +++++++++--- app/lib/config/router/app_router.dart | 8 +- .../core/remote_config/mq_remote_config.dart | 79 +++++++++++------- app/lib/l10n/arb/app_ar.arb | 7 +- app/lib/l10n/arb/app_en.arb | 7 +- app/lib/l10n/arb/app_id.arb | 7 +- app/lib/l10n/arb/app_kk.arb | 8 +- app/lib/l10n/arb/app_ky.arb | 9 ++- app/lib/l10n/arb/app_ru.arb | 7 +- app/lib/l10n/arb/app_tr.arb | 7 +- app/lib/main.dart | 11 ++- 15 files changed, 285 insertions(+), 60 deletions(-) create mode 100644 app/lib/app/presentation/view/app_listener.dart diff --git a/app/lib/app/presentation/cubit/app_cubit.dart b/app/lib/app/presentation/cubit/app_cubit.dart index 9445587e..70d7ad38 100644 --- a/app/lib/app/presentation/cubit/app_cubit.dart +++ b/app/lib/app/presentation/cubit/app_cubit.dart @@ -15,8 +15,9 @@ class AppCubit extends Cubit { required this.getAppVersionUseCase, }) : super( AppState( - getInitialThemeUseCase.call, - getAppVersionUseCase.call, + theme: getInitialThemeUseCase.call, + appVersionStatus: const NoNewVersion(), + appVersion: getAppVersionUseCase.call, ), ); @@ -40,4 +41,18 @@ class AppCubit extends Cubit { await setColorUseCase(index, color); emit(state.copyWith(theme: state.theme.copyWith(targetColor: color))); } + + Future setAppVersionStatus({ + required int requiredBuildNumber, + required int recommendedBuildNumber, + required int currentBuildNumber, + }) async { + if (currentBuildNumber < requiredBuildNumber) { + emit(state.copyWith(appVersionStatus: YesRequiredVersion(requiredBuildNumber))); + } else if (currentBuildNumber < recommendedBuildNumber) { + emit(state.copyWith(appVersionStatus: YesRecommendedVersion(recommendedBuildNumber))); + } else { + emit(state.copyWith(appVersionStatus: const NoNewVersion())); + } + } } diff --git a/app/lib/app/presentation/cubit/app_state.dart b/app/lib/app/presentation/cubit/app_state.dart index 79f6387a..dad4921b 100644 --- a/app/lib/app/presentation/cubit/app_state.dart +++ b/app/lib/app/presentation/cubit/app_state.dart @@ -1,24 +1,56 @@ part of 'app_cubit.dart'; class AppState extends Equatable { - const AppState( - this.theme, - this.appVersion, - ); + const AppState({ + required this.theme, + required this.appVersionStatus, + required this.appVersion, + this.deviceId, + }); final CustomTheme theme; + final String? deviceId; + final AppVersionStatus appVersionStatus; final String appVersion; @override - List get props => [theme, appVersion]; + List get props => [theme, deviceId, appVersionStatus]; AppState copyWith({ CustomTheme? theme, String? appVersion, + String? deviceId, + AppVersionStatus? appVersionStatus, }) { return AppState( - theme ?? this.theme, - appVersion ?? this.appVersion, + theme: theme ?? this.theme, + deviceId: deviceId ?? this.deviceId, + appVersionStatus: appVersionStatus ?? this.appVersionStatus, + appVersion: appVersion ?? this.appVersion, ); } } + +@immutable +sealed class AppVersionStatus { + const AppVersionStatus(); +} + +@immutable +final class NoNewVersion extends AppVersionStatus { + const NoNewVersion(); +} + +@immutable +final class YesRecommendedVersion extends AppVersionStatus { + const YesRecommendedVersion(this.buildNumber); + + final int buildNumber; +} + +@immutable +final class YesRequiredVersion extends AppVersionStatus { + const YesRequiredVersion(this.buildNumber); + + final int buildNumber; +} diff --git a/app/lib/app/presentation/presenation.dart b/app/lib/app/presentation/presenation.dart index 58eb425b..7758b424 100644 --- a/app/lib/app/presentation/presenation.dart +++ b/app/lib/app/presentation/presenation.dart @@ -1,3 +1,4 @@ export 'cubit/app_cubit.dart'; export 'cubit/auth_cubit.dart'; export 'view/app_view.dart'; +export 'view/app_listener.dart'; diff --git a/app/lib/app/presentation/view/app_listener.dart b/app/lib/app/presentation/view/app_listener.dart new file mode 100644 index 00000000..5c6655eb --- /dev/null +++ b/app/lib/app/presentation/view/app_listener.dart @@ -0,0 +1,80 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:my_quran/app/app.dart'; +import 'package:my_quran/core/core.dart'; +import 'package:my_quran/l10n/l10.dart'; + +class AppListener extends StatelessWidget { + const AppListener({ + required this.child, + required this.navigationKey, + super.key, + }); + + final Widget child; + final GlobalKey navigationKey; + + @override + Widget build(BuildContext context) { + return BlocListener( + listenWhen: (p, c) => p.appVersionStatus != c.appVersionStatus, + listener: (context, state) { + if (state.appVersionStatus is YesRecommendedVersion) { + _showUpdateDialog(context); + } else if (state.appVersionStatus is YesRequiredVersion) { + _showUpdateDialog(context, forceUpdate: true); + } + }, + child: child, + ); + } + + void _showUpdateDialog(BuildContext context, {bool forceUpdate = false}) { + final ctx = navigationKey.currentState!.overlay!.context; + showAdaptiveDialog( + context: ctx, + barrierDismissible: !forceUpdate, + builder: (ctx) { + return PopScope( + canPop: !forceUpdate, + child: AlertDialog.adaptive( + title: Text( + ctx.l10n.appUpdateAvailable, + style: Theme.of(context).textTheme.titleMedium, + ), + content: Text( + forceUpdate ? ctx.l10n.requiredVersionDescription : ctx.l10n.recommendedVersionDescription, + ), + actions: [ + if (!forceUpdate) + TextButton( + onPressed: () => Navigator.pop(ctx), + child: Text( + ctx.l10n.remindMeLater, + textAlign: TextAlign.center, + ), + ), + TextButton( + onPressed: () => AppLaunch.launchURL(_getPlatformAppStoreLink), + child: Text( + ctx.l10n.updateNow, + textAlign: TextAlign.center, + ), + ), + ], + ), + ); + }, + ); + } + + String get _getPlatformAppStoreLink { + if (Platform.isAndroid) { + return 'https://play.google.com/store/apps/details?id=com.alee.my_quran&hl=en'; + } else { + return 'https://apps.apple.com/kg/app/my-quran/id1671645027'; + } + } +} diff --git a/app/lib/app/presentation/view/app_view.dart b/app/lib/app/presentation/view/app_view.dart index f1d8d123..e533cedf 100644 --- a/app/lib/app/presentation/view/app_view.dart +++ b/app/lib/app/presentation/view/app_view.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:just_audio/just_audio.dart'; import 'package:loader_overlay/loader_overlay.dart'; @@ -84,21 +85,51 @@ class MyApp extends StatelessWidget { } } -class QuranApp extends StatelessWidget { +class QuranApp extends StatefulWidget { const QuranApp({super.key}); + @override + State createState() => _QuranAppState(); +} + +class _QuranAppState extends State { + @override + void initState() { + super.initState(); + SchedulerBinding.instance.addPostFrameCallback((_) async { + await Future.delayed(const Duration(seconds: 2)); + _listenRemoteConfig(); + }); + } + @override Widget build(BuildContext context) { return GlobalLoaderOverlay( - child: MaterialApp.router( - title: 'MyQuranKhatm', - debugShowCheckedModeBanner: false, - locale: context.watch().state.currentLocale, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - theme: context.watch().state.theme.themeData, - routerConfig: AppRouter.router, + child: AppListener( + navigationKey: rootNavigatorKey, + child: MaterialApp.router( + title: 'MyQuranKhatm', + debugShowCheckedModeBanner: false, + locale: context.watch().state.currentLocale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + theme: context.watch().state.theme.themeData, + routerConfig: AppRouter.router, + ), ), ); } + + void _listenRemoteConfig() { + final remoteConfig = context.read(); + context.read().setAppVersionStatus( + requiredBuildNumber: remoteConfig.requiredBuildNumber, + recommendedBuildNumber: remoteConfig.recommendedBuildNumber, + currentBuildNumber: remoteConfig.currentBuildNumber, + ); + remoteConfig.remoteConfig.onConfigUpdated.listen((event) async { + await remoteConfig.remoteConfig.activate(); + setState(() {}); + }); + } } diff --git a/app/lib/config/router/app_router.dart b/app/lib/config/router/app_router.dart index bbaee742..c14ac9d5 100644 --- a/app/lib/config/router/app_router.dart +++ b/app/lib/config/router/app_router.dart @@ -7,7 +7,7 @@ import 'package:my_quran/app/app.dart'; import 'package:my_quran/config/config.dart'; import 'package:my_quran/modules/modules.dart'; -final _rootNavigatorKey = GlobalKey(); +final rootNavigatorKey = GlobalKey(); final _sectionNavigatorKey1 = GlobalKey(debugLabel: 'home'); final _sectionNavigatorKey2 = GlobalKey(debugLabel: 'quran'); final _sectionNavigatorKey3 = GlobalKey(debugLabel: 'quran-audio'); @@ -38,7 +38,7 @@ final class AppRouter { static final router = GoRouter( initialLocation: '/home', - navigatorKey: _rootNavigatorKey, + navigatorKey: rootNavigatorKey, debugLogDiagnostics: kDebugMode, routes: [ GoRoute( @@ -132,7 +132,7 @@ final class AppRouter { GoRoute( path: '$hatimRead/:isHatim/:pages', name: hatimRead, - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, builder: (context, state) { final args = ParseParams.parseRead(state.pathParameters); return ReadView(args.$1, isHatim: args.$2); @@ -148,7 +148,7 @@ final class AppRouter { GoRoute( path: '$read/:isHatim/:pages', name: read, - parentNavigatorKey: _rootNavigatorKey, + parentNavigatorKey: rootNavigatorKey, builder: (context, state) { final args = ParseParams.parseRead(state.pathParameters); return ReadView(args.$1, isHatim: args.$2); diff --git a/app/lib/core/remote_config/mq_remote_config.dart b/app/lib/core/remote_config/mq_remote_config.dart index e70a2e93..a48e78a9 100644 --- a/app/lib/core/remote_config/mq_remote_config.dart +++ b/app/lib/core/remote_config/mq_remote_config.dart @@ -1,39 +1,62 @@ -import 'dart:developer'; +import 'dart:convert'; +import 'dart:io'; import 'package:firebase_remote_config/firebase_remote_config.dart'; import 'package:flutter/foundation.dart'; -import 'package:my_quran/core/core.dart'; @immutable final class MqRemoteConfig { - const MqRemoteConfig._(); - - static Future initialize() async { - final remoteConfig = FirebaseRemoteConfig.instance; - - try { - await remoteConfig.setDefaults({ - 'version': '1.3.1', - 'build_number': 10, - 'update_required': false, - }); - await remoteConfig.fetchAndActivate(); - - _listenForUpdates(remoteConfig); - } catch (error, stackTrace) { - MqCrashlytics.report(error, stackTrace); - log('MqRemoteConfig initialize error: $error, stackTrace: $stackTrace'); - } + const MqRemoteConfig({ + required this.remoteConfig, + required this.buildNumber, + }); + + final FirebaseRemoteConfig remoteConfig; + final String buildNumber; + + Future initialise() async { + await remoteConfig.setConfigSettings( + RemoteConfigSettings( + fetchTimeout: const Duration(minutes: 1), + minimumFetchInterval: const Duration(hours: 1), + ), + ); + + await remoteConfig.setDefaults(_defaultParams(currentBuildNumber)); + await remoteConfig.fetchAndActivate(); + } + + int get currentBuildNumber => int.tryParse(buildNumber) ?? 100; + int get requiredBuildNumber => version.$1; + int get recommendedBuildNumber => version.$2; + + (int, int) get version { + final data = jsonDecode(remoteConfig.getString(_appVersion)) as Map; + final platformName = Platform.isIOS ? 'ios' : 'android'; + final versionData = data[platformName] as Map; + return (versionData['requiredBuildNumber'], versionData['recommendedBuildNumber']); } - static String get latestVersion => FirebaseRemoteConfig.instance.getString('version'); - static int get buildNumber => FirebaseRemoteConfig.instance.getInt('build_number'); - static bool get updateRequired => FirebaseRemoteConfig.instance.getBool('update_required'); + static const _enableHatim = 'enableHatim'; + static const _appVersion = 'appVersion'; + + static Map _defaultAppVersionValue(int currentBuildNumber) { + return { + 'android': { + 'requiredBuildNumber': currentBuildNumber, + 'recommendedBuildNumber': currentBuildNumber, + }, + 'ios': { + 'requiredBuildNumber': currentBuildNumber, + 'recommendedBuildNumber': currentBuildNumber, + }, + }; + } - static void _listenForUpdates(FirebaseRemoteConfig remoteConfig) { - remoteConfig.onConfigUpdated.listen((event) async { - await remoteConfig.activate(); - log('MqRemoteConfig: Config updated and activated'); - }); + static Map _defaultParams(int currentBuildNumber) { + return { + _enableHatim: true, + _appVersion: jsonEncode(_defaultAppVersionValue(currentBuildNumber)), + }; } } diff --git a/app/lib/l10n/arb/app_ar.arb b/app/lib/l10n/arb/app_ar.arb index 5e975fe2..a3c2bf88 100644 --- a/app/lib/l10n/arb/app_ar.arb +++ b/app/lib/l10n/arb/app_ar.arb @@ -81,5 +81,10 @@ "signOutContext": "هل أنت متأكد أنك تريد تسجيل الخروج؟", "yes": "نعم", "no": "لا", - "appleSignInNotAvailable": "تسجيل الدخول عبر Apple غير متاح على أجهزة Android." + "appleSignInNotAvailable": "تسجيل الدخول عبر Apple غير متاح على أجهزة Android.", + "appUpdateAvailable": "تحديث التطبيق متاح", + "requiredVersionDescription": "نسخة جديدة من التطبيق متاحة. يجب عليك تحديثه للمتابعة.", + "recommendedVersionDescription": "نسخة جديدة من التطبيق متاحة.", + "remindMeLater": "ذكرني لاحقاً", + "updateNow": "تحديث الآن" } \ No newline at end of file diff --git a/app/lib/l10n/arb/app_en.arb b/app/lib/l10n/arb/app_en.arb index d8293876..351fdfa3 100644 --- a/app/lib/l10n/arb/app_en.arb +++ b/app/lib/l10n/arb/app_en.arb @@ -81,5 +81,10 @@ "signOutContext": "Are you sure you want to logout?", "yes": "Yes", "no": "No", - "appleSignInNotAvailable": "Apple Sign-In is not available on Android devices." + "appleSignInNotAvailable": "Apple Sign-In is not available on Android devices.", + "appUpdateAvailable":"App update available", + "requiredVersionDescription": "A new version of the app is available. You need to update it to continue.", + "recommendedVersionDescription": "A new version of the app is available.", + "remindMeLater": "Remind me later", + "updateNow": "Update now" } \ No newline at end of file diff --git a/app/lib/l10n/arb/app_id.arb b/app/lib/l10n/arb/app_id.arb index 65b9381c..e9c19e8e 100644 --- a/app/lib/l10n/arb/app_id.arb +++ b/app/lib/l10n/arb/app_id.arb @@ -81,5 +81,10 @@ "signOutContext": "Apakah Anda yakin ingin keluar?", "yes": "Ya", "no": "Tidak", - "appleSignInNotAvailable": "Masuk dengan Apple tidak tersedia di perangkat Android." + "appleSignInNotAvailable": "Masuk dengan Apple tidak tersedia di perangkat Android.", + "appUpdateAvailable": "Pembaruan aplikasi tersedia", + "requiredVersionDescription": "Versi baru dari aplikasi tersedia. Anda perlu memperbarui untuk melanjutkan.", + "recommendedVersionDescription": "Versi baru dari aplikasi tersedia.", + "remindMeLater": "Ingatkan saya nanti", + "updateNow": "Perbarui sekarang" } \ No newline at end of file diff --git a/app/lib/l10n/arb/app_kk.arb b/app/lib/l10n/arb/app_kk.arb index ad2c7e06..3e6cbfce 100644 --- a/app/lib/l10n/arb/app_kk.arb +++ b/app/lib/l10n/arb/app_kk.arb @@ -81,5 +81,11 @@ "signOutContext": "Шығыңыз келетініне сенімдісіз бе?", "yes": "Иә", "no": "Жоқ", - "appleSignInNotAvailable": "Apple жүйесіне кіру Android құрылғыларында қол жетімді емес." + "appleSignInNotAvailable": "Apple жүйесіне кіру Android құрылғыларында қол жетімді емес.", + "appUpdateAvailable": "Қолданба жаңарту қолданысқа қойылатын", + "requiredVersionDescription": "Қолданбаның жаңа нұсқасы қолайлы. Жалғастыру үшін оны жаңартуыңыз керек.", + "recommendedVersionDescription": "Қолданбаның жаңа нұсқасы қолайлы.", + "remindMeLater": "Кейін еске салу", + "updateNow": "Қазір жаңарту" + } \ No newline at end of file diff --git a/app/lib/l10n/arb/app_ky.arb b/app/lib/l10n/arb/app_ky.arb index 4bb862a8..8625e4c9 100644 --- a/app/lib/l10n/arb/app_ky.arb +++ b/app/lib/l10n/arb/app_ky.arb @@ -81,5 +81,10 @@ "signOutContext": "Чын эле чыгып кеткиңиз келеби?", "yes": "Ооба", "no": "Жок", - "appleSignInNotAvailable": "Apple аркылуу кирүү Android системаларында жеткиликтүү эмес." -} \ No newline at end of file + "appleSignInNotAvailable": "Apple аркылуу кирүү Android системаларында жеткиликтүү эмес.", + "appUpdateAvailable": "Колдонмону жаңыртуу жеткиликтүү", + "requiredVersionDescription": "Колдонмонун жаңы версиясы жеткиликтүү. Улантуу үчүн аны жаңыртышыңыз керек.", + "recommendedVersionDescription": "Колдонмонун жаңы версиясы жеткиликтүү", + "remindMeLater": "Кийинчерээк эстетип коюу", + "updateNow": "Азыр жаңыртуу" + } \ No newline at end of file diff --git a/app/lib/l10n/arb/app_ru.arb b/app/lib/l10n/arb/app_ru.arb index 20a57914..6e26912a 100644 --- a/app/lib/l10n/arb/app_ru.arb +++ b/app/lib/l10n/arb/app_ru.arb @@ -81,5 +81,10 @@ "signOutContext": "Вы уверены, что хотите выйти?", "yes": "Да", "no": "Нет", - "appleSignInNotAvailable": "Вход с Apple недоступен на устройствах Android." + "appleSignInNotAvailable": "Вход с Apple недоступен на устройствах Android.", + "appUpdateAvailable": "Доступно обновление приложения", + "requiredVersionDescription": "Доступна новая версия приложения. Вам необходимо обновить его, чтобы продолжить использование.", + "recommendedVersionDescription": "Доступна новая версия приложения.", + "remindMeLater": "Напомнить позже", + "updateNow": "Обновить сейчас" } \ No newline at end of file diff --git a/app/lib/l10n/arb/app_tr.arb b/app/lib/l10n/arb/app_tr.arb index f6157833..36c7ae39 100644 --- a/app/lib/l10n/arb/app_tr.arb +++ b/app/lib/l10n/arb/app_tr.arb @@ -81,5 +81,10 @@ "signOutContext": "Çıkış yapmak istediğinizden emin misiniz?", "yes": "Evet", "no": "Hayır", - "appleSignInNotAvailable": "Apple ile giriş Android cihazlarda mevcut değil." + "appleSignInNotAvailable": "Apple ile giriş Android cihazlarda mevcut değil.", + "appUpdateAvailable": "Uygulama güncellemesi mevcut", + "requiredVersionDescription": "Uygulamanın yeni bir sürümü mevcut. Devam etmek için güncellemelisiniz.", + "recommendedVersionDescription": "Uygulamanın yeni bir sürümü mevcut.", + "remindMeLater": "Daha sonra hatırlat", + "updateNow": "Şimdi güncelle" } \ No newline at end of file diff --git a/app/lib/main.dart b/app/lib/main.dart index e1c7df76..405b3419 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_remote_config/firebase_remote_config.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -26,8 +27,6 @@ Future main({AppConfig? appConfig}) async { ); await FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(true); - await MqRemoteConfig.initialize(); - await MqCrashlytics.setCrashlyticsCollectionEnabled(enabled: kDebugMode); FlutterError.onError = MqCrashlytics.recordFlutterError; @@ -50,8 +49,15 @@ Future main({AppConfig? appConfig}) async { Bloc.observer = const AppBlocObserver(onLog: log); final storage = await PreferencesStorage.getInstance(); + final packageInfo = await PackageInfo.fromPlatform(); + final remoteConfig = MqRemoteConfig( + remoteConfig: FirebaseRemoteConfig.instance, + buildNumber: packageInfo.buildNumber, + ); + + await remoteConfig.initialise(); appConfig ??= AppConfig(storage: storage); appConfig.init(); @@ -65,6 +71,7 @@ Future main({AppConfig? appConfig}) async { RepositoryProvider( create: (context) => NetworkClient(Connectivity()), ), + RepositoryProvider(create: (context) => remoteConfig), RepositoryProvider( create: (context) => RemoteClient(Client(), context.read()), ), From 2aa93b6905f283087fdcf54c3038a4cc87ddd3d5 Mon Sep 17 00:00:00 2001 From: Aidaiym Date: Sat, 6 Jul 2024 21:13:32 +0600 Subject: [PATCH 03/11] add enable hatim bool --- app/lib/core/remote_config/mq_remote_config.dart | 2 ++ app/lib/modules/home/presentation/view/home_view.dart | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/app/lib/core/remote_config/mq_remote_config.dart b/app/lib/core/remote_config/mq_remote_config.dart index a48e78a9..6d794e82 100644 --- a/app/lib/core/remote_config/mq_remote_config.dart +++ b/app/lib/core/remote_config/mq_remote_config.dart @@ -59,4 +59,6 @@ final class MqRemoteConfig { _appVersion: jsonEncode(_defaultAppVersionValue(currentBuildNumber)), }; } + + bool get isHatimEnabled => remoteConfig.getBool(_enableHatim); } diff --git a/app/lib/modules/home/presentation/view/home_view.dart b/app/lib/modules/home/presentation/view/home_view.dart index c461bf69..bed49746 100644 --- a/app/lib/modules/home/presentation/view/home_view.dart +++ b/app/lib/modules/home/presentation/view/home_view.dart @@ -103,8 +103,16 @@ class HomeBody extends StatelessWidget { key: const Key(MqKeys.participantToHatim), text: l10n.homeGoHatim, onPressed: () { + // if (const MqRemoteConfig().isHatimEnabled) { MqAnalytic.track(AnalyticKey.goHatim); context.goNamed(AppRouter.hatim); + // } else { + // ScaffoldMessenger.of(context).showSnackBar( + // const SnackBar( + // content: Text('Hatim is not available.'), + // ), + // ); + // } }, ), ), From ea32cce5fe0ac86fd4c675ddb0e70e9f8188c1c0 Mon Sep 17 00:00:00 2001 From: Aidaiym Date: Mon, 8 Jul 2024 15:48:43 +0600 Subject: [PATCH 04/11] fix unit test --- app/lib/app/presentation/cubit/app_cubit.dart | 20 +------- app/lib/app/presentation/cubit/app_state.dart | 46 +++-------------- .../cubit/remote_config_cubit.dart | 23 +++++++++ .../cubit/remote_config_state.dart | 49 +++++++++++++++++++ app/lib/app/presentation/presenation.dart | 1 + .../app/presentation/view/app_listener.dart | 2 +- app/lib/app/presentation/view/app_view.dart | 9 ++-- .../core/remote_config/mq_remote_config.dart | 8 +-- app/lib/l10n/arb/app_ar.arb | 3 +- app/lib/l10n/arb/app_en.arb | 3 +- app/lib/l10n/arb/app_id.arb | 3 +- app/lib/l10n/arb/app_kk.arb | 4 +- app/lib/l10n/arb/app_ky.arb | 3 +- app/lib/l10n/arb/app_ru.arb | 3 +- app/lib/l10n/arb/app_tr.arb | 3 +- .../home/presentation/view/home_view.dart | 20 ++++---- app/test/helpers/pump_app.dart | 6 +++ app/test/widget_test.dart | 36 ++++++++++++++ 18 files changed, 158 insertions(+), 84 deletions(-) create mode 100644 app/lib/app/presentation/cubit/remote_config_cubit.dart create mode 100644 app/lib/app/presentation/cubit/remote_config_state.dart diff --git a/app/lib/app/presentation/cubit/app_cubit.dart b/app/lib/app/presentation/cubit/app_cubit.dart index 70d7ad38..855f1d26 100644 --- a/app/lib/app/presentation/cubit/app_cubit.dart +++ b/app/lib/app/presentation/cubit/app_cubit.dart @@ -14,11 +14,7 @@ class AppCubit extends Cubit { required this.setColorUseCase, required this.getAppVersionUseCase, }) : super( - AppState( - theme: getInitialThemeUseCase.call, - appVersionStatus: const NoNewVersion(), - appVersion: getAppVersionUseCase.call, - ), + AppState(getInitialThemeUseCase.call, getAppVersionUseCase.call), ); final GetAppInitialThemeUseCase getInitialThemeUseCase; @@ -41,18 +37,4 @@ class AppCubit extends Cubit { await setColorUseCase(index, color); emit(state.copyWith(theme: state.theme.copyWith(targetColor: color))); } - - Future setAppVersionStatus({ - required int requiredBuildNumber, - required int recommendedBuildNumber, - required int currentBuildNumber, - }) async { - if (currentBuildNumber < requiredBuildNumber) { - emit(state.copyWith(appVersionStatus: YesRequiredVersion(requiredBuildNumber))); - } else if (currentBuildNumber < recommendedBuildNumber) { - emit(state.copyWith(appVersionStatus: YesRecommendedVersion(recommendedBuildNumber))); - } else { - emit(state.copyWith(appVersionStatus: const NoNewVersion())); - } - } } diff --git a/app/lib/app/presentation/cubit/app_state.dart b/app/lib/app/presentation/cubit/app_state.dart index dad4921b..79f6387a 100644 --- a/app/lib/app/presentation/cubit/app_state.dart +++ b/app/lib/app/presentation/cubit/app_state.dart @@ -1,56 +1,24 @@ part of 'app_cubit.dart'; class AppState extends Equatable { - const AppState({ - required this.theme, - required this.appVersionStatus, - required this.appVersion, - this.deviceId, - }); + const AppState( + this.theme, + this.appVersion, + ); final CustomTheme theme; - final String? deviceId; - final AppVersionStatus appVersionStatus; final String appVersion; @override - List get props => [theme, deviceId, appVersionStatus]; + List get props => [theme, appVersion]; AppState copyWith({ CustomTheme? theme, String? appVersion, - String? deviceId, - AppVersionStatus? appVersionStatus, }) { return AppState( - theme: theme ?? this.theme, - deviceId: deviceId ?? this.deviceId, - appVersionStatus: appVersionStatus ?? this.appVersionStatus, - appVersion: appVersion ?? this.appVersion, + theme ?? this.theme, + appVersion ?? this.appVersion, ); } } - -@immutable -sealed class AppVersionStatus { - const AppVersionStatus(); -} - -@immutable -final class NoNewVersion extends AppVersionStatus { - const NoNewVersion(); -} - -@immutable -final class YesRecommendedVersion extends AppVersionStatus { - const YesRecommendedVersion(this.buildNumber); - - final int buildNumber; -} - -@immutable -final class YesRequiredVersion extends AppVersionStatus { - const YesRequiredVersion(this.buildNumber); - - final int buildNumber; -} diff --git a/app/lib/app/presentation/cubit/remote_config_cubit.dart b/app/lib/app/presentation/cubit/remote_config_cubit.dart new file mode 100644 index 00000000..cdaa90cc --- /dev/null +++ b/app/lib/app/presentation/cubit/remote_config_cubit.dart @@ -0,0 +1,23 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +part 'remote_config_state.dart'; + +class RemoteConfigCubit extends Cubit { + RemoteConfigCubit() : super(const RemoteConfigState(appVersionStatus: NoNewVersion())); + + Future setAppVersionStatus({ + required int requiredBuildNumber, + required int recommendedBuildNumber, + required int currentBuildNumber, + }) async { + if (currentBuildNumber < requiredBuildNumber) { + emit(state.copyWith(appVersionStatus: YesRequiredVersion(requiredBuildNumber))); + } else if (currentBuildNumber < recommendedBuildNumber) { + emit(state.copyWith(appVersionStatus: YesRecommendedVersion(recommendedBuildNumber))); + } else { + emit(state.copyWith(appVersionStatus: const NoNewVersion())); + } + } +} diff --git a/app/lib/app/presentation/cubit/remote_config_state.dart b/app/lib/app/presentation/cubit/remote_config_state.dart new file mode 100644 index 00000000..aa7c830f --- /dev/null +++ b/app/lib/app/presentation/cubit/remote_config_state.dart @@ -0,0 +1,49 @@ +part of 'remote_config_cubit.dart'; + +class RemoteConfigState extends Equatable { + const RemoteConfigState({ + required this.appVersionStatus, + this.deviceId, + }); + + final AppVersionStatus appVersionStatus; + final String? deviceId; + + @override + List get props => [appVersionStatus, deviceId]; + + RemoteConfigState copyWith({ + AppVersionStatus? appVersionStatus, + String? deviceId, + bool? disableHatim, + }) { + return RemoteConfigState( + appVersionStatus: appVersionStatus ?? this.appVersionStatus, + deviceId: deviceId ?? this.deviceId, + ); + } +} + +@immutable +abstract class AppVersionStatus { + const AppVersionStatus(); +} + +@immutable +final class NoNewVersion extends AppVersionStatus { + const NoNewVersion(); +} + +@immutable +final class YesRecommendedVersion extends AppVersionStatus { + const YesRecommendedVersion(this.buildNumber); + + final int buildNumber; +} + +@immutable +final class YesRequiredVersion extends AppVersionStatus { + const YesRequiredVersion(this.buildNumber); + + final int buildNumber; +} diff --git a/app/lib/app/presentation/presenation.dart b/app/lib/app/presentation/presenation.dart index 7758b424..da80663c 100644 --- a/app/lib/app/presentation/presenation.dart +++ b/app/lib/app/presentation/presenation.dart @@ -1,4 +1,5 @@ export 'cubit/app_cubit.dart'; export 'cubit/auth_cubit.dart'; +export 'cubit/remote_config_cubit.dart'; export 'view/app_view.dart'; export 'view/app_listener.dart'; diff --git a/app/lib/app/presentation/view/app_listener.dart b/app/lib/app/presentation/view/app_listener.dart index 5c6655eb..9696abba 100644 --- a/app/lib/app/presentation/view/app_listener.dart +++ b/app/lib/app/presentation/view/app_listener.dart @@ -18,7 +18,7 @@ class AppListener extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocListener( + return BlocListener( listenWhen: (p, c) => p.appVersionStatus != c.appVersionStatus, listener: (context, state) { if (state.appVersionStatus is YesRecommendedVersion) { diff --git a/app/lib/app/presentation/view/app_view.dart b/app/lib/app/presentation/view/app_view.dart index e533cedf..e2b33b4e 100644 --- a/app/lib/app/presentation/view/app_view.dart +++ b/app/lib/app/presentation/view/app_view.dart @@ -79,6 +79,9 @@ class MyApp extends StatelessWidget { context.read(), )..init(), ), + BlocProvider( + create: (context) => RemoteConfigCubit(), + ), ], child: const QuranApp(), ); @@ -96,8 +99,7 @@ class _QuranAppState extends State { @override void initState() { super.initState(); - SchedulerBinding.instance.addPostFrameCallback((_) async { - await Future.delayed(const Duration(seconds: 2)); + SchedulerBinding.instance.addPostFrameCallback((_) { _listenRemoteConfig(); }); } @@ -122,11 +124,12 @@ class _QuranAppState extends State { void _listenRemoteConfig() { final remoteConfig = context.read(); - context.read().setAppVersionStatus( + context.read().setAppVersionStatus( requiredBuildNumber: remoteConfig.requiredBuildNumber, recommendedBuildNumber: remoteConfig.recommendedBuildNumber, currentBuildNumber: remoteConfig.currentBuildNumber, ); + remoteConfig.remoteConfig.onConfigUpdated.listen((event) async { await remoteConfig.remoteConfig.activate(); setState(() {}); diff --git a/app/lib/core/remote_config/mq_remote_config.dart b/app/lib/core/remote_config/mq_remote_config.dart index 6d794e82..36327f5c 100644 --- a/app/lib/core/remote_config/mq_remote_config.dart +++ b/app/lib/core/remote_config/mq_remote_config.dart @@ -5,7 +5,7 @@ import 'package:firebase_remote_config/firebase_remote_config.dart'; import 'package:flutter/foundation.dart'; @immutable -final class MqRemoteConfig { +class MqRemoteConfig { const MqRemoteConfig({ required this.remoteConfig, required this.buildNumber, @@ -37,7 +37,7 @@ final class MqRemoteConfig { return (versionData['requiredBuildNumber'], versionData['recommendedBuildNumber']); } - static const _enableHatim = 'enableHatim'; + static const _disableHatim = 'isHatimDisabled'; static const _appVersion = 'appVersion'; static Map _defaultAppVersionValue(int currentBuildNumber) { @@ -55,10 +55,10 @@ final class MqRemoteConfig { static Map _defaultParams(int currentBuildNumber) { return { - _enableHatim: true, + _disableHatim: true, _appVersion: jsonEncode(_defaultAppVersionValue(currentBuildNumber)), }; } - bool get isHatimEnabled => remoteConfig.getBool(_enableHatim); + bool get isHatimDisabled => remoteConfig.getBool(_disableHatim); } diff --git a/app/lib/l10n/arb/app_ar.arb b/app/lib/l10n/arb/app_ar.arb index a3c2bf88..2ce8ffe0 100644 --- a/app/lib/l10n/arb/app_ar.arb +++ b/app/lib/l10n/arb/app_ar.arb @@ -86,5 +86,6 @@ "requiredVersionDescription": "نسخة جديدة من التطبيق متاحة. يجب عليك تحديثه للمتابعة.", "recommendedVersionDescription": "نسخة جديدة من التطبيق متاحة.", "remindMeLater": "ذكرني لاحقاً", - "updateNow": "تحديث الآن" + "updateNow": "تحديث الآن", + "hatimNotAvailable": "خاتم غير متاح" } \ No newline at end of file diff --git a/app/lib/l10n/arb/app_en.arb b/app/lib/l10n/arb/app_en.arb index 351fdfa3..d81ead11 100644 --- a/app/lib/l10n/arb/app_en.arb +++ b/app/lib/l10n/arb/app_en.arb @@ -86,5 +86,6 @@ "requiredVersionDescription": "A new version of the app is available. You need to update it to continue.", "recommendedVersionDescription": "A new version of the app is available.", "remindMeLater": "Remind me later", - "updateNow": "Update now" + "updateNow": "Update now", + "hatimNotAvailable": "Hatim is not available." } \ No newline at end of file diff --git a/app/lib/l10n/arb/app_id.arb b/app/lib/l10n/arb/app_id.arb index e9c19e8e..6871a509 100644 --- a/app/lib/l10n/arb/app_id.arb +++ b/app/lib/l10n/arb/app_id.arb @@ -86,5 +86,6 @@ "requiredVersionDescription": "Versi baru dari aplikasi tersedia. Anda perlu memperbarui untuk melanjutkan.", "recommendedVersionDescription": "Versi baru dari aplikasi tersedia.", "remindMeLater": "Ingatkan saya nanti", - "updateNow": "Perbarui sekarang" + "updateNow": "Perbarui sekarang", + "hatimNotAvailable": "Hatim tidak tersedia." } \ No newline at end of file diff --git a/app/lib/l10n/arb/app_kk.arb b/app/lib/l10n/arb/app_kk.arb index 3e6cbfce..63c30cd4 100644 --- a/app/lib/l10n/arb/app_kk.arb +++ b/app/lib/l10n/arb/app_kk.arb @@ -86,6 +86,6 @@ "requiredVersionDescription": "Қолданбаның жаңа нұсқасы қолайлы. Жалғастыру үшін оны жаңартуыңыз керек.", "recommendedVersionDescription": "Қолданбаның жаңа нұсқасы қолайлы.", "remindMeLater": "Кейін еске салу", - "updateNow": "Қазір жаңарту" - + "updateNow": "Қазір жаңарту", + "hatimNotAvailable": "Хатым жетімді емес." } \ No newline at end of file diff --git a/app/lib/l10n/arb/app_ky.arb b/app/lib/l10n/arb/app_ky.arb index 8625e4c9..a7fabbc7 100644 --- a/app/lib/l10n/arb/app_ky.arb +++ b/app/lib/l10n/arb/app_ky.arb @@ -86,5 +86,6 @@ "requiredVersionDescription": "Колдонмонун жаңы версиясы жеткиликтүү. Улантуу үчүн аны жаңыртышыңыз керек.", "recommendedVersionDescription": "Колдонмонун жаңы версиясы жеткиликтүү", "remindMeLater": "Кийинчерээк эстетип коюу", - "updateNow": "Азыр жаңыртуу" + "updateNow": "Азыр жаңыртуу", + "hatimNotAvailable": "Хатым жеткиликтүү эмес." } \ No newline at end of file diff --git a/app/lib/l10n/arb/app_ru.arb b/app/lib/l10n/arb/app_ru.arb index 6e26912a..46afede8 100644 --- a/app/lib/l10n/arb/app_ru.arb +++ b/app/lib/l10n/arb/app_ru.arb @@ -86,5 +86,6 @@ "requiredVersionDescription": "Доступна новая версия приложения. Вам необходимо обновить его, чтобы продолжить использование.", "recommendedVersionDescription": "Доступна новая версия приложения.", "remindMeLater": "Напомнить позже", - "updateNow": "Обновить сейчас" + "updateNow": "Обновить сейчас", + "hatimNotAvailable": "Хатим недоступен." } \ No newline at end of file diff --git a/app/lib/l10n/arb/app_tr.arb b/app/lib/l10n/arb/app_tr.arb index 36c7ae39..17d2ce90 100644 --- a/app/lib/l10n/arb/app_tr.arb +++ b/app/lib/l10n/arb/app_tr.arb @@ -86,5 +86,6 @@ "requiredVersionDescription": "Uygulamanın yeni bir sürümü mevcut. Devam etmek için güncellemelisiniz.", "recommendedVersionDescription": "Uygulamanın yeni bir sürümü mevcut.", "remindMeLater": "Daha sonra hatırlat", - "updateNow": "Şimdi güncelle" + "updateNow": "Şimdi güncelle", + "hatimNotAvailable": "Hatim mevcut değil." } \ No newline at end of file diff --git a/app/lib/modules/home/presentation/view/home_view.dart b/app/lib/modules/home/presentation/view/home_view.dart index bed49746..aeecb52a 100644 --- a/app/lib/modules/home/presentation/view/home_view.dart +++ b/app/lib/modules/home/presentation/view/home_view.dart @@ -103,16 +103,16 @@ class HomeBody extends StatelessWidget { key: const Key(MqKeys.participantToHatim), text: l10n.homeGoHatim, onPressed: () { - // if (const MqRemoteConfig().isHatimEnabled) { - MqAnalytic.track(AnalyticKey.goHatim); - context.goNamed(AppRouter.hatim); - // } else { - // ScaffoldMessenger.of(context).showSnackBar( - // const SnackBar( - // content: Text('Hatim is not available.'), - // ), - // ); - // } + if (context.read().isHatimDisabled) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(context.l10n.hatimNotAvailable), + ), + ); + } else { + MqAnalytic.track(AnalyticKey.goHatim); + context.goNamed(AppRouter.hatim); + } }, ), ), diff --git a/app/test/helpers/pump_app.dart b/app/test/helpers/pump_app.dart index 1bdda3b0..8bf3ffb8 100644 --- a/app/test/helpers/pump_app.dart +++ b/app/test/helpers/pump_app.dart @@ -3,6 +3,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:my_quran/app/app.dart'; import 'package:my_quran/config/app_config.dart'; +import 'package:my_quran/core/core.dart'; import 'package:my_quran/modules/modules.dart'; extension PumpApp on WidgetTester { @@ -19,6 +20,7 @@ extension PumpApp on WidgetTester { PatchGenderUseCase patchGenderUseCase, PatchLocaleCodeUseCase patchLocaleCodeUseCase, LogoutUseCase logoutUseCase, + MqRemoteConfig remoteConfig, ) { return pumpWidget( RepositoryProvider( @@ -47,6 +49,10 @@ extension PumpApp on WidgetTester { BlocProvider( create: (context) => HomeCubit(GetHomeDataUseCase(homeRepo)), ), + BlocProvider( + create: (context) => RemoteConfigCubit(), + ), + RepositoryProvider(create: (context) => remoteConfig), ], child: const QuranApp(), ), diff --git a/app/test/widget_test.dart b/app/test/widget_test.dart index 38ca7330..0adea541 100644 --- a/app/test/widget_test.dart +++ b/app/test/widget_test.dart @@ -1,3 +1,4 @@ +import 'package:firebase_remote_config/firebase_remote_config.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; @@ -28,6 +29,39 @@ final class MockHomeRepositoryImpl implements HomeRepository { } } +final class MockFirebaseRemoteConfig extends Mock implements FirebaseRemoteConfig { + @override + Stream get onConfigUpdated { + return const Stream.empty(); + } +} + +class MockMqRemoteConfig implements MqRemoteConfig { + @override + String get buildNumber => '1.3.0'; + + @override + int get currentBuildNumber => 10; + + @override + Future initialise() async {} + + @override + bool get isHatimDisabled => false; + + @override + int get recommendedBuildNumber => 10; + + @override + int get requiredBuildNumber => 10; + + @override + (int, int) get version => (10, 10); + + @override + FirebaseRemoteConfig get remoteConfig => MockFirebaseRemoteConfig(); +} + // flutter test void main() { @@ -35,6 +69,7 @@ void main() { final storage = MockPreferencesStorage(); final packageInfo = MockPackageInfo(); final remoteClient = MockRemoteClient(); + final remoteConfig = MockMqRemoteConfig(); final homeRepo = MockHomeRepositoryImpl(); final appLocalDataSource = AppLocalDataSource(packageInfo: packageInfo); @@ -82,6 +117,7 @@ void main() { pathGenderUseCase, patchLocaleCodeUseCase, logoutUseCase, + remoteConfig, ); await tester.pumpAndSettle(); expect(find.byType(MaterialApp), findsOneWidget); From 07820e651d7d7cb61f63aeeac8fc7dac2e67597e Mon Sep 17 00:00:00 2001 From: Aidaiym Date: Mon, 8 Jul 2024 15:59:41 +0600 Subject: [PATCH 05/11] fix versions --- app/lib/app/presentation/view/app_view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/app/lib/app/presentation/view/app_view.dart b/app/lib/app/presentation/view/app_view.dart index e2b33b4e..252fa872 100644 --- a/app/lib/app/presentation/view/app_view.dart +++ b/app/lib/app/presentation/view/app_view.dart @@ -124,6 +124,7 @@ class _QuranAppState extends State { void _listenRemoteConfig() { final remoteConfig = context.read(); + context.read().setAppVersionStatus( requiredBuildNumber: remoteConfig.requiredBuildNumber, recommendedBuildNumber: remoteConfig.recommendedBuildNumber, From 2dfc5bd66a13d961d18c988f47564d05cd87bd91 Mon Sep 17 00:00:00 2001 From: Aidaiym Date: Mon, 8 Jul 2024 16:01:20 +0600 Subject: [PATCH 06/11] fix versions --- app/lib/l10n/arb/app_ky.arb | 2 +- app/lib/modules/home/presentation/view/home_view.dart | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/lib/l10n/arb/app_ky.arb b/app/lib/l10n/arb/app_ky.arb index a7fabbc7..611ef4d0 100644 --- a/app/lib/l10n/arb/app_ky.arb +++ b/app/lib/l10n/arb/app_ky.arb @@ -87,5 +87,5 @@ "recommendedVersionDescription": "Колдонмонун жаңы версиясы жеткиликтүү", "remindMeLater": "Кийинчерээк эстетип коюу", "updateNow": "Азыр жаңыртуу", - "hatimNotAvailable": "Хатым жеткиликтүү эмес." + "hatimNotAvailable": "Хатым азырынча жеткиликтүү эмес." } \ No newline at end of file diff --git a/app/lib/modules/home/presentation/view/home_view.dart b/app/lib/modules/home/presentation/view/home_view.dart index aeecb52a..8e9be61a 100644 --- a/app/lib/modules/home/presentation/view/home_view.dart +++ b/app/lib/modules/home/presentation/view/home_view.dart @@ -105,9 +105,7 @@ class HomeBody extends StatelessWidget { onPressed: () { if (context.read().isHatimDisabled) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.hatimNotAvailable), - ), + SnackBar(content: Text(context.l10n.hatimNotAvailable)), ); } else { MqAnalytic.track(AnalyticKey.goHatim); From 778f7c4faf2001aba608d2aac21fc72902b62597 Mon Sep 17 00:00:00 2001 From: Aidaiym Date: Mon, 8 Jul 2024 16:42:47 +0600 Subject: [PATCH 07/11] fix verstion --- app/ios/Podfile.lock | 50 ++++++++++++++++++++------------------------ app/pubspec.lock | 16 +++++++------- app/pubspec.yaml | 6 +----- 3 files changed, 31 insertions(+), 41 deletions(-) diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index fdb6c5c8..08c3fa3c 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -26,13 +26,12 @@ PODS: - FirebaseCore (= 10.27.0) - Firebase/Crashlytics (10.27.0): - Firebase/CoreOnly - - FirebaseCrashlytics (~> 10.25.0) - - Firebase/RemoteConfig (10.25.0): + - FirebaseCrashlytics (~> 10.27.0) + - Firebase/RemoteConfig (10.27.0): - Firebase/CoreOnly - - FirebaseRemoteConfig (~> 10.25.0) - - firebase_analytics (10.10.7): - - Firebase/Analytics (= 10.25.0) - + - FirebaseRemoteConfig (~> 10.27.0) + - firebase_analytics (11.1.0): + - Firebase/Analytics (= 10.27.0) - firebase_core - Flutter - firebase_auth (5.1.1): @@ -46,15 +45,14 @@ PODS: - Firebase/Crashlytics (= 10.27.0) - firebase_core - Flutter - - firebase_remote_config (4.4.7): - - Firebase/RemoteConfig (= 10.25.0) + - firebase_remote_config (5.0.2): + - Firebase/RemoteConfig (= 10.27.0) - firebase_core - Flutter - FirebaseABTesting (10.28.0): - FirebaseCore (~> 10.0) - - FirebaseAnalytics (10.25.0): - - FirebaseAnalytics/AdIdSupport (= 10.25.0) - + - FirebaseAnalytics (10.27.0): + - FirebaseAnalytics/AdIdSupport (= 10.27.0) - FirebaseCore (~> 10.0) - FirebaseInstallations (~> 10.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.11) @@ -101,7 +99,7 @@ PODS: - GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/UserDefaults (~> 7.8) - PromisesObjC (~> 2.1) - - FirebaseRemoteConfig (10.25.0): + - FirebaseRemoteConfig (10.27.0): - FirebaseABTesting (~> 10.0) - FirebaseCore (~> 10.0) - FirebaseInstallations (~> 10.0) @@ -186,10 +184,10 @@ PODS: - GTMAppAuth (4.1.1): - AppAuth/Core (~> 1.7) - GTMSessionFetcher/Core (< 4.0, >= 3.3) - - GTMSessionFetcher (3.4.1): - - GTMSessionFetcher/Full (= 3.4.1) - - GTMSessionFetcher/Core (3.4.1) - - GTMSessionFetcher/Full (3.4.1): + - GTMSessionFetcher (3.5.0): + - GTMSessionFetcher/Full (= 3.5.0) + - GTMSessionFetcher/Core (3.5.0) + - GTMSessionFetcher/Full (3.5.0): - GTMSessionFetcher/Core - just_audio (0.0.1): - Flutter @@ -310,16 +308,14 @@ SPEC CHECKSUMS: audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207 connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d - - Firebase: 0312a2352584f782ea56f66d91606891d4607f06 - firebase_analytics: cc06e24d6a2343c44f845b3112143db72d10ef78 - firebase_auth: 59b73550b86d6213a4e9509a9c21b59ea524c647 - firebase_core: a626d00494efa398e7c54f25f1454a64c8abf197 - firebase_crashlytics: 17e856fabec68d993662abaf2f6fe2413f0abece - firebase_remote_config: 7b05c80210ab558c80f7a756681022b4ee98eea0 + Firebase: 26b040b20866a55f55eb3611b9fcf3ae64816b86 + firebase_analytics: cc6e3ca8f159facf99fb1ceb2d81bda9cc811556 + firebase_auth: cc4ea3f47dda7afc4c64f59540aa570aca60814d + firebase_core: f8d0424c45e0f1e596811085fc12c638d628457c + firebase_crashlytics: 39ca2155bac4fa2eec0aec9f0eb5e938a08bca23 + firebase_remote_config: 962876d64b52d7710d756ea85e27c768002dc628 FirebaseABTesting: 589bc28c0ab3e5554336895a34aa262e24276665 - FirebaseAnalytics: ec00fe8b93b41dc6fe4a28784b8e51da0647a248 - + FirebaseAnalytics: f9211b719db260cc91aebee8bb539cb367d0dfd1 FirebaseAppCheckInterop: 5315f40293191bfec04b2cfab0215760e441540a FirebaseAuth: 77a012b7e08042bf44d0db835ca2e86e6ca7bbd3 FirebaseCore: a2b95ae4ce7c83ceecfbbbe3b6f1cddc7415a808 @@ -327,7 +323,7 @@ SPEC CHECKSUMS: FirebaseCoreInternal: 58d07f1362fddeb0feb6a857d1d1d1c5e558e698 FirebaseCrashlytics: 81ea6ec96519388687f6061beb838a8eec482293 FirebaseInstallations: 60c1d3bc1beef809fd1ad1189a8057a040c59f2e - FirebaseRemoteConfig: 9f3935cefecd85d5b312192117f444957de24a75 + FirebaseRemoteConfig: 37a2ba3c8c454be8553a41ba1a2f4a4f0b845670 FirebaseRemoteConfigInterop: 70d200c6956ef3b5c3592a95e824c1210682d785 FirebaseSessions: 20da8500ad66bb12622743e170459bf62a0768e8 FirebaseSharedSwift: 48de4aec81a6b79bb30404e5e6db43ea74848fed @@ -338,7 +334,7 @@ SPEC CHECKSUMS: GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de - GTMSessionFetcher: 8000756fc1c19d2e5697b90311f7832d2e33f6cd + GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa nanopb: 438bc412db1928dac798aa6fd75726007be04262 package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c diff --git a/app/pubspec.lock b/app/pubspec.lock index 4eef3826..7417f7e3 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -424,33 +424,31 @@ packages: sha256: "8ec63ebefe9233d3cdc744f75d5b88cf16b6241d8680e6284c2d272bcb0a10af" url: "https://pub.dev" source: hosted - - version: "3.6.35" + version: "3.6.38" firebase_remote_config: dependency: "direct main" description: name: firebase_remote_config - sha256: "653bd94b68e2c4e89eca10db90576101f1024151f39f2d4e7c64ae6a90a5f9c5" + sha256: a554ac6d87e3f45d52fda2df664787311d2ece301c6de9d438acce3c3f8bdc9c url: "https://pub.dev" source: hosted - version: "4.4.7" + version: "5.0.2" firebase_remote_config_platform_interface: dependency: transitive description: name: firebase_remote_config_platform_interface - sha256: "24a2c445b15de3af7e4582ebceb2aa9a1e3731d0202cb3e7a1e03012440fa07d" + sha256: c419b12dd5c15b72ceb501e1472cfdeb9924372adc2b3bcf20e37bc5a4aa0984 url: "https://pub.dev" source: hosted - version: "1.4.35" + version: "1.4.38" firebase_remote_config_web: dependency: transitive description: name: firebase_remote_config_web - sha256: "525aa3000fd27cd023841c802010a06515e564aab2f147aa964b35f54abbf449" + sha256: ac525f7d67d1e41aef15c47f5f8285681841ce6fadc23ca6a5a0586e7a7ce52d url: "https://pub.dev" source: hosted - version: "1.6.7" - + version: "1.6.10" fixnum: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index cf864fe8..d9b932d6 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: firebase_auth: ^5.1.1 firebase_crashlytics: ^4.0.2 firebase_analytics: ^11.1.0 + firebase_remote_config: ^5.0.2 google_sign_in: ^6.2.1 web_socket_channel: ^2.4.5 # 3.0.0 sign_in_with_apple: ^6.1.1 @@ -43,11 +44,6 @@ dependencies: path: ../packages/mq_storage/ mq_ci_keys: path: ../packages/mq_ci_keys/ - - firebase_crashlytics: ^3.5.7 - firebase_analytics: ^10.10.7 - firebase_remote_config: ^4.4.7 - # flutter_native_splash: ^2.4.0 dev_dependencies: From c17bae018b08f32ac6d9710323406131236a5975 Mon Sep 17 00:00:00 2001 From: Aidaiym Date: Mon, 8 Jul 2024 17:10:30 +0600 Subject: [PATCH 08/11] change ky --- app/lib/l10n/arb/app_ky.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/l10n/arb/app_ky.arb b/app/lib/l10n/arb/app_ky.arb index 611ef4d0..47cd19e7 100644 --- a/app/lib/l10n/arb/app_ky.arb +++ b/app/lib/l10n/arb/app_ky.arb @@ -87,5 +87,5 @@ "recommendedVersionDescription": "Колдонмонун жаңы версиясы жеткиликтүү", "remindMeLater": "Кийинчерээк эстетип коюу", "updateNow": "Азыр жаңыртуу", - "hatimNotAvailable": "Хатым азырынча жеткиликтүү эмес." + "hatimNotAvailable": "Хатымга катышуу азырынча жеткиликтүү эмес." } \ No newline at end of file From 236d99c9ee8b1cef024b09666a2806a29b83f806 Mon Sep 17 00:00:00 2001 From: Aidaiym Date: Tue, 9 Jul 2024 00:07:21 +0600 Subject: [PATCH 09/11] fix --- .../cubit/remote_config_cubit.dart | 25 ++++++++++++++-- .../cubit/remote_config_state.dart | 11 ++++--- app/lib/app/presentation/view/app_view.dart | 22 ++++---------- .../core/remote_config/mq_remote_config.dart | 6 ++-- .../home/presentation/view/home_view.dart | 30 +++++++++++-------- app/test/helpers/pump_app.dart | 12 +++++--- app/test/widget_test.dart | 6 +++- 7 files changed, 67 insertions(+), 45 deletions(-) diff --git a/app/lib/app/presentation/cubit/remote_config_cubit.dart b/app/lib/app/presentation/cubit/remote_config_cubit.dart index cdaa90cc..3e33d5c3 100644 --- a/app/lib/app/presentation/cubit/remote_config_cubit.dart +++ b/app/lib/app/presentation/cubit/remote_config_cubit.dart @@ -1,17 +1,38 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:meta/meta.dart'; +import 'package:my_quran/core/core.dart'; +import 'package:package_info_plus/package_info_plus.dart'; part 'remote_config_state.dart'; class RemoteConfigCubit extends Cubit { - RemoteConfigCubit() : super(const RemoteConfigState(appVersionStatus: NoNewVersion())); + RemoteConfigCubit(this.packageInfo, this.remoteConfig) + : super(const RemoteConfigState(appVersionStatus: NoNewVersion(), isHatimEnable: true)); + + final PackageInfo packageInfo; + final MqRemoteConfig remoteConfig; + + Future init() async { + await setAppVersionStatus( + requiredBuildNumber: remoteConfig.requiredBuildNumber, + recommendedBuildNumber: remoteConfig.recommendedBuildNumber, + ); + + emit(state.copyWith(isHatimEnable: remoteConfig.hatimIsEnable)); + + remoteConfig.remoteConfig.onConfigUpdated.listen((event) async { + await remoteConfig.remoteConfig.activate(); + }); + } Future setAppVersionStatus({ required int requiredBuildNumber, required int recommendedBuildNumber, - required int currentBuildNumber, }) async { + final currentBuildNumber = int.parse(packageInfo.buildNumber); + if (currentBuildNumber < requiredBuildNumber) { emit(state.copyWith(appVersionStatus: YesRequiredVersion(requiredBuildNumber))); } else if (currentBuildNumber < recommendedBuildNumber) { diff --git a/app/lib/app/presentation/cubit/remote_config_state.dart b/app/lib/app/presentation/cubit/remote_config_state.dart index aa7c830f..a435cd49 100644 --- a/app/lib/app/presentation/cubit/remote_config_state.dart +++ b/app/lib/app/presentation/cubit/remote_config_state.dart @@ -3,22 +3,21 @@ part of 'remote_config_cubit.dart'; class RemoteConfigState extends Equatable { const RemoteConfigState({ required this.appVersionStatus, + required this.isHatimEnable, this.deviceId, }); final AppVersionStatus appVersionStatus; final String? deviceId; + final bool isHatimEnable; @override - List get props => [appVersionStatus, deviceId]; + List get props => [appVersionStatus, deviceId, isHatimEnable]; - RemoteConfigState copyWith({ - AppVersionStatus? appVersionStatus, - String? deviceId, - bool? disableHatim, - }) { + RemoteConfigState copyWith({AppVersionStatus? appVersionStatus, String? deviceId, bool? isHatimEnable}) { return RemoteConfigState( appVersionStatus: appVersionStatus ?? this.appVersionStatus, + isHatimEnable: isHatimEnable ?? this.isHatimEnable, deviceId: deviceId ?? this.deviceId, ); } diff --git a/app/lib/app/presentation/view/app_view.dart b/app/lib/app/presentation/view/app_view.dart index 252fa872..6abc6cf2 100644 --- a/app/lib/app/presentation/view/app_view.dart +++ b/app/lib/app/presentation/view/app_view.dart @@ -80,7 +80,10 @@ class MyApp extends StatelessWidget { )..init(), ), BlocProvider( - create: (context) => RemoteConfigCubit(), + create: (context) => RemoteConfigCubit( + context.read(), + context.read(), + ), ), ], child: const QuranApp(), @@ -100,7 +103,7 @@ class _QuranAppState extends State { void initState() { super.initState(); SchedulerBinding.instance.addPostFrameCallback((_) { - _listenRemoteConfig(); + context.read().init(); }); } @@ -121,19 +124,4 @@ class _QuranAppState extends State { ), ); } - - void _listenRemoteConfig() { - final remoteConfig = context.read(); - - context.read().setAppVersionStatus( - requiredBuildNumber: remoteConfig.requiredBuildNumber, - recommendedBuildNumber: remoteConfig.recommendedBuildNumber, - currentBuildNumber: remoteConfig.currentBuildNumber, - ); - - remoteConfig.remoteConfig.onConfigUpdated.listen((event) async { - await remoteConfig.remoteConfig.activate(); - setState(() {}); - }); - } } diff --git a/app/lib/core/remote_config/mq_remote_config.dart b/app/lib/core/remote_config/mq_remote_config.dart index 36327f5c..21377116 100644 --- a/app/lib/core/remote_config/mq_remote_config.dart +++ b/app/lib/core/remote_config/mq_remote_config.dart @@ -37,7 +37,7 @@ class MqRemoteConfig { return (versionData['requiredBuildNumber'], versionData['recommendedBuildNumber']); } - static const _disableHatim = 'isHatimDisabled'; + static const _hatimIsEnable = 'hatimIsEnable'; static const _appVersion = 'appVersion'; static Map _defaultAppVersionValue(int currentBuildNumber) { @@ -55,10 +55,10 @@ class MqRemoteConfig { static Map _defaultParams(int currentBuildNumber) { return { - _disableHatim: true, + _hatimIsEnable: true, _appVersion: jsonEncode(_defaultAppVersionValue(currentBuildNumber)), }; } - bool get isHatimDisabled => remoteConfig.getBool(_disableHatim); + bool get hatimIsEnable => remoteConfig.getBool(_hatimIsEnable); } diff --git a/app/lib/modules/home/presentation/view/home_view.dart b/app/lib/modules/home/presentation/view/home_view.dart index 8e9be61a..fb2f308f 100644 --- a/app/lib/modules/home/presentation/view/home_view.dart +++ b/app/lib/modules/home/presentation/view/home_view.dart @@ -99,18 +99,24 @@ class HomeBody extends StatelessWidget { ), Padding( padding: const EdgeInsets.symmetric(horizontal: 15), - child: CustomButton( - key: const Key(MqKeys.participantToHatim), - text: l10n.homeGoHatim, - onPressed: () { - if (context.read().isHatimDisabled) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.hatimNotAvailable)), - ); - } else { - MqAnalytic.track(AnalyticKey.goHatim); - context.goNamed(AppRouter.hatim); - } + child: BlocBuilder( + builder: (context, state) { + return CustomButton( + key: const Key(MqKeys.participantToHatim), + text: l10n.homeGoHatim, + onPressed: () { + if (state.isHatimEnable) { + MqAnalytic.track(AnalyticKey.goHatim); + context.goNamed(AppRouter.hatim); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(context.l10n.hatimNotAvailable), + ), + ); + } + }, + ); }, ), ), diff --git a/app/test/helpers/pump_app.dart b/app/test/helpers/pump_app.dart index 8bf3ffb8..e2e1bd07 100644 --- a/app/test/helpers/pump_app.dart +++ b/app/test/helpers/pump_app.dart @@ -5,6 +5,7 @@ import 'package:my_quran/app/app.dart'; import 'package:my_quran/config/app_config.dart'; import 'package:my_quran/core/core.dart'; import 'package:my_quran/modules/modules.dart'; +import 'package:package_info_plus/package_info_plus.dart'; extension PumpApp on WidgetTester { Future pumpApp( @@ -21,10 +22,14 @@ extension PumpApp on WidgetTester { PatchLocaleCodeUseCase patchLocaleCodeUseCase, LogoutUseCase logoutUseCase, MqRemoteConfig remoteConfig, + PackageInfo packageIngo, ) { return pumpWidget( - RepositoryProvider( - create: (context) => const AppConfig(isIntegrationTest: true), + MultiRepositoryProvider( + providers: [ + RepositoryProvider(create: (context) => const AppConfig(isIntegrationTest: true)), + RepositoryProvider(create: (context) => remoteConfig), + ], child: MultiBlocProvider( providers: [ BlocProvider( @@ -50,9 +55,8 @@ extension PumpApp on WidgetTester { create: (context) => HomeCubit(GetHomeDataUseCase(homeRepo)), ), BlocProvider( - create: (context) => RemoteConfigCubit(), + create: (context) => RemoteConfigCubit(packageIngo, remoteConfig), ), - RepositoryProvider(create: (context) => remoteConfig), ], child: const QuranApp(), ), diff --git a/app/test/widget_test.dart b/app/test/widget_test.dart index 0adea541..481d0c9e 100644 --- a/app/test/widget_test.dart +++ b/app/test/widget_test.dart @@ -20,6 +20,9 @@ final class MockSccialAuth extends Mock implements SoccialAuth {} final class MockPackageInfo extends Mock implements PackageInfo { @override String get version => '1.3.0'; + + @override + String get buildNumber => '10'; } final class MockHomeRepositoryImpl implements HomeRepository { @@ -47,7 +50,7 @@ class MockMqRemoteConfig implements MqRemoteConfig { Future initialise() async {} @override - bool get isHatimDisabled => false; + bool get hatimIsEnable => false; @override int get recommendedBuildNumber => 10; @@ -118,6 +121,7 @@ void main() { patchLocaleCodeUseCase, logoutUseCase, remoteConfig, + packageInfo, ); await tester.pumpAndSettle(); expect(find.byType(MaterialApp), findsOneWidget); From 2305c4432c0781da6b4638c5ce76c089b40783a3 Mon Sep 17 00:00:00 2001 From: Aidaiym Date: Tue, 9 Jul 2024 22:15:30 +0600 Subject: [PATCH 10/11] add builder --- .../app/presentation/cubit/remote_config_cubit.dart | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/lib/app/presentation/cubit/remote_config_cubit.dart b/app/lib/app/presentation/cubit/remote_config_cubit.dart index 3e33d5c3..407f2518 100644 --- a/app/lib/app/presentation/cubit/remote_config_cubit.dart +++ b/app/lib/app/presentation/cubit/remote_config_cubit.dart @@ -19,11 +19,10 @@ class RemoteConfigCubit extends Cubit { requiredBuildNumber: remoteConfig.requiredBuildNumber, recommendedBuildNumber: remoteConfig.recommendedBuildNumber, ); - - emit(state.copyWith(isHatimEnable: remoteConfig.hatimIsEnable)); - + _checkHatimIsEnable(); remoteConfig.remoteConfig.onConfigUpdated.listen((event) async { await remoteConfig.remoteConfig.activate(); + _checkHatimIsEnable(); }); } @@ -41,4 +40,11 @@ class RemoteConfigCubit extends Cubit { emit(state.copyWith(appVersionStatus: const NoNewVersion())); } } + + void _checkHatimIsEnable() { + final currentHatimIsEnable = remoteConfig.hatimIsEnable; + if (state.isHatimEnable != currentHatimIsEnable) { + emit(state.copyWith(isHatimEnable: currentHatimIsEnable)); + } + } } From 91dfb482f29823bcbd50090e05753a6b7ffb6252 Mon Sep 17 00:00:00 2001 From: Eldar2021 Date: Tue, 9 Jul 2024 23:33:30 +0600 Subject: [PATCH 11/11] Refactored --- app/ios/Podfile.lock | 108 ++++++------- .../cubit/remote_config_cubit.dart | 45 +++--- .../cubit/remote_config_state.dart | 18 ++- app/lib/app/presentation/view/app_view.dart | 4 +- .../home/presentation/view/home_view.dart | 147 +++++++----------- app/pubspec.lock | 120 ++++---------- app/pubspec.yaml | 11 +- app/test/helpers/pump_app.dart | 7 +- app/test/mocks/app_mocks.dart | 61 ++++++++ app/test/widget_test.dart | 60 +------ 10 files changed, 248 insertions(+), 333 deletions(-) create mode 100644 app/test/mocks/app_mocks.dart diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index 08c3fa3c..258b3b96 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -12,47 +12,45 @@ PODS: - connectivity_plus (0.0.1): - Flutter - FlutterMacOS - - device_info_plus (0.0.1): - - Flutter - - Firebase/Analytics (10.27.0): + - Firebase/Analytics (10.28.0): - Firebase/Core - - Firebase/Auth (10.27.0): + - Firebase/Auth (10.28.0): - Firebase/CoreOnly - - FirebaseAuth (~> 10.27.0) - - Firebase/Core (10.27.0): + - FirebaseAuth (~> 10.28.0) + - Firebase/Core (10.28.0): - Firebase/CoreOnly - - FirebaseAnalytics (~> 10.27.0) - - Firebase/CoreOnly (10.27.0): - - FirebaseCore (= 10.27.0) - - Firebase/Crashlytics (10.27.0): + - FirebaseAnalytics (~> 10.28.0) + - Firebase/CoreOnly (10.28.0): + - FirebaseCore (= 10.28.0) + - Firebase/Crashlytics (10.28.0): - Firebase/CoreOnly - - FirebaseCrashlytics (~> 10.27.0) - - Firebase/RemoteConfig (10.27.0): + - FirebaseCrashlytics (~> 10.28.0) + - Firebase/RemoteConfig (10.28.0): - Firebase/CoreOnly - - FirebaseRemoteConfig (~> 10.27.0) - - firebase_analytics (11.1.0): - - Firebase/Analytics (= 10.27.0) + - FirebaseRemoteConfig (~> 10.28.0) + - firebase_analytics (11.2.0): + - Firebase/Analytics (= 10.28.0) - firebase_core - Flutter - - firebase_auth (5.1.1): - - Firebase/Auth (= 10.27.0) + - firebase_auth (5.1.2): + - Firebase/Auth (= 10.28.0) - firebase_core - Flutter - - firebase_core (3.1.1): - - Firebase/CoreOnly (= 10.27.0) + - firebase_core (3.2.0): + - Firebase/CoreOnly (= 10.28.0) - Flutter - - firebase_crashlytics (4.0.2): - - Firebase/Crashlytics (= 10.27.0) + - firebase_crashlytics (4.0.3): + - Firebase/Crashlytics (= 10.28.0) - firebase_core - Flutter - - firebase_remote_config (5.0.2): - - Firebase/RemoteConfig (= 10.27.0) + - firebase_remote_config (5.0.3): + - Firebase/RemoteConfig (= 10.28.0) - firebase_core - Flutter - FirebaseABTesting (10.28.0): - FirebaseCore (~> 10.0) - - FirebaseAnalytics (10.27.0): - - FirebaseAnalytics/AdIdSupport (= 10.27.0) + - FirebaseAnalytics (10.28.0): + - FirebaseAnalytics/AdIdSupport (= 10.28.0) - FirebaseCore (~> 10.0) - FirebaseInstallations (~> 10.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.11) @@ -60,24 +58,24 @@ PODS: - GoogleUtilities/Network (~> 7.11) - "GoogleUtilities/NSData+zlib (~> 7.11)" - nanopb (< 2.30911.0, >= 2.30908.0) - - FirebaseAnalytics/AdIdSupport (10.27.0): + - FirebaseAnalytics/AdIdSupport (10.28.0): - FirebaseCore (~> 10.0) - FirebaseInstallations (~> 10.0) - - GoogleAppMeasurement (= 10.27.0) + - GoogleAppMeasurement (= 10.28.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.11) - GoogleUtilities/MethodSwizzler (~> 7.11) - GoogleUtilities/Network (~> 7.11) - "GoogleUtilities/NSData+zlib (~> 7.11)" - nanopb (< 2.30911.0, >= 2.30908.0) - FirebaseAppCheckInterop (10.28.0) - - FirebaseAuth (10.27.0): + - FirebaseAuth (10.28.0): - FirebaseAppCheckInterop (~> 10.17) - FirebaseCore (~> 10.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.8) - GoogleUtilities/Environment (~> 7.8) - GTMSessionFetcher/Core (< 4.0, >= 2.1) - RecaptchaInterop (~> 100.0) - - FirebaseCore (10.27.0): + - FirebaseCore (10.28.0): - FirebaseCoreInternal (~> 10.0) - GoogleUtilities/Environment (~> 7.12) - GoogleUtilities/Logger (~> 7.12) @@ -85,7 +83,7 @@ PODS: - FirebaseCore (~> 10.0) - FirebaseCoreInternal (10.28.0): - "GoogleUtilities/NSData+zlib (~> 7.8)" - - FirebaseCrashlytics (10.27.0): + - FirebaseCrashlytics (10.28.1): - FirebaseCore (~> 10.5) - FirebaseInstallations (~> 10.0) - FirebaseRemoteConfigInterop (~> 10.23) @@ -99,7 +97,7 @@ PODS: - GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/UserDefaults (~> 7.8) - PromisesObjC (~> 2.1) - - FirebaseRemoteConfig (10.27.0): + - FirebaseRemoteConfig (10.28.0): - FirebaseABTesting (~> 10.0) - FirebaseCore (~> 10.0) - FirebaseInstallations (~> 10.0) @@ -125,21 +123,21 @@ PODS: - FlutterMacOS - GoogleSignIn (~> 7.1) - GTMSessionFetcher (>= 3.4.0) - - GoogleAppMeasurement (10.27.0): - - GoogleAppMeasurement/AdIdSupport (= 10.27.0) + - GoogleAppMeasurement (10.28.0): + - GoogleAppMeasurement/AdIdSupport (= 10.28.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.11) - GoogleUtilities/MethodSwizzler (~> 7.11) - GoogleUtilities/Network (~> 7.11) - "GoogleUtilities/NSData+zlib (~> 7.11)" - nanopb (< 2.30911.0, >= 2.30908.0) - - GoogleAppMeasurement/AdIdSupport (10.27.0): - - GoogleAppMeasurement/WithoutAdIdSupport (= 10.27.0) + - GoogleAppMeasurement/AdIdSupport (10.28.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 10.28.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.11) - GoogleUtilities/MethodSwizzler (~> 7.11) - GoogleUtilities/Network (~> 7.11) - "GoogleUtilities/NSData+zlib (~> 7.11)" - nanopb (< 2.30911.0, >= 2.30908.0) - - GoogleAppMeasurement/WithoutAdIdSupport (10.27.0): + - GoogleAppMeasurement/WithoutAdIdSupport (10.28.0): - GoogleUtilities/AppDelegateSwizzler (~> 7.11) - GoogleUtilities/MethodSwizzler (~> 7.11) - GoogleUtilities/Network (~> 7.11) @@ -184,10 +182,10 @@ PODS: - GTMAppAuth (4.1.1): - AppAuth/Core (~> 1.7) - GTMSessionFetcher/Core (< 4.0, >= 3.3) - - GTMSessionFetcher (3.5.0): - - GTMSessionFetcher/Full (= 3.5.0) - - GTMSessionFetcher/Core (3.5.0) - - GTMSessionFetcher/Full (3.5.0): + - GTMSessionFetcher (3.4.1): + - GTMSessionFetcher/Full (= 3.4.1) + - GTMSessionFetcher/Core (3.4.1) + - GTMSessionFetcher/Full (3.4.1): - GTMSessionFetcher/Core - just_audio (0.0.1): - Flutter @@ -220,7 +218,6 @@ DEPENDENCIES: - audio_service (from `.symlinks/plugins/audio_service/ios`) - audio_session (from `.symlinks/plugins/audio_session/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`) - - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) @@ -271,8 +268,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/audio_session/ios" connectivity_plus: :path: ".symlinks/plugins/connectivity_plus/darwin" - device_info_plus: - :path: ".symlinks/plugins/device_info_plus/ios" firebase_analytics: :path: ".symlinks/plugins/firebase_analytics/ios" firebase_auth: @@ -307,34 +302,33 @@ SPEC CHECKSUMS: audio_service: f509d65da41b9521a61f1c404dd58651f265a567 audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207 connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db - device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d - Firebase: 26b040b20866a55f55eb3611b9fcf3ae64816b86 - firebase_analytics: cc6e3ca8f159facf99fb1ceb2d81bda9cc811556 - firebase_auth: cc4ea3f47dda7afc4c64f59540aa570aca60814d - firebase_core: f8d0424c45e0f1e596811085fc12c638d628457c - firebase_crashlytics: 39ca2155bac4fa2eec0aec9f0eb5e938a08bca23 - firebase_remote_config: 962876d64b52d7710d756ea85e27c768002dc628 + Firebase: 5121c624121af81cbc81df3bda414b3c28c4f3c3 + firebase_analytics: e51d02e2da883fd058f2a4f151f6a80acdfe67aa + firebase_auth: e778ee89483b86fe4200d1f8e9a1c52aa5fb64a8 + firebase_core: a9d0180d5285527884d07a41eb4a9ec9ed12cdb6 + firebase_crashlytics: 12b2b1ecfc50f6c551c68e491ae156b2b7d41273 + firebase_remote_config: 5f92bfc62c3ef2c657bf3d703ffa4be29082280f FirebaseABTesting: 589bc28c0ab3e5554336895a34aa262e24276665 - FirebaseAnalytics: f9211b719db260cc91aebee8bb539cb367d0dfd1 + FirebaseAnalytics: 1e06fe7d246af7230b08d1d9cdca54a4624dd461 FirebaseAppCheckInterop: 5315f40293191bfec04b2cfab0215760e441540a - FirebaseAuth: 77a012b7e08042bf44d0db835ca2e86e6ca7bbd3 - FirebaseCore: a2b95ae4ce7c83ceecfbbbe3b6f1cddc7415a808 + FirebaseAuth: 3d872fbbfc4223edeb72769e488f325fa8b0a4a9 + FirebaseCore: 857dc1c6dd1255675047404d8466f7dfaac5d779 FirebaseCoreExtension: f63147b723e2a700fe0f34ec6fb7f358d6fe83e0 FirebaseCoreInternal: 58d07f1362fddeb0feb6a857d1d1d1c5e558e698 - FirebaseCrashlytics: 81ea6ec96519388687f6061beb838a8eec482293 + FirebaseCrashlytics: f51e12b93f8e1134bbed602ed22df33804d55ccf FirebaseInstallations: 60c1d3bc1beef809fd1ad1189a8057a040c59f2e - FirebaseRemoteConfig: 37a2ba3c8c454be8553a41ba1a2f4a4f0b845670 + FirebaseRemoteConfig: f0879a8dccf4e8905716ed849569130efaeab3e2 FirebaseRemoteConfigInterop: 70d200c6956ef3b5c3592a95e824c1210682d785 FirebaseSessions: 20da8500ad66bb12622743e170459bf62a0768e8 FirebaseSharedSwift: 48de4aec81a6b79bb30404e5e6db43ea74848fed Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 google_sign_in_ios: 07375bfbf2620bc93a602c0e27160d6afc6ead38 - GoogleAppMeasurement: f65fc137531af9ad647f1c0a42f3b6a4d3a98049 + GoogleAppMeasurement: 55a4a3c8000c1280d68bf4c084adbfef20c49db1 GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de - GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 + GTMSessionFetcher: 8000756fc1c19d2e5697b90311f7832d2e33f6cd just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa nanopb: 438bc412db1928dac798aa6fd75726007be04262 package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c diff --git a/app/lib/app/presentation/cubit/remote_config_cubit.dart b/app/lib/app/presentation/cubit/remote_config_cubit.dart index 407f2518..7ab9b4d8 100644 --- a/app/lib/app/presentation/cubit/remote_config_cubit.dart +++ b/app/lib/app/presentation/cubit/remote_config_cubit.dart @@ -8,43 +8,42 @@ import 'package:package_info_plus/package_info_plus.dart'; part 'remote_config_state.dart'; class RemoteConfigCubit extends Cubit { - RemoteConfigCubit(this.packageInfo, this.remoteConfig) - : super(const RemoteConfigState(appVersionStatus: NoNewVersion(), isHatimEnable: true)); + RemoteConfigCubit({ + required this.packageInfo, + required this.remoteConfig, + }) : super(const RemoteConfigState()); final PackageInfo packageInfo; final MqRemoteConfig remoteConfig; Future init() async { - await setAppVersionStatus( - requiredBuildNumber: remoteConfig.requiredBuildNumber, - recommendedBuildNumber: remoteConfig.recommendedBuildNumber, - ); - _checkHatimIsEnable(); + _emitNewState(); remoteConfig.remoteConfig.onConfigUpdated.listen((event) async { await remoteConfig.remoteConfig.activate(); - _checkHatimIsEnable(); + _emitNewState(); }); } - Future setAppVersionStatus({ - required int requiredBuildNumber, - required int recommendedBuildNumber, - }) async { - final currentBuildNumber = int.parse(packageInfo.buildNumber); + void _emitNewState() { + final newState = state.copyWith( + appVersionStatus: _getAppVersionStatus, + isHatimEnable: _hatimIsEnable, + ); + emit(newState); + } + + bool get _hatimIsEnable => remoteConfig.hatimIsEnable; + AppVersionStatus get _getAppVersionStatus { + final currentBuildNumber = int.parse(packageInfo.buildNumber); + final requiredBuildNumber = remoteConfig.requiredBuildNumber; + final recommendedBuildNumber = remoteConfig.recommendedBuildNumber; if (currentBuildNumber < requiredBuildNumber) { - emit(state.copyWith(appVersionStatus: YesRequiredVersion(requiredBuildNumber))); + return YesRequiredVersion(requiredBuildNumber); } else if (currentBuildNumber < recommendedBuildNumber) { - emit(state.copyWith(appVersionStatus: YesRecommendedVersion(recommendedBuildNumber))); + return YesRecommendedVersion(recommendedBuildNumber); } else { - emit(state.copyWith(appVersionStatus: const NoNewVersion())); - } - } - - void _checkHatimIsEnable() { - final currentHatimIsEnable = remoteConfig.hatimIsEnable; - if (state.isHatimEnable != currentHatimIsEnable) { - emit(state.copyWith(isHatimEnable: currentHatimIsEnable)); + return const NoNewVersion(); } } } diff --git a/app/lib/app/presentation/cubit/remote_config_state.dart b/app/lib/app/presentation/cubit/remote_config_state.dart index a435cd49..d1a7c27d 100644 --- a/app/lib/app/presentation/cubit/remote_config_state.dart +++ b/app/lib/app/presentation/cubit/remote_config_state.dart @@ -2,8 +2,8 @@ part of 'remote_config_cubit.dart'; class RemoteConfigState extends Equatable { const RemoteConfigState({ - required this.appVersionStatus, - required this.isHatimEnable, + this.appVersionStatus = const NoNewVersion(), + this.isHatimEnable = true, this.deviceId, }); @@ -12,9 +12,17 @@ class RemoteConfigState extends Equatable { final bool isHatimEnable; @override - List get props => [appVersionStatus, deviceId, isHatimEnable]; - - RemoteConfigState copyWith({AppVersionStatus? appVersionStatus, String? deviceId, bool? isHatimEnable}) { + List get props => [ + appVersionStatus, + deviceId, + isHatimEnable, + ]; + + RemoteConfigState copyWith({ + AppVersionStatus? appVersionStatus, + String? deviceId, + bool? isHatimEnable, + }) { return RemoteConfigState( appVersionStatus: appVersionStatus ?? this.appVersionStatus, isHatimEnable: isHatimEnable ?? this.isHatimEnable, diff --git a/app/lib/app/presentation/view/app_view.dart b/app/lib/app/presentation/view/app_view.dart index 6abc6cf2..b0a4a4e7 100644 --- a/app/lib/app/presentation/view/app_view.dart +++ b/app/lib/app/presentation/view/app_view.dart @@ -81,8 +81,8 @@ class MyApp extends StatelessWidget { ), BlocProvider( create: (context) => RemoteConfigCubit( - context.read(), - context.read(), + packageInfo: context.read(), + remoteConfig: context.read(), ), ), ], diff --git a/app/lib/modules/home/presentation/view/home_view.dart b/app/lib/modules/home/presentation/view/home_view.dart index fb2f308f..fb6df8ee 100644 --- a/app/lib/modules/home/presentation/view/home_view.dart +++ b/app/lib/modules/home/presentation/view/home_view.dart @@ -1,12 +1,8 @@ -import 'dart:io' show Platform; - -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:mq_ci_keys/mq_ci_keys.dart'; import 'package:my_quran/config/config.dart'; -import 'package:upgrader/upgrader.dart'; import 'package:my_quran/core/core.dart'; import 'package:my_quran/app/app.dart'; @@ -29,101 +25,78 @@ class _HomeViewState extends State { final authCubit = context.read(); final user = authCubit.state.user; final validName = user?.username.replaceAll(RegExp(r'\W+'), '_'); - if (homeCubit.state.status != FetchStatus.success && authCubit.state.user != null) { - MqCrashlytics.setUserIdentifier( - validName ?? user!.accessToken, - ); - MqAnalytic.setUserProperty(validName ?? user!.accessToken); - homeCubit.getData(user!.accessToken); + if (homeCubit.state.status != FetchStatus.success && user != null) { + MqCrashlytics.setUserIdentifier(validName ?? user.accessToken); + MqAnalytic.setUserProperty(validName ?? user.accessToken); + homeCubit.getData(user.accessToken); } super.initState(); } - @override - Widget build(BuildContext context) { - return Scaffold( - body: RepositoryProvider.of(context).isIntegrationTest - ? const HomeBody() - : UpgradeAlert( - dialogStyle: kIsWeb || Platform.isAndroid ? UpgradeDialogStyle.material : UpgradeDialogStyle.cupertino, - shouldPopScope: () => true, - barrierDismissible: true, - child: const HomeBody(), - ), - ); - } -} - -class HomeBody extends StatelessWidget { - const HomeBody({super.key}); - @override Widget build(BuildContext context) { final l10n = context.l10n; final homeCubit = context.watch(); final colorScheme = Theme.of(context).colorScheme; - return RefreshIndicator( - onRefresh: () async { - MqAnalytic.track(AnalyticKey.refreshHomePage); - if (context.read().state.user != null) { - await context.read().getData(context.read().state.user!.accessToken); - } - }, - child: SafeArea( - child: ListView( - key: const Key(MqKeys.homeListView), - children: [ - const SizedBox(height: 10), - Assets.icons.alQuran.svg( - key: const Key(MqKeys.alQuran), - height: 100, - colorFilter: ColorFilter.mode(colorScheme.primary, BlendMode.srcIn), - ), - HomeCard( - key: const Key(MqKeys.allHatimCount), - titleText: l10n.homeAllHatim, - descriptioText: l10n.homeAllHatimDesc, - valueText: '${homeCubit.state.homeModel?.allDoneHatims ?? 0}', - ), - HomeCard( - key: const Key(MqKeys.allReadedPageCount), - titleText: l10n.allDonePages, - descriptioText: l10n.allDonePagesDesc, - valueText: '${homeCubit.state.homeModel?.allDonePages ?? 0}', - verticalSpace: 0, - ), - HomeCard( - titleText: l10n.homeUserReadAllPage, - descriptioText: l10n.homeUserReadAllPageDesc, - valueText: '${homeCubit.state.homeModel?.donePages ?? 0}', - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: BlocBuilder( - builder: (context, state) { - return CustomButton( - key: const Key(MqKeys.participantToHatim), - text: l10n.homeGoHatim, - onPressed: () { - if (state.isHatimEnable) { - MqAnalytic.track(AnalyticKey.goHatim); - context.goNamed(AppRouter.hatim); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.hatimNotAvailable), - ), - ); - } - }, - ); - }, + return Scaffold( + body: RefreshIndicator( + onRefresh: () async { + MqAnalytic.track(AnalyticKey.refreshHomePage); + final user = context.read().state.user; + if (user != null) { + await context.read().getData(user.accessToken); + } + }, + child: SafeArea( + child: ListView( + key: const Key(MqKeys.homeListView), + children: [ + const SizedBox(height: 10), + Assets.icons.alQuran.svg( + key: const Key(MqKeys.alQuran), + height: 100, + colorFilter: ColorFilter.mode(colorScheme.primary, BlendMode.srcIn), + ), + HomeCard( + key: const Key(MqKeys.allHatimCount), + titleText: l10n.homeAllHatim, + descriptioText: l10n.homeAllHatimDesc, + valueText: '${homeCubit.state.homeModel?.allDoneHatims ?? 0}', + ), + HomeCard( + key: const Key(MqKeys.allReadedPageCount), + titleText: l10n.allDonePages, + descriptioText: l10n.allDonePagesDesc, + valueText: '${homeCubit.state.homeModel?.allDonePages ?? 0}', + verticalSpace: 0, ), - ), - const SizedBox(height: 20), - ], + HomeCard( + titleText: l10n.homeUserReadAllPage, + descriptioText: l10n.homeUserReadAllPageDesc, + valueText: '${homeCubit.state.homeModel?.donePages ?? 0}', + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: BlocBuilder( + builder: (context, state) { + return CustomButton( + key: const Key(MqKeys.participantToHatim), + text: l10n.homeGoHatim, + onPressed: state.isHatimEnable ? _onJoinToHatim : null, + ); + }, + ), + ), + const SizedBox(height: 20), + ], + ), ), ), ); } + + void _onJoinToHatim() { + MqAnalytic.track(AnalyticKey.goHatim); + context.goNamed(AppRouter.hatim); + } } diff --git a/app/pubspec.lock b/app/pubspec.lock index 7417f7e3..ffe8d9c8 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: a315d1c444402c3fa468de626d33a1c666041c87e9e195e8fb355b7084aefcc1 + sha256: b46f62516902afb04befa4b30eb6a12ac1f58ca8cb25fb9d632407259555dd3d url: "https://pub.dev" source: hosted - version: "1.3.38" + version: "1.3.39" analyzer: dependency: transitive description: @@ -249,14 +249,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" - csslib: - dependency: transitive - description: - name: csslib - sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" - url: "https://pub.dev" - source: hosted - version: "1.0.0" cupertino_icons: dependency: "direct main" description: @@ -289,22 +281,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.10" - device_info_plus: - dependency: transitive - description: - name: device_info_plus - sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91 - url: "https://pub.dev" - source: hosted - version: "10.1.0" - device_info_plus_platform_interface: - dependency: transitive - description: - name: device_info_plus_platform_interface - sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 - url: "https://pub.dev" - source: hosted - version: "7.0.0" equatable: dependency: "direct main" description: @@ -341,58 +317,58 @@ packages: dependency: "direct main" description: name: firebase_analytics - sha256: "726596f4ac3352978238274c33234435e61bdb811484ea3d6a2b857bf47a2715" + sha256: "2017da2cb0745fa912e13aadfc94e49691796cf6b7ed37edbca2a2388713da11" url: "https://pub.dev" source: hosted - version: "11.1.0" + version: "11.2.0" firebase_analytics_platform_interface: dependency: transitive description: name: firebase_analytics_platform_interface - sha256: db445c727aa38038f91a3c6f6873d045a6740c79d03c0b6c61959e0c6ecfd771 + sha256: "4ea00fe31ff74c92c613e082193b55bd21f0653b536dd77cc1ba4cf60771d214" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.2.0" firebase_analytics_web: dependency: transitive description: name: firebase_analytics_web - sha256: "49a8a5ca0bf7fd7541e4b0915b8eb99816edc7a610d5078b89ae013a813fd567" + sha256: e5ce84f0c4e6fb8f8ca673d009445938a61845d8fa6b42c11913d5bbdbedf300 url: "https://pub.dev" source: hosted - version: "0.5.8" + version: "0.5.9" firebase_auth: dependency: "direct main" description: name: firebase_auth - sha256: "087fdcb54b0af6f4c5c756e1db4f90e9b65871b9b3a75fabaa0e0ee578301669" + sha256: a41b56878fa6aef3ea52962329b47eee333672d4b0ecc406e071b9fc729f242c url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.1.2" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: "8fac689f71ac3489a785579e99a4bad24a93ad3d78c313fb786ee517012d25f1" + sha256: d1c68097588f3b75ef79a22102ff96c311735c254353bccf6824d19f1a7e86b9 url: "https://pub.dev" source: hosted - version: "7.4.1" + version: "7.4.2" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: "486b2527fcfcab01278378d8d4791f4f7bee8a9f15bf35e801ba08fbdd84c234" + sha256: e66ec0ae5697ee39ccd4865d6887cb0df220dd4ea0b21404910c68ca4c1a731a url: "https://pub.dev" source: hosted - version: "5.12.3" + version: "5.12.4" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "1e06b0538ab3108a61d895ee16951670b491c4a94fce8f2d30e5de7a5eca4b28" + sha256: "5159984ce9b70727473eb388394650677c02c925aaa6c9439905e1f30966a4d5" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.2.0" firebase_core_platform_interface: dependency: transitive description: @@ -405,50 +381,50 @@ packages: dependency: transitive description: name: firebase_core_web - sha256: "6643fe3dbd021e6ccfb751f7882b39df355708afbdeb4130fc50f9305a9d1a3d" + sha256: "23509cb3cddfb3c910c143279ac3f07f06d3120f7d835e4a5d4b42558e978712" url: "https://pub.dev" source: hosted - version: "2.17.2" + version: "2.17.3" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: "54c06fa45585ed77e978b049f8e488db7677313d5dc808c54d24384a6e5bf0c8" + sha256: da32da3b441d1bee73ca990085a3ae174b9fb3585229f02a278a2ea42454d784 url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.0.3" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: "8ec63ebefe9233d3cdc744f75d5b88cf16b6241d8680e6284c2d272bcb0a10af" + sha256: b7567106ed57bbadaa0610774cc17a10b82ed04a1aba99790f303385ac4ba78f url: "https://pub.dev" source: hosted - version: "3.6.38" + version: "3.6.39" firebase_remote_config: dependency: "direct main" description: name: firebase_remote_config - sha256: a554ac6d87e3f45d52fda2df664787311d2ece301c6de9d438acce3c3f8bdc9c + sha256: aa150fcbaa1fe5afcb912ccf6a059f1a8ef8566dceccaa45ff72c8498ca2103e url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "5.0.3" firebase_remote_config_platform_interface: dependency: transitive description: name: firebase_remote_config_platform_interface - sha256: c419b12dd5c15b72ceb501e1472cfdeb9924372adc2b3bcf20e37bc5a4aa0984 + sha256: "0c4f4b473074ab37b069360629998dbc7175c334afc249051d8ad590521741a8" url: "https://pub.dev" source: hosted - version: "1.4.38" + version: "1.4.39" firebase_remote_config_web: dependency: transitive description: name: firebase_remote_config_web - sha256: ac525f7d67d1e41aef15c47f5f8285681841ce6fadc23ca6a5a0586e7a7ce52d + sha256: "45fcb61f5bd46eada6dc11d7167512e3441db50cfadd05394272fc1fdce4a999" url: "https://pub.dev" source: hosted - version: "1.6.10" + version: "1.6.11" fixnum: dependency: transitive description: @@ -631,14 +607,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" - html: - dependency: transitive - description: - name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" - url: "https://pub.dev" - source: hosted - version: "0.15.4" http: dependency: "direct main" description: @@ -869,14 +837,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" - os_detect: - dependency: transitive - description: - name: os_detect - sha256: faf3bcf39515e64da8ff76b2f2805b20a6ff47ae515393e535f8579ff91d6b7f - url: "https://pub.dev" - source: hosted - version: "2.0.1" package_config: dependency: transitive description: @@ -1330,14 +1290,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" - upgrader: - dependency: "direct main" - description: - name: upgrader - sha256: d45483694620883107c2f5ca1dff7cdd4237b16810337a9c9c234203eb79eb5f - url: "https://pub.dev" - source: hosted - version: "10.3.0" url_launcher: dependency: "direct main" description: @@ -1442,14 +1394,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - version: - dependency: transitive - description: - name: version - sha256: "3d4140128e6ea10d83da32fef2fa4003fccbf6852217bb854845802f04191f94" - url: "https://pub.dev" - source: hosted - version: "3.0.2" very_good_analysis: dependency: "direct dev" description: @@ -1514,14 +1458,6 @@ packages: url: "https://pub.dev" source: hosted version: "5.5.1" - win32_registry: - dependency: transitive - description: - name: win32_registry - sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" - url: "https://pub.dev" - source: hosted - version: "1.1.3" xdg_directories: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index d9b932d6..4517a988 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -15,11 +15,11 @@ dependencies: flutter_localizations: sdk: flutter cupertino_icons: ^1.0.8 - firebase_core: ^3.1.1 - firebase_auth: ^5.1.1 - firebase_crashlytics: ^4.0.2 - firebase_analytics: ^11.1.0 - firebase_remote_config: ^5.0.2 + firebase_core: ^3.2.0 + firebase_auth: ^5.1.2 + firebase_crashlytics: ^4.0.3 + firebase_analytics: ^11.2.0 + firebase_remote_config: ^5.0.3 google_sign_in: ^6.2.1 web_socket_channel: ^2.4.5 # 3.0.0 sign_in_with_apple: ^6.1.1 @@ -35,7 +35,6 @@ dependencies: url_launcher: ^6.3.0 json_annotation: ^4.9.0 font_awesome_flutter: ^10.7.0 - upgrader: ^10.3.0 just_audio: ^0.9.38 just_audio_background: ^0.0.1-beta.12 meta: ^1.12.0 # 1.15.0 diff --git a/app/test/helpers/pump_app.dart b/app/test/helpers/pump_app.dart index e2e1bd07..9f55d757 100644 --- a/app/test/helpers/pump_app.dart +++ b/app/test/helpers/pump_app.dart @@ -22,7 +22,7 @@ extension PumpApp on WidgetTester { PatchLocaleCodeUseCase patchLocaleCodeUseCase, LogoutUseCase logoutUseCase, MqRemoteConfig remoteConfig, - PackageInfo packageIngo, + PackageInfo packageInfo, ) { return pumpWidget( MultiRepositoryProvider( @@ -55,7 +55,10 @@ extension PumpApp on WidgetTester { create: (context) => HomeCubit(GetHomeDataUseCase(homeRepo)), ), BlocProvider( - create: (context) => RemoteConfigCubit(packageIngo, remoteConfig), + create: (context) => RemoteConfigCubit( + packageInfo: packageInfo, + remoteConfig: remoteConfig, + ), ), ], child: const QuranApp(), diff --git a/app/test/mocks/app_mocks.dart b/app/test/mocks/app_mocks.dart new file mode 100644 index 00000000..449dc93d --- /dev/null +++ b/app/test/mocks/app_mocks.dart @@ -0,0 +1,61 @@ +import 'package:firebase_remote_config/firebase_remote_config.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +import 'package:mocktail/mocktail.dart'; +import 'package:mq_storage/mq_storage.dart'; +import 'package:my_quran/core/core.dart'; +import 'package:my_quran/modules/modules.dart'; + +final class MockPreferencesStorage extends Mock implements PreferencesStorage {} + +final class MockRemoteClient extends Mock implements RemoteClient {} + +final class MockSccialAuth extends Mock implements SoccialAuth {} + +final class MockPackageInfo extends Mock implements PackageInfo { + @override + String get version => '1.3.0'; + + @override + String get buildNumber => '10'; +} + +final class MockHomeRepositoryImpl implements HomeRepository { + @override + Future getData(String token) async { + return const HomeEntity(allDoneHatims: 8, allDonePages: 5325, donePages: 634); + } +} + +final class MockFirebaseRemoteConfig extends Mock implements FirebaseRemoteConfig { + @override + Stream get onConfigUpdated { + return const Stream.empty(); + } +} + +class MockMqRemoteConfig implements MqRemoteConfig { + @override + String get buildNumber => '1.3.0'; + + @override + int get currentBuildNumber => 10; + + @override + Future initialise() async {} + + @override + bool get hatimIsEnable => false; + + @override + int get recommendedBuildNumber => 10; + + @override + int get requiredBuildNumber => 10; + + @override + (int, int) get version => (10, 10); + + @override + FirebaseRemoteConfig get remoteConfig => MockFirebaseRemoteConfig(); +} diff --git a/app/test/widget_test.dart b/app/test/widget_test.dart index 481d0c9e..7b154882 100644 --- a/app/test/widget_test.dart +++ b/app/test/widget_test.dart @@ -1,69 +1,11 @@ -import 'package:firebase_remote_config/firebase_remote_config.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:mq_storage/mq_storage.dart'; import 'package:my_quran/app/app.dart'; import 'package:my_quran/constants/contants.dart'; -import 'package:my_quran/core/core.dart'; -import 'package:my_quran/modules/modules.dart'; -import 'package:package_info_plus/package_info_plus.dart'; import 'helpers/helpers.dart'; - -final class MockPreferencesStorage extends Mock implements PreferencesStorage {} - -final class MockRemoteClient extends Mock implements RemoteClient {} - -final class MockSccialAuth extends Mock implements SoccialAuth {} - -final class MockPackageInfo extends Mock implements PackageInfo { - @override - String get version => '1.3.0'; - - @override - String get buildNumber => '10'; -} - -final class MockHomeRepositoryImpl implements HomeRepository { - @override - Future getData(String token) async { - return const HomeEntity(allDoneHatims: 8, allDonePages: 5325, donePages: 634); - } -} - -final class MockFirebaseRemoteConfig extends Mock implements FirebaseRemoteConfig { - @override - Stream get onConfigUpdated { - return const Stream.empty(); - } -} - -class MockMqRemoteConfig implements MqRemoteConfig { - @override - String get buildNumber => '1.3.0'; - - @override - int get currentBuildNumber => 10; - - @override - Future initialise() async {} - - @override - bool get hatimIsEnable => false; - - @override - int get recommendedBuildNumber => 10; - - @override - int get requiredBuildNumber => 10; - - @override - (int, int) get version => (10, 10); - - @override - FirebaseRemoteConfig get remoteConfig => MockFirebaseRemoteConfig(); -} +import 'mocks/app_mocks.dart'; // flutter test