From c1f96fe74ed7e6cb82812bdbec053331716d618a Mon Sep 17 00:00:00 2001 From: fatkhul jabal Date: Mon, 19 Feb 2024 13:46:53 +0700 Subject: [PATCH] update feature proof reading & create feature check version apps --- app/Makefile | 5 +++ .../repositories/register_user_repo.dart | 29 ++++++++++++++ .../main/presentation/pages/home_page.dart | 9 ++++- .../onboarding/model/version_model.dart | 13 +++++++ .../bloc/user_initialization_bloc.dart | 23 ++++++++--- .../bloc/user_initialization_state.dart | 5 +++ .../onboarding/presentation/pages/_pages.dart | 2 + .../presentation/pages/splash_screen.dart | 14 ++++++- .../presentation/pages/update_dialog.dart | 39 +++++++++++++++++++ .../presentation/model/quiz_exercise.dart | 2 +- .../repositories/quiz_exercise.dart | 5 +-- .../presentation/pages/task_list_page.dart | 15 ++++++- .../Flutter/GeneratedPluginRegistrant.swift | 2 + app/pubspec.lock | 18 ++++++++- app/pubspec.yaml | 5 ++- 15 files changed, 170 insertions(+), 16 deletions(-) create mode 100644 app/lib/features/onboarding/model/version_model.dart create mode 100644 app/lib/features/onboarding/presentation/pages/update_dialog.dart diff --git a/app/Makefile b/app/Makefile index 5cd93d0..a7ec93f 100644 --- a/app/Makefile +++ b/app/Makefile @@ -19,6 +19,11 @@ init: make build_runner @echo "╠ Initialize successed!" +release: + make init + @flutter pub run flutter_launcher_icons + @flutter build appbundle + dev: @flutter run lib/main.dart diff --git a/app/lib/features/authentication/register/repositories/register_user_repo.dart b/app/lib/features/authentication/register/repositories/register_user_repo.dart index 0ad9da6..5dc5bac 100644 --- a/app/lib/features/authentication/register/repositories/register_user_repo.dart +++ b/app/lib/features/authentication/register/repositories/register_user_repo.dart @@ -2,6 +2,7 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/foundation.dart'; import 'package:injectable/injectable.dart'; +import '../../../onboarding/model/version_model.dart'; import '../model/registered_user.dart'; @Injectable() @@ -111,4 +112,32 @@ class RegisterUserRepository { } return null; } + + Future getVersionApps(String version) async { + try { + final result = await FirebaseFirestore.instance + .collection('configuration') + .doc('version') + .get(); + + final allVersion = result.data()?['version'] as List; + + for (final element in allVersion) { + if (element == version) { + final storedVersion = element.toString(); + return VersionModel(version: storedVersion); + } + } + return null; + } on FirebaseException catch (e) { + if (kDebugMode) { + print("Failed with error '${e.code}': '${e.message}'"); + } + return null; + } catch (e) { + throw Exception(e.toString()); + } + } + + } diff --git a/app/lib/features/main/presentation/pages/home_page.dart b/app/lib/features/main/presentation/pages/home_page.dart index 3bd1ee7..0e3a08f 100644 --- a/app/lib/features/main/presentation/pages/home_page.dart +++ b/app/lib/features/main/presentation/pages/home_page.dart @@ -103,7 +103,7 @@ class _HomePageState extends State { text: 'Pengaturan', ), SizedBox( - height: MediaQuery.of(context).size.height - 620, + height: MediaQuery.of(context).size.height - 640, ), InkWell( onTap: () async { @@ -122,6 +122,13 @@ class _HomePageState extends State { ), ), ), + Center( + child: Text( + 'V 1.0.0', + textAlign: TextAlign.center, + style: FontTheme.greyNormal14(), + ), + ), ], ), ), diff --git a/app/lib/features/onboarding/model/version_model.dart b/app/lib/features/onboarding/model/version_model.dart new file mode 100644 index 0000000..f0fb255 --- /dev/null +++ b/app/lib/features/onboarding/model/version_model.dart @@ -0,0 +1,13 @@ +class VersionModel { + final String version; + + VersionModel({ + required this.version, + }); + + factory VersionModel.fromJson(Map json) { + return VersionModel( + version: json['version'].toString(), + ); + } +} diff --git a/app/lib/features/onboarding/presentation/bloc/user_initialization_bloc.dart b/app/lib/features/onboarding/presentation/bloc/user_initialization_bloc.dart index dfe7e6a..850ae60 100644 --- a/app/lib/features/onboarding/presentation/bloc/user_initialization_bloc.dart +++ b/app/lib/features/onboarding/presentation/bloc/user_initialization_bloc.dart @@ -4,6 +4,7 @@ import 'package:equatable/equatable.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:injectable/injectable.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import '../../../authentication/register/repositories/register_user_repo.dart'; @@ -14,6 +15,7 @@ part 'user_initialization_state.dart'; @singleton class UserInitializationBloc extends Bloc { + final RegisterUserRepository registerUserRepository; // final GoogleSignIn _googleSignIn = GoogleSignIn(); @@ -21,7 +23,7 @@ class UserInitializationBloc : super(UserInitializationInitial()) { on(_auth); } - + FutureOr _auth( OnboardingAuthEvent state, Emitter emit, @@ -31,20 +33,31 @@ class UserInitializationBloc if (creds != null) { emit(UserAuthenticated()); final userId = creds.uid; + + final packageInfo = await PackageInfo.fromPlatform(); + + final checkVersionApps = await registerUserRepository.getVersionApps( + packageInfo.version + ); + + if (checkVersionApps == null) { + return emit(UpdateAvailable()); + } + try { final data = await registerUserRepository.getById(userId); print(data); if (data == null) { - emit(UserUnregistered()); + return emit(UserUnregistered()); } else { - emit(UserRegistered()); + return emit(UserRegistered()); } } catch (e) { - emit(UserError(e.toString())); + return emit(UserError(e.toString())); } } else { - emit(UserUnauthenticated()); + return emit(UserUnauthenticated()); } } } diff --git a/app/lib/features/onboarding/presentation/bloc/user_initialization_state.dart b/app/lib/features/onboarding/presentation/bloc/user_initialization_state.dart index 2e81ff1..b1e02d9 100644 --- a/app/lib/features/onboarding/presentation/bloc/user_initialization_state.dart +++ b/app/lib/features/onboarding/presentation/bloc/user_initialization_state.dart @@ -40,6 +40,11 @@ class UserRegistered extends UserInitializationState { List get props => []; } +class UpdateAvailable extends UserInitializationState { + @override + List get props => []; +} + class UserGetDataLoading extends UserInitializationState { @override List get props => []; diff --git a/app/lib/features/onboarding/presentation/pages/_pages.dart b/app/lib/features/onboarding/presentation/pages/_pages.dart index aa11675..88dd56b 100644 --- a/app/lib/features/onboarding/presentation/pages/_pages.dart +++ b/app/lib/features/onboarding/presentation/pages/_pages.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:url_launcher/url_launcher.dart'; import '../../../../core/constants/assets.dart'; import '../bloc/user_initialization_bloc.dart'; part 'splash_screen.dart'; part 'onboarding_page.dart'; +part 'update_dialog.dart'; diff --git a/app/lib/features/onboarding/presentation/pages/splash_screen.dart b/app/lib/features/onboarding/presentation/pages/splash_screen.dart index bc19b76..c2efea3 100644 --- a/app/lib/features/onboarding/presentation/pages/splash_screen.dart +++ b/app/lib/features/onboarding/presentation/pages/splash_screen.dart @@ -1,5 +1,6 @@ part of '_pages.dart'; + class SplashScreen extends StatelessWidget { const SplashScreen({super.key}); @@ -8,7 +9,9 @@ class SplashScreen extends StatelessWidget { final size = MediaQuery.of(context).size; return BlocListener( listener: (context, state) { - if (state is UserUnauthenticated) { + if (state is UpdateAvailable) { + showUpdateDialog(context); + } else if (state is UserUnauthenticated) { context.go('/onboarding'); } else if (state is UserUnregistered) { context.go('/register'); @@ -35,3 +38,12 @@ class SplashScreen extends StatelessWidget { ); } } + +void showUpdateDialog(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + return const UpdateDialog(); + }, + ); +} diff --git a/app/lib/features/onboarding/presentation/pages/update_dialog.dart b/app/lib/features/onboarding/presentation/pages/update_dialog.dart new file mode 100644 index 0000000..a92daaa --- /dev/null +++ b/app/lib/features/onboarding/presentation/pages/update_dialog.dart @@ -0,0 +1,39 @@ +part of '_pages.dart'; + +class UpdateDialog extends StatelessWidget { + const UpdateDialog({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Versi Baru Tersedia'), + content: const SingleChildScrollView( + child: ListBody( + children: [ + Text('Sebuah versi baru dari aplikasi tersedia.'), + Text( + 'Silakan perbarui ke versi terbaru untuk ' + 'menikmati fitur dan perbaikan baru.' + ), + ], + ), + ), + actions: [ + Center( + child: TextButton( + child: const Text('Update Sekarang'), + onPressed: () async { + // Navigator.of(context).pop(); + final url = Uri.parse( + 'https://play.google.com/store/apps/details?id=com.toki.bebras_pandai&hl=en-US&ah=i6XiDj6PnW-iPzIlbXBXM-jTYjA', + ); + if (!await launchUrl(url)) { + throw Exception('Could not launch $url'); + } + }, + ), + ) + ], + ); + } +} diff --git a/app/lib/features/quiz_exercise/presentation/model/quiz_exercise.dart b/app/lib/features/quiz_exercise/presentation/model/quiz_exercise.dart index 3a9d22b..799fd9c 100644 --- a/app/lib/features/quiz_exercise/presentation/model/quiz_exercise.dart +++ b/app/lib/features/quiz_exercise/presentation/model/quiz_exercise.dart @@ -17,7 +17,7 @@ class QuizExerciseBase { factory QuizExerciseBase.fromJson(Map json) { return QuizExerciseBase( - id: json['id'] as String, + id: json['doc_id'] as String, challengeGroup: json['challenge_group'] as String, title: json['title'] as String, status: json['status'] as String?, diff --git a/app/lib/features/quiz_exercise/presentation/repositories/quiz_exercise.dart b/app/lib/features/quiz_exercise/presentation/repositories/quiz_exercise.dart index 14a8597..00a4915 100644 --- a/app/lib/features/quiz_exercise/presentation/repositories/quiz_exercise.dart +++ b/app/lib/features/quiz_exercise/presentation/repositories/quiz_exercise.dart @@ -71,13 +71,10 @@ class QuizExerciseRepository { final globalResult = await db.collection('configuration').doc('global_variables').get(); - final taskSet = globalResult.get('task_set_doc_index') as List; + final taskSet = globalResult.get('task_set_doc_$group') as List; for (final element in taskSet) { - final challengeGroup = element['challenge_group'] as String; - if (challengeGroup == group) { quizExerciseBaseList .add(QuizExerciseBase.fromJson(element as Map)); - } } return quizExerciseBaseList; diff --git a/app/lib/features/task_list/presentation/pages/task_list_page.dart b/app/lib/features/task_list/presentation/pages/task_list_page.dart index 0d0c460..41b6bb2 100644 --- a/app/lib/features/task_list/presentation/pages/task_list_page.dart +++ b/app/lib/features/task_list/presentation/pages/task_list_page.dart @@ -80,11 +80,24 @@ class _TaskListPageState extends State { return GestureDetector( onTap: () { + String cleanedTaskId; + if (task.id.startsWith('penegak_')) { + cleanedTaskId = task.id.replaceFirst('penegak_', ''); + } else if (task.id.startsWith('sikecil_')) { + cleanedTaskId = task.id.replaceFirst('sikecil_', ''); + } else if (task.id.startsWith('penggalang_')) { + cleanedTaskId = task.id.replaceFirst('penggalang_', ''); + } else if (task.id.startsWith('siaga_')) { + cleanedTaskId = task.id.replaceFirst('siaga_', ''); + } else { + cleanedTaskId = task.id; + } + context.push( Uri( path: '/task_detail', queryParameters: { - 'task_id': task.id, + 'task_id': cleanedTaskId, }, ).toString(), ); diff --git a/app/macos/Flutter/GeneratedPluginRegistrant.swift b/app/macos/Flutter/GeneratedPluginRegistrant.swift index 1ad35f2..97d31d8 100644 --- a/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -12,6 +12,7 @@ import firebase_messaging import firebase_storage import flutter_local_notifications import geolocator_apple +import package_info_plus import path_provider_foundation import printing import shared_preferences_foundation @@ -26,6 +27,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseStoragePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseStoragePlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) diff --git a/app/pubspec.lock b/app/pubspec.lock index 28904c9..a687958 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -960,6 +960,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" + url: "https://pub.dev" + source: hosted + version: "5.0.1" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + url: "https://pub.dev" + source: hosted + version: "2.0.1" path: dependency: transitive description: @@ -1558,5 +1574,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=3.13.0" diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 1d84219..73f96b2 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -1,5 +1,5 @@ name: bebras_pandai -description: Bebras description +description: Aplikasi belajar Computational Thinking oleh bebras.org # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.0+1 +version: 1.0.0+13 environment: sdk: '>=3.0.5 <4.0.0' @@ -67,6 +67,7 @@ dependencies: firebase_messaging: ^14.7.3 cached_network_svg_image: ^0.0.7 flutter_html_svg: ^3.0.0-beta.2 + package_info_plus: ^5.0.1 dev_dependencies: build_runner: null