From 125490e0381e3634cc36e0116d0dec566c7c9380 Mon Sep 17 00:00:00 2001 From: Omar Yasser Date: Sun, 7 Apr 2024 09:36:36 +0100 Subject: [PATCH] Implement global challenge --- assets/images/add_facebook_friend.png | Bin 56263 -> 0 bytes lib/models/global_challenge.dart | 23 ++ .../facebook_authentication_request_body.dart | 14 - ...nish_custom_simple_challenge_response.dart | 14 + .../finish_global_challenge_response.dart | 14 + .../get_global_challenge_response.dart | 21 ++ lib/net/api_interface/response_base.dart | 9 +- lib/net/endpoints.dart | 6 + lib/net/net_services/challenges_service.dart | 32 ++- lib/services/cache_manager.dart | 2 +- lib/utils/app_localizations.dart | 16 ++ lib/utils/features.dart | 1 + lib/views/auth/auth_main_screen.dart | 1 - .../add_friend/facebook_friends_screen.dart | 39 --- .../invite_facebook_friend_widget.dart | 88 ------ .../all_challenges/all_challenges_widget.dart | 10 +- .../challenge_list_item_widget.dart | 18 +- .../global_challenge_widget.dart | 230 +++++++++++++++ ...create_custom_simple_challenge_screen.dart | 3 +- .../select_azkar/write_zekr_screen.dart | 26 +- .../do_azkar_challenge_list_item_widget.dart | 5 +- .../do_azkar_challenge_screen.dart | 15 +- .../do_global_challenge_screen.dart | 269 ++++++++++++++++++ .../core_views/home/home_main_screen.dart | 11 +- .../user_progress_loading_widget.dart | 76 +++-- .../user_progress/user_progress_widget.dart | 98 +++---- lib/views/onboarding/onboarding_screen.dart | 7 - pubspec.yaml | 9 +- 28 files changed, 759 insertions(+), 298 deletions(-) delete mode 100644 assets/images/add_facebook_friend.png create mode 100644 lib/models/global_challenge.dart delete mode 100644 lib/net/api_interface/authentication/requests/facebook_authentication_request_body.dart create mode 100644 lib/net/api_interface/challenges/responses/finish_custom_simple_challenge_response.dart create mode 100644 lib/net/api_interface/challenges/responses/finish_global_challenge_response.dart create mode 100644 lib/net/api_interface/challenges/responses/get_global_challenge_response.dart delete mode 100644 lib/views/core_views/friends/add_friend/facebook_friends_screen.dart delete mode 100644 lib/views/core_views/friends/add_friend/invite_facebook_friend_widget.dart create mode 100644 lib/views/core_views/home/all_challenges/global_challenge_widget.dart create mode 100644 lib/views/core_views/home/do_challenge/do_global_challenge/do_global_challenge_screen.dart diff --git a/assets/images/add_facebook_friend.png b/assets/images/add_facebook_friend.png deleted file mode 100644 index c3b315061ecaed5a09dd2adec107f32d401c4c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56263 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 Hm+1cjz19SH diff --git a/lib/models/global_challenge.dart b/lib/models/global_challenge.dart new file mode 100644 index 00000000..67048e76 --- /dev/null +++ b/lib/models/global_challenge.dart @@ -0,0 +1,23 @@ +import 'package:azkar/models/challenge.dart'; + + +class GlobalChallenge { + GlobalChallenge({ + required this.challenge, + required this.finishedCount, + }); + + Challenge challenge; + int finishedCount; + + factory GlobalChallenge.fromJson(Map json) => + GlobalChallenge( + challenge: Challenge.fromJson(json["challenge"]), + finishedCount: json["finishedCount"], + ); + + Map toJson() => { + "challenge": challenge.toJson(), + "finishedCount": finishedCount, + }; +} diff --git a/lib/net/api_interface/authentication/requests/facebook_authentication_request_body.dart b/lib/net/api_interface/authentication/requests/facebook_authentication_request_body.dart deleted file mode 100644 index 69b86a10..00000000 --- a/lib/net/api_interface/authentication/requests/facebook_authentication_request_body.dart +++ /dev/null @@ -1,14 +0,0 @@ - -import '../../request_base.dart'; - -class FacebookAuthenticationRequestBody extends RequestBodyBase { - final String token; - final String facebookUserId; - - FacebookAuthenticationRequestBody( - {required this.token, required this.facebookUserId}); - - @override - Map toJson() => - {'token': token, 'facebookUserId': facebookUserId}; -} diff --git a/lib/net/api_interface/challenges/responses/finish_custom_simple_challenge_response.dart b/lib/net/api_interface/challenges/responses/finish_custom_simple_challenge_response.dart new file mode 100644 index 00000000..e39c7967 --- /dev/null +++ b/lib/net/api_interface/challenges/responses/finish_custom_simple_challenge_response.dart @@ -0,0 +1,14 @@ +import 'package:azkar/net/api_interface/response_base.dart'; + +class FinishCustomSimpleChallengeResponse extends ResponseBase { + static FinishCustomSimpleChallengeResponse fromJson( + Map json) { + FinishCustomSimpleChallengeResponse response = + new FinishCustomSimpleChallengeResponse(); + response.setError(json); + if (response.hasError()) { + return response; + } + return response; + } +} diff --git a/lib/net/api_interface/challenges/responses/finish_global_challenge_response.dart b/lib/net/api_interface/challenges/responses/finish_global_challenge_response.dart new file mode 100644 index 00000000..b39b6cf9 --- /dev/null +++ b/lib/net/api_interface/challenges/responses/finish_global_challenge_response.dart @@ -0,0 +1,14 @@ +import 'package:azkar/net/api_interface/response_base.dart'; + +class FinishGlobalChallengeResponse extends ResponseBase { + static FinishGlobalChallengeResponse fromJson( + Map json) { + FinishGlobalChallengeResponse response = + new FinishGlobalChallengeResponse(); + response.setError(json); + if (response.hasError()) { + return response; + } + return response; + } +} diff --git a/lib/net/api_interface/challenges/responses/get_global_challenge_response.dart b/lib/net/api_interface/challenges/responses/get_global_challenge_response.dart new file mode 100644 index 00000000..48739fba --- /dev/null +++ b/lib/net/api_interface/challenges/responses/get_global_challenge_response.dart @@ -0,0 +1,21 @@ +import 'package:azkar/models/global_challenge.dart'; +import 'package:azkar/net/api_interface/response_base.dart'; + +class GetGlobalChallengeResponse extends ResponseBase { + late GlobalChallenge challenge; + + GetGlobalChallengeResponse(); + + static GetGlobalChallengeResponse fromJson(Map json) { + GetGlobalChallengeResponse response = new GetGlobalChallengeResponse(); + response.setError(json); + if (response.hasError()) { + return response; + } + response.challenge = GlobalChallenge.fromJson(json['data']); + return response; + } + + Map toJson() => + {'data': challenge.toJson()}; +} diff --git a/lib/net/api_interface/response_base.dart b/lib/net/api_interface/response_base.dart index 98d3edae..b585f518 100644 --- a/lib/net/api_interface/response_base.dart +++ b/lib/net/api_interface/response_base.dart @@ -7,8 +7,13 @@ abstract class ResponseBase { @protected setError(Map json) { - error = - new Status((json['status'] ?? const {})['code'] ?? Status.API_SUCCESS); + if (json['status'] is Map) { + error = Status((json['status']['code'] ?? Status.API_DEFAULT_ERROR)); + } else if (!json.containsKey('status')){ + error = Status(Status.API_SUCCESS); + } else { + error = Status(Status.API_DEFAULT_ERROR); + } } void setErrorMessage(int errorCode) { diff --git a/lib/net/endpoints.dart b/lib/net/endpoints.dart index 958e9d78..60ea544b 100644 --- a/lib/net/endpoints.dart +++ b/lib/net/endpoints.dart @@ -42,9 +42,11 @@ enum EndpointRoute { FINISH_MEANING_CHALLENGE, FINISH_READING_QURAN_CHALLENGE, FINISH_CUSTOM_SIMPLE_CHALLENGE, + FINISH_GLOBAL_CHALLENGE, GET_ALL_CHALLENGES, GET_ALL_CHALLENGES_IN_GROUP, GET_AZKAR_CHALLENGE, + GET_GLOBAL_CHALLENGE, DELETE_CHALLENGE, DELETE_PERSONAL_CHALLENGE, GET_ORIGINAL_CHALLENGE, @@ -175,6 +177,8 @@ class ApiRoutesUtil { case EndpointRoute.FINISH_CUSTOM_SIMPLE_CHALLENGE: assert(route.pathVariables.length == 1); return '/challenges/finish/simple/${route.pathVariables[0]}/'; + case EndpointRoute.FINISH_GLOBAL_CHALLENGE: + return '/challenges/finish/global/'; case EndpointRoute.GET_ALL_CHALLENGES: return '/challenges/v2'; case EndpointRoute.GET_ALL_CHALLENGES_IN_GROUP: @@ -183,6 +187,8 @@ class ApiRoutesUtil { case EndpointRoute.GET_AZKAR_CHALLENGE: assert(route.pathVariables.length == 1); return '/challenges/${route.pathVariables[0]}'; + case EndpointRoute.GET_GLOBAL_CHALLENGE: + return '/challenges/global'; case EndpointRoute.DELETE_CHALLENGE: assert(route.pathVariables.length == 1); return '/challenges/${route.pathVariables[0]}'; diff --git a/lib/net/net_services/challenges_service.dart b/lib/net/net_services/challenges_service.dart index 48a3f39f..cb82dbcc 100644 --- a/lib/net/net_services/challenges_service.dart +++ b/lib/net/net_services/challenges_service.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:azkar/models/azkar_challenge.dart'; import 'package:azkar/models/challenge.dart'; +import 'package:azkar/models/global_challenge.dart'; import 'package:azkar/net/api_caller.dart'; import 'package:azkar/net/api_exception.dart'; import 'package:azkar/net/api_interface/challenges/requests/add_azkar_challenge_in_group_request.dart'; @@ -18,12 +19,15 @@ import 'package:azkar/net/api_interface/challenges/responses/add_memorization_ch import 'package:azkar/net/api_interface/challenges/responses/add_reading_quran_challenge_response.dart'; import 'package:azkar/net/api_interface/challenges/responses/add_simple_custom_challenge_response.dart'; import 'package:azkar/net/api_interface/challenges/responses/delete_challenge_response.dart'; +import 'package:azkar/net/api_interface/challenges/responses/finish_custom_simple_challenge_response.dart'; +import 'package:azkar/net/api_interface/challenges/responses/finish_global_challenge_response.dart'; import 'package:azkar/net/api_interface/challenges/responses/finish_meaning_challenge_response.dart'; import 'package:azkar/net/api_interface/challenges/responses/finish_memorization_challenge_response.dart'; import 'package:azkar/net/api_interface/challenges/responses/finish_reading_quran_challenge_response.dart'; import 'package:azkar/net/api_interface/challenges/responses/get_azkar_challenge_response.dart'; import 'package:azkar/net/api_interface/challenges/responses/get_challenges_response.dart'; import 'package:azkar/net/api_interface/challenges/responses/get_finished_challenges_count_response.dart'; +import 'package:azkar/net/api_interface/challenges/responses/get_global_challenge_response.dart'; import 'package:azkar/net/api_interface/challenges/responses/update_azkar_challenge_response.dart'; import 'package:azkar/net/endpoints.dart'; import 'package:azkar/services/cache_manager.dart'; @@ -167,7 +171,21 @@ class ChallengesService { endpointRoute: EndpointRoute.FINISH_CUSTOM_SIMPLE_CHALLENGE, pathVariables: [id])); - var response = FinishReadingQuranChallengeResponse.fromJson( + var response = FinishCustomSimpleChallengeResponse.fromJson( + jsonDecode(utf8.decode(httpResponse.body.codeUnits))); + if (response.hasError()) { + throw new ApiException(response.error!); + } + + await _updateStreakDataAfterFinishingChallenge(); + } + + Future finishGlobalChallenge() async { + http.Response httpResponse = await ApiCaller.put( + route: Endpoint( + endpointRoute: EndpointRoute.FINISH_GLOBAL_CHALLENGE)); + + var response = FinishGlobalChallengeResponse.fromJson( jsonDecode(utf8.decode(httpResponse.body.codeUnits))); if (response.hasError()) { throw new ApiException(response.error!); @@ -210,6 +228,18 @@ class ChallengesService { return response.challenge!; } + Future getGlobalChallenge() async { + http.Response httpResponse = await ApiCaller.get( + route: Endpoint( + endpointRoute: EndpointRoute.GET_GLOBAL_CHALLENGE)); + var response = GetGlobalChallengeResponse.fromJson( + jsonDecode(utf8.decode(httpResponse.body.codeUnits))); + if (response.hasError()) { + throw new ApiException(response.error!); + } + return response.challenge; + } + Future deleteChallenge(String challengeId) async { ServiceProvider.cacheManager.invalidateFrequentlyChangingData(); diff --git a/lib/services/cache_manager.dart b/lib/services/cache_manager.dart index 72bfd169..a241beed 100644 --- a/lib/services/cache_manager.dart +++ b/lib/services/cache_manager.dart @@ -29,7 +29,7 @@ class CacheManager { "018"; // Saved in milliseconds since epoch static const String CACHE_KEY_LAST_FINISHED_CHALLENGE_DATE = "019"; - static const String ASKED_FOR_NOTIFICATIONS_PERMISSION = "020"; + static const String CACHE_KEY_ASKED_FOR_NOTIFICATIONS_PERMISSION = "020"; Future _prefs = SharedPreferences.getInstance(); diff --git a/lib/utils/app_localizations.dart b/lib/utils/app_localizations.dart index 278a198f..4fdf3ed5 100644 --- a/lib/utils/app_localizations.dart +++ b/lib/utils/app_localizations.dart @@ -254,6 +254,10 @@ class AppLocalizations { 'حدث خطأ أثناء تسجيل الدخول باستخدام جوجل', 'username should not be empty': 'يجب ألا يكون كود المستخدم فارغًا', 'signing in...': 'جاري تسجيل الدخول...', + 'global challenge feature': 'تحدي مشترك', + 'global challenge feature description': + 'هذا تحدي مشترك بين جميع مستخدمي التطبيق', + 'global challenge': 'تحدي مشترك', }, }; @@ -355,11 +359,23 @@ class AppLocalizations { return _localizedValues[locale.languageCode]!['delete and copy challenge']!; } + String get globalChallengeFeature { + return _localizedValues[locale.languageCode]!['global challenge feature']!; + } + String get swipeTheChallengeCardToTheRightToDeleteOrCopyAChallenge { return _localizedValues[locale.languageCode]![ 'swipe the challenge card to the right to delete or copy a challenge']!; } + String get globalChallengeFeatureDescription { + return _localizedValues[locale.languageCode]!['global challenge feature description']!; + } + + String get globalChallenge { + return _localizedValues[locale.languageCode]!['global challenge']!; + } + String get copy { return _localizedValues[locale.languageCode]!['copy']!; } diff --git a/lib/utils/features.dart b/lib/utils/features.dart index 9497118b..8ecb4f16 100644 --- a/lib/utils/features.dart +++ b/lib/utils/features.dart @@ -12,4 +12,5 @@ class Features { static const String PROFILE_SCREEN = "profile-screen-feature"; static const String LIVE_SUPPORT_SCREEN = "live-support-screen-feature"; static const String SETTING_SCREEN = "settings-screen-feature"; + static const String GLOBAL_CHALLENGE = "global-challenge-feature"; } diff --git a/lib/views/auth/auth_main_screen.dart b/lib/views/auth/auth_main_screen.dart index 56e16c0b..6b7480aa 100644 --- a/lib/views/auth/auth_main_screen.dart +++ b/lib/views/auth/auth_main_screen.dart @@ -268,7 +268,6 @@ class _AuthMainScreenState extends State await account!.authentication; googleIdToken = authentication.idToken!; } catch (error) { - print(error); setState(() { _isSigningInWithGoogle = false; }); diff --git a/lib/views/core_views/friends/add_friend/facebook_friends_screen.dart b/lib/views/core_views/friends/add_friend/facebook_friends_screen.dart deleted file mode 100644 index 21de47b6..00000000 --- a/lib/views/core_views/friends/add_friend/facebook_friends_screen.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:azkar/models/user.dart'; -import 'package:azkar/utils/app_localizations.dart'; -import 'package:azkar/views/core_views/friends/add_friend/invite_facebook_friend_widget.dart'; -import 'package:flutter/material.dart'; - -class FacebookFriendsScreen extends StatelessWidget { - final List facebookFriends; - - FacebookFriendsScreen({required this.facebookFriends}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(AppLocalizations.of(context).inviteFacebookFriends), - ), - body: Padding( - padding: const EdgeInsets.all(8.0), - child: getMainWidgetConditionally(context), - ), - ); - } - - Widget getMainWidgetConditionally(BuildContext context) { - if ((facebookFriends.length ?? 0) == 0) { - return Center( - child: Text(AppLocalizations.of(context).noFriendsFound), - ); - } - return Center( - child: ListView.builder( - itemCount: facebookFriends.length, - itemBuilder: (context, index) { - return InviteFacebookFriendWidget( - facebookFriend: facebookFriends[index]); - }, - )); - } -} diff --git a/lib/views/core_views/friends/add_friend/invite_facebook_friend_widget.dart b/lib/views/core_views/friends/add_friend/invite_facebook_friend_widget.dart deleted file mode 100644 index ffa39eb1..00000000 --- a/lib/views/core_views/friends/add_friend/invite_facebook_friend_widget.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:azkar/models/user.dart'; -import 'package:azkar/net/api_exception.dart'; -import 'package:azkar/services/service_provider.dart'; -import 'package:azkar/utils/app_localizations.dart'; -import 'package:azkar/utils/snack_bar_utils.dart'; -import 'package:flutter/material.dart'; - -class InviteFacebookFriendWidget extends StatefulWidget { - final User facebookFriend; - - InviteFacebookFriendWidget({required this.facebookFriend}); - - @override - _InviteFacebookFriendWidgetState createState() => - _InviteFacebookFriendWidgetState(); -} - -class _InviteFacebookFriendWidgetState - extends State { - bool invited = false; - - @override - Widget build(BuildContext context) { - return Container( - child: Card( - shadowColor: Colors.black, - elevation: 10, - child: Row( - children: [ - Padding( - padding: const EdgeInsets.only(right: 10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '${widget.facebookFriend.firstName} ${widget.facebookFriend.lastName}', - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), - ) - ], - ), - ), - Padding(padding: EdgeInsets.all(8)), - Expanded(child: conditionallyGetInviteButton()), - ], - ), - ), - ); - } - - Widget conditionallyGetInviteButton() { - return Padding( - padding: const EdgeInsets.only(left: 8.0), - child: ElevatedButton( - child: invited - ? Text(AppLocalizations.of(context).invited) - : Text(AppLocalizations.of(context).invite), - style: ButtonStyle( - backgroundColor: - MaterialStateProperty.all(invited ? null : Colors.green.shade400), - ), - onPressed: () => invited ? null : onInvitePressed(), - ), - ); - } - - void onInvitePressed() async { - try { - await ServiceProvider.usersService - .addFriendWithUsername(widget.facebookFriend.username); - } on ApiException catch (e) { - SnackBarUtils.showSnackBar( - context, - '${AppLocalizations.of(context).error}: ${e.errorStatus.errorMessage}', - ); - return; - } - - SnackBarUtils.showSnackBar( - context, - '${AppLocalizations.of(context).anInvitationHasBeenSentTo} ${widget.facebookFriend.firstName} ${widget.facebookFriend.lastName}', - color: Colors.green.shade400, - ); - - setState(() { - invited = true; - }); - } -} diff --git a/lib/views/core_views/home/all_challenges/all_challenges_widget.dart b/lib/views/core_views/home/all_challenges/all_challenges_widget.dart index a5408c00..e9d498d7 100644 --- a/lib/views/core_views/home/all_challenges/all_challenges_widget.dart +++ b/lib/views/core_views/home/all_challenges/all_challenges_widget.dart @@ -6,6 +6,7 @@ import 'package:azkar/utils/app_localizations.dart'; import 'package:azkar/utils/snapshot_utils.dart'; import 'package:azkar/views/core_views/home/all_challenges/challenge_list_item_loading_widget.dart'; import 'package:azkar/views/core_views/home/all_challenges/challenge_list_item_widget.dart'; +import 'package:azkar/views/core_views/home/all_challenges/global_challenge_widget.dart'; import 'package:azkar/views/core_views/home/home_main_screen.dart'; import 'package:azkar/views/keys.dart'; import 'package:flutter/material.dart'; @@ -102,8 +103,15 @@ class _AllChallengesWidgetState extends State { addAutomaticKeepAlives: true, // Cache half screen after and half screen before the current screen. cacheExtent: MediaQuery.of(context).size.height * 0.5, - itemCount: challenges.length, + itemCount: challenges.length + 1, itemBuilder: (context, index) { + if (index == 0) { + return GlobalChallengeWidget( + reloadHomeMainScreenCallback: () { + widget.reloadHomeMainScreenCallback(); + }, + ); + } return ChallengeListItemWidget( key: Key(challenges[index].getId()!), challenge: challenges[index], diff --git a/lib/views/core_views/home/all_challenges/challenge_list_item_widget.dart b/lib/views/core_views/home/all_challenges/challenge_list_item_widget.dart index fad3543b..6a9ba672 100644 --- a/lib/views/core_views/home/all_challenges/challenge_list_item_widget.dart +++ b/lib/views/core_views/home/all_challenges/challenge_list_item_widget.dart @@ -111,7 +111,6 @@ class _ChallengeListItemWidgetState extends State parent: _controller, curve: Curves.elasticIn, )); - } @override @@ -202,7 +201,8 @@ class _ChallengeListItemWidgetState extends State ), Padding( padding: const EdgeInsets.only(top: 16), - child: SnapshotUtils.getErrorWidget(context, snapshot), + child: + SnapshotUtils.getErrorWidget(context, snapshot), ) ], ), @@ -292,16 +292,16 @@ class _ChallengeListItemWidgetState extends State crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - Row( - children: [], - ), Visibility( + maintainSize: false, + maintainAnimation: false, + maintainState: false, visible: widget.challenge.challengeType == ChallengeType.AZKAR && - (widget.challenge.azkarChallenge?.motivation ?? - "".length ?? - 0) != - 0, + (((widget.challenge.azkarChallenge?.motivation ?? + "") + .length) != + 0), child: Container( width: MediaQuery.of(context).size.width * 2 / 3, child: AutoSizeText( diff --git a/lib/views/core_views/home/all_challenges/global_challenge_widget.dart b/lib/views/core_views/home/all_challenges/global_challenge_widget.dart new file mode 100644 index 00000000..5fcaa70a --- /dev/null +++ b/lib/views/core_views/home/all_challenges/global_challenge_widget.dart @@ -0,0 +1,230 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:azkar/models/challenge.dart'; +import 'package:azkar/models/global_challenge.dart'; +import 'package:azkar/services/service_provider.dart'; +import 'package:azkar/utils/app_localizations.dart'; +import 'package:azkar/utils/features.dart'; +import 'package:azkar/views/core_views/home/all_challenges/challenge_list_item_loading_widget.dart'; +import 'package:azkar/views/core_views/home/do_challenge/do_global_challenge/do_global_challenge_screen.dart'; +import 'package:azkar/views/core_views/home/home_main_screen.dart'; +import 'package:feature_discovery/feature_discovery.dart'; +import 'package:flutter/material.dart'; + +class GlobalChallengeWidget extends StatefulWidget { + final ReloadHomeMainScreenCallback reloadHomeMainScreenCallback; + + GlobalChallengeWidget({required this.reloadHomeMainScreenCallback}); + + @override + State createState() => _GlobalChallengeWidgetState(); +} + +class _GlobalChallengeWidgetState extends State + with SingleTickerProviderStateMixin { + late GlobalChallenge _globalChallenge; + late AnimationController _controller; + late Animation _offsetAnimation; + + Future getNeededData() async { + _globalChallenge = + await ServiceProvider.challengesService.getGlobalChallenge(); + } + + @override + void initState() { + super.initState(); + + _controller = AnimationController( + duration: const Duration(seconds: 2), + vsync: this, + )..repeat(reverse: true); + _offsetAnimation = Tween( + begin: Offset.zero, + end: const Offset(1.5, 0.0), + ).animate(CurvedAnimation( + parent: _controller, + curve: Curves.elasticIn, + )); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: getNeededData(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done && + !snapshot.hasError && + _globalChallenge.challenge.challengeType == ChallengeType.AZKAR) { + return SafeArea( + child: Banner( + message: 'جديد', + location: BannerLocation.topEnd, + textStyle: TextStyle(fontSize: 20), + child: Padding( + padding: const EdgeInsets.only(bottom: 4), + child: RawMaterialButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10)), + padding: EdgeInsets.all(4), + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => DoGlobalChallengeScreen( + globalChallenge: _globalChallenge, + reloadHomeMainScreenCallback: + widget.reloadHomeMainScreenCallback, + ))), + elevation: 3.0, + fillColor: Colors.white, + child: DescribedFeatureOverlay( + key: Key(Features.GLOBAL_CHALLENGE), + featureId: Features.GLOBAL_CHALLENGE, + overflowMode: OverflowMode.wrapBackground, + contentLocation: ContentLocation.below, + barrierDismissible: false, + backgroundDismissible: false, + tapTarget: Icon(Icons.public), + // The widget that will be displayed as the tap target. + title: Center( + child: Row( + children: [ + Expanded( + child: FittedBox( + child: Text( + AppLocalizations.of(context) + .globalChallengeFeature, + softWrap: true, + textAlign: TextAlign.center, + maxLines: 1, + style: TextStyle( + fontSize: 25, + )), + ), + ), + ], + ), + ), + description: Row( + children: [ + Expanded( + child: Text( + AppLocalizations.of(context) + .globalChallengeFeatureDescription, + softWrap: true, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 20, + )), + ), + ], + ), + backgroundColor: Theme.of(context).colorScheme.primary, + targetColor: Colors.white, + textColor: Colors.black, + child: Column( + children: [ + Text( + AppLocalizations.of(context).globalChallenge, + style: TextStyle( + fontSize: 35, fontWeight: FontWeight.bold), + maxLines: 1, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Padding(padding: EdgeInsets.only(right: 8)), + Expanded( + child: FittedBox( + fit: BoxFit.scaleDown, + alignment: Alignment.center, + child: Text( + _globalChallenge + .challenge.azkarChallenge!.name!, + style: TextStyle(fontSize: 35), + maxLines: 1, + textAlign: TextAlign.start, + overflow: TextOverflow.ellipsis, + ), + ), + ), + Padding(padding: EdgeInsets.only(right: 8)), + ], + ), + ), + Padding( + padding: const EdgeInsets.only( + top: 8, + ), + child: Visibility( + visible: (((_globalChallenge.challenge + .azkarChallenge?.motivation ?? + "") + .length) != + 0), + child: AutoSizeText( + _globalChallenge + .challenge.azkarChallenge?.motivation ?? + "", + overflow: TextOverflow.ellipsis, + softWrap: false, + maxLines: 3, + style: TextStyle(fontSize: 25), + textAlign: TextAlign.center, + minFontSize: 25, + ), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: Text.rich( + TextSpan( + style: TextStyle( + color: Colors.black, + fontSize: 25, + ), + children: [ + new TextSpan( + text: 'أُنهي ', + style: new TextStyle( + fontWeight: FontWeight.bold), + ), + new TextSpan( + text: _globalChallenge.finishedCount + .toString(), + style: new TextStyle( + color: Colors.green, + fontWeight: FontWeight.bold), + ), + new TextSpan( + text: ' مرة', + style: new TextStyle( + fontWeight: FontWeight.bold), + ), + ], + ), + ), + ) + ], + ), + ), + ), + ), + ), + ); + } else if (snapshot.hasError) { + return Container(); + } else { + return ChallengeListItemLoadingWidget(); + } + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/lib/views/core_views/home/create_challenge/create_custom_simple_challenge_screen.dart b/lib/views/core_views/home/create_challenge/create_custom_simple_challenge_screen.dart index 92348f52..56a20439 100644 --- a/lib/views/core_views/home/create_challenge/create_custom_simple_challenge_screen.dart +++ b/lib/views/core_views/home/create_challenge/create_custom_simple_challenge_screen.dart @@ -107,8 +107,7 @@ class _CreateCustomSimpleChallengeScreenState margin: const EdgeInsets.all(8), child: ButtonTheme( height: 50, - // ignore: deprecated_member_use - child: OutlinedButton( + child: TextButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Colors.grey), ), diff --git a/lib/views/core_views/home/create_challenge/select_azkar/write_zekr_screen.dart b/lib/views/core_views/home/create_challenge/select_azkar/write_zekr_screen.dart index 7a755803..cd80015c 100644 --- a/lib/views/core_views/home/create_challenge/select_azkar/write_zekr_screen.dart +++ b/lib/views/core_views/home/create_challenge/select_azkar/write_zekr_screen.dart @@ -74,19 +74,17 @@ class _WriteZekrScreenState extends State { ), ), Padding(padding: EdgeInsets.all(4)), - Flexible( - child: Row( - children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.only(bottom: 8, top: 8), - child: !readyToFinishChallenge(false) - ? getNotReadyButton() - : getReadyButton(), - ), + Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.only(bottom: 8, top: 8), + child: !readyToFinishChallenge(false) + ? getNotReadyButton() + : getReadyButton(), ), - ], - ), + ), + ], ), ], ), @@ -99,9 +97,7 @@ class _WriteZekrScreenState extends State { return Container( margin: const EdgeInsets.all(8), child: ButtonTheme( - height: 50, - // ignore: deprecated_member_use - child: OutlinedButton( + child: ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Colors.grey), ), diff --git a/lib/views/core_views/home/do_challenge/do_azkar_challenge/do_azkar_challenge_list_item_widget.dart b/lib/views/core_views/home/do_challenge/do_azkar_challenge/do_azkar_challenge_list_item_widget.dart index ae828fcd..d3c2e341 100644 --- a/lib/views/core_views/home/do_challenge/do_azkar_challenge/do_azkar_challenge_list_item_widget.dart +++ b/lib/views/core_views/home/do_challenge/do_azkar_challenge/do_azkar_challenge_list_item_widget.dart @@ -16,8 +16,11 @@ class DoAzkarChallengeListItemWidget extends StatefulWidget { // and will not be consistent. final AzkarChallenge challenge; + final bool keepAlive; + DoAzkarChallengeListItemWidget({ required Key key, + required this.keepAlive, required this.subChallenge, required this.callback, required this.challenge, @@ -130,5 +133,5 @@ class _DoAzkarChallengeListItemWidgetState } @override - bool get wantKeepAlive => true; + bool get wantKeepAlive => widget.keepAlive; } diff --git a/lib/views/core_views/home/do_challenge/do_azkar_challenge/do_azkar_challenge_screen.dart b/lib/views/core_views/home/do_challenge/do_azkar_challenge/do_azkar_challenge_screen.dart index 929174ae..2b98fc53 100644 --- a/lib/views/core_views/home/do_challenge/do_azkar_challenge/do_azkar_challenge_screen.dart +++ b/lib/views/core_views/home/do_challenge/do_azkar_challenge/do_azkar_challenge_screen.dart @@ -142,16 +142,14 @@ class _DoAzkarChallengeScreenState extends State ), Visibility( child: Divider(), - visible: (widget.challenge.motivation ?? - "".length ?? - 0) != - 0, + visible: (((widget.challenge.motivation ?? "") + .length) != + 0), ), Visibility( - visible: (widget.challenge.motivation ?? - "".length ?? - 0) != - 0, + visible: (((widget.challenge.motivation ?? "") + .length) != + 0), maintainSize: false, child: Row( children: [ @@ -225,6 +223,7 @@ class _DoAzkarChallengeScreenState extends State itemBuilder: (context, index) { return DoAzkarChallengeListItemWidget( key: UniqueKey(), + keepAlive: true, subChallenge: widget.challenge.subChallenges[index], challenge: widget.challenge, callback: (SubChallenge newSubChallenge) async { diff --git a/lib/views/core_views/home/do_challenge/do_global_challenge/do_global_challenge_screen.dart b/lib/views/core_views/home/do_challenge/do_global_challenge/do_global_challenge_screen.dart new file mode 100644 index 00000000..1ab5bcbb --- /dev/null +++ b/lib/views/core_views/home/do_challenge/do_global_challenge/do_global_challenge_screen.dart @@ -0,0 +1,269 @@ +import 'dart:math'; + +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:azkar/models/global_challenge.dart'; +import 'package:azkar/models/sub_challenge.dart'; +import 'package:azkar/net/api_exception.dart'; +import 'package:azkar/services/service_provider.dart'; +import 'package:azkar/utils/snack_bar_utils.dart'; +import 'package:azkar/views/core_views/home/do_challenge/do_azkar_challenge/do_azkar_challenge_list_item_widget.dart'; +import 'package:confetti/confetti.dart'; +import 'package:flutter/material.dart'; + +import '../../home_main_screen.dart'; + +class DoGlobalChallengeScreen extends StatefulWidget { + final GlobalChallenge globalChallenge; + final ReloadHomeMainScreenCallback reloadHomeMainScreenCallback; + + DoGlobalChallengeScreen({ + required this.globalChallenge, + required this.reloadHomeMainScreenCallback, + }); + + @override + _DoGlobalChallengeScreenState createState() => + _DoGlobalChallengeScreenState(); +} + +class _DoGlobalChallengeScreenState extends State + with WidgetsBindingObserver { + late ConfettiController confettiControler; + late bool _finishedConfetti; + + @override + void initState() { + super.initState(); + + List finishedSubChallenges = widget + .globalChallenge.challenge.azkarChallenge!.subChallenges + .where((subChallenge) => subChallenge.done()) + .toList(); + widget.globalChallenge.challenge.azkarChallenge!.subChallenges + .removeWhere((subChallenge) => subChallenge.done()); + widget.globalChallenge.challenge.azkarChallenge!.subChallenges + .addAll(finishedSubChallenges); + + WidgetsBinding.instance.addObserver(this); + _finishedConfetti = false; + initConfettiController(); + } + + void initConfettiController() { + confettiControler = + ConfettiController(duration: const Duration(seconds: 1)); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: AutoSizeText( + widget.globalChallenge.challenge.azkarChallenge!.name ?? "", + style: TextStyle(fontSize: 30), + ), + ), + body: Stack( + children: [ + Center( + child: Column( + children: [ + Expanded( + child: ListView.separated( + padding: EdgeInsets.all(4), + shrinkWrap: true, + scrollDirection: Axis.vertical, + itemCount: widget.globalChallenge.challenge + .azkarChallenge!.subChallenges.length, + separatorBuilder: (BuildContext context, int index) => + Padding(padding: EdgeInsets.only(bottom: 4)), + itemBuilder: (context, index) { + return DoAzkarChallengeListItemWidget( + key: UniqueKey(), + keepAlive: false, + subChallenge: widget.globalChallenge.challenge + .azkarChallenge!.subChallenges[index], + challenge: + widget.globalChallenge.challenge.azkarChallenge!, + callback: (SubChallenge newSubChallenge) async { + widget.globalChallenge.challenge.azkarChallenge! + .subChallenges[index] = newSubChallenge; + if (newSubChallenge.done()) { + setState(() { + widget.globalChallenge.challenge.azkarChallenge! + .subChallenges + .add(newSubChallenge); + widget.globalChallenge.challenge.azkarChallenge! + .subChallenges + .removeAt(index); + }); + } + + if (widget.globalChallenge.challenge.azkarChallenge! + .done()) { + finishGlobalChallenge(); + confettiControler.addListener(() { + if (confettiControler.state == + ConfettiControllerState.stopped) { + onFinishedConfetti(); + } + }); + confettiControler.play(); + } + }, + ); + }, + ), + ), + ], + ), + ), + getConfettiWidget(), + ], + )); + } + + Future finishGlobalChallenge() async { + try { + await ServiceProvider.challengesService.finishGlobalChallenge(); + } on ApiException catch (e) { + SnackBarUtils.showSnackBar(context, e.errorStatus.errorMessage); + Navigator.of(context).pop(); + } + } + + Align getConfettiWidget() { + return Align( + alignment: Alignment.topCenter, + child: ConfettiWidget( + maximumSize: Size(30, 30), + shouldLoop: false, + confettiController: confettiControler, + blastDirection: pi, + blastDirectionality: BlastDirectionality.explosive, + maxBlastForce: 10, + minBlastForce: 3, + emissionFrequency: 0.5, + numberOfParticles: 5, + gravity: 1, + ), + ); + } + + onFinishedConfetti() async { + // Avoid popping twice if confetti's controller decided to call our listner + // more than once. + if (_finishedConfetti) { + return; + } + _finishedConfetti = true; + + await await showDialog( + context: context, + builder: (_) => Center( + child: SizedBox( + width: double.maxFinite, + child: Card( + color: Theme.of(context).colorScheme.primary, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: FittedBox( + fit: BoxFit.fitWidth, + child: Text( + 'وَتَعَاوَنُوا عَلَى الْبِرِّ وَالتَّقْوَى 🔥', + style: TextStyle( + fontFamily: Theme.of(context) + .primaryTextTheme + .labelLarge! + .fontFamily, + ), + ), + ), + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text.rich( + TextSpan( + style: TextStyle( + color: Colors.black, + fontSize: 25, + ), + children: widget.globalChallenge.finishedCount == 0 + ? [ + new TextSpan( + text: + 'تهانينا! أنت أول من أنهى تحدي الأذكار المشترك بنجاح! 🎉', + style: new TextStyle( + color: Colors.grey.shade700, + fontWeight: FontWeight.bold), + ), + ] + : [ + new TextSpan( + text: 'تهانينا! لقد تم إنهاء هذا التحدي المشترك ', + style: new TextStyle( + color: Colors.grey.shade700, + fontWeight: FontWeight.bold), + ), + new TextSpan( + text: (widget.globalChallenge.finishedCount + 1) + .toString(), + style: new TextStyle( + color: Colors.green, + fontSize: 35, + fontWeight: FontWeight.bold), + ), + new TextSpan( + text: + ' مرات حتى الآن. يمكنك إنهاء هذا التحدي أكثر من مرة 🎉', + style: new TextStyle( + color: Colors.grey.shade700, + fontWeight: FontWeight.bold), + ), + ], + ), + ), + ), + RawMaterialButton( + onPressed: () { + Navigator.pop(context); + }, + elevation: 2.0, + fillColor: Colors.white, + child: Text( + '💪', + style: TextStyle(fontSize: 25), + ), + padding: EdgeInsets.all(15.0), + shape: CircleBorder(), + ), + Padding( + padding: EdgeInsets.only(top: 8), + ) + ], + ), + ), + ), + ), + ); + widget.reloadHomeMainScreenCallback(); + Navigator.of(context).pop(); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + confettiControler.dispose(); + super.dispose(); + } +} diff --git a/lib/views/core_views/home/home_main_screen.dart b/lib/views/core_views/home/home_main_screen.dart index b8fec02e..c31050c2 100644 --- a/lib/views/core_views/home/home_main_screen.dart +++ b/lib/views/core_views/home/home_main_screen.dart @@ -32,6 +32,7 @@ class _HomeMainScreenState extends State // Feature ids for every feature that we want to showcase in order. [ Features.ADD_CHALLENGE, + Features.GLOBAL_CHALLENGE, Features.CLONE_AND_DELETE, Features.CHALLENGES_SCREEN, Features.FRIENDS_SCREEN, @@ -48,11 +49,11 @@ class _HomeMainScreenState extends State await ServiceProvider.cacheManager.getPrefs(); if (Platform.isAndroid && !sharedPreferences - .containsKey(CacheManager.ASKED_FOR_NOTIFICATIONS_PERMISSION)) { + .containsKey(CacheManager.CACHE_KEY_ASKED_FOR_NOTIFICATIONS_PERMISSION)) { await Permission.notification.request(); await sharedPreferences.setBool( - CacheManager.ASKED_FOR_NOTIFICATIONS_PERMISSION, true); + CacheManager.CACHE_KEY_ASKED_FOR_NOTIFICATIONS_PERMISSION, true); } }); } @@ -63,12 +64,8 @@ class _HomeMainScreenState extends State body: SafeArea( child: Column( children: [ - Expanded( - flex: 2, - child: UserProgressWidget(), - ), + UserProgressWidget(), Flexible( - flex: 5, child: AllChallengesWidget( reloadHomeMainScreenCallback: () { setState(() {}); diff --git a/lib/views/core_views/home/user_progress/user_progress_loading_widget.dart b/lib/views/core_views/home/user_progress/user_progress_loading_widget.dart index 1b5020c3..304b7775 100644 --- a/lib/views/core_views/home/user_progress/user_progress_loading_widget.dart +++ b/lib/views/core_views/home/user_progress/user_progress_loading_widget.dart @@ -9,56 +9,46 @@ class UserProgressLoadingWidget extends StatelessWidget { highlightColor: Colors.grey[100]!, enabled: true, child: Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.only( + left: 16.0, right: 16.0, top: 8.0, bottom: 8.0), child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - child: Column( - children: [ - Flexible( - child: FittedBox( - fit: BoxFit.scaleDown, - child: Text( - 'المواظبة', - maxLines: 1, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 45, - ), - ), + Column( + children: [ + FittedBox( + fit: BoxFit.scaleDown, + child: Text( + 'المواظبة', + maxLines: 1, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 40, ), ), - Flexible( - child: LineWidget( - width: MediaQuery.of(context).size.width / 3, - ), - ), - ], - ), + ), + LineWidget( + width: MediaQuery.of(context).size.width / 3, + ), + ], ), - Expanded( - child: Column( - children: [ - Flexible( - child: FittedBox( - fit: BoxFit.scaleDown, - child: Text( - 'الإنجازات', - maxLines: 1, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 45, - ), - ), - ), - ), - Flexible( - child: LineWidget( - width: MediaQuery.of(context).size.width / 3, + Column( + children: [ + FittedBox( + fit: BoxFit.scaleDown, + child: Text( + 'الإنجازات', + maxLines: 1, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 40, ), ), - ], - ), + ), + LineWidget( + width: MediaQuery.of(context).size.width / 3, + ), + ], ), ], ), diff --git a/lib/views/core_views/home/user_progress/user_progress_widget.dart b/lib/views/core_views/home/user_progress/user_progress_widget.dart index 92326704..6e07738e 100644 --- a/lib/views/core_views/home/user_progress/user_progress_widget.dart +++ b/lib/views/core_views/home/user_progress/user_progress_widget.dart @@ -57,41 +57,39 @@ class _UserProgressWidgetState extends State !snapshot.hasError) { animationController.forward(); return Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.only( + left: 16.0, right: 16.0, top: 8.0, bottom: 8.0), child: Row( - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( + Flexible( + flex: 1, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Flexible( - child: FittedBox( - fit: BoxFit.scaleDown, - child: Text( - 'المواظبة', - maxLines: 1, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 45, - ), + FittedBox( + fit: BoxFit.scaleDown, + child: Text( + 'المواظبة', + maxLines: 1, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 40, ), ), ), - Flexible( - child: FittedBox( - fit: BoxFit.scaleDown, - child: Directionality( - textDirection: TextDirection.ltr, - // ignore: missing_required_param - child: SlideOdometerTransition( - letterWidth: 30, - odometerAnimation: consecutiveDaysAnimation, - numberTextStyle: const TextStyle( - fontSize: 45, - color: Colors.green, - fontWeight: FontWeight.bold, - ), + FittedBox( + fit: BoxFit.scaleDown, + child: Directionality( + textDirection: TextDirection.ltr, + // ignore: missing_required_param + child: SlideOdometerTransition( + letterWidth: 30, + odometerAnimation: consecutiveDaysAnimation, + numberTextStyle: const TextStyle( + fontSize: 45, + color: Colors.green, + fontWeight: FontWeight.bold, ), ), ), @@ -99,36 +97,34 @@ class _UserProgressWidgetState extends State ], ), ), - Expanded( + Padding(padding: EdgeInsets.only(left: 16)), + Padding( + padding: const EdgeInsets.all(8.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Flexible( - child: FittedBox( - fit: BoxFit.scaleDown, - child: Text( - 'الإنجازات', - maxLines: 1, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 45, - ), + FittedBox( + fit: BoxFit.scaleDown, + child: Text( + 'الإنجازات', + maxLines: 1, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 40, ), ), ), - Flexible( - child: FittedBox( - fit: BoxFit.scaleDown, - child: Directionality( - textDirection: TextDirection.ltr, - child: SlideOdometerTransition( - letterWidth: 30, - odometerAnimation: finishedCountAnimation, - numberTextStyle: const TextStyle( - fontSize: 45, - color: Colors.green, - fontWeight: FontWeight.bold, - ), + FittedBox( + fit: BoxFit.scaleDown, + child: Directionality( + textDirection: TextDirection.ltr, + child: SlideOdometerTransition( + letterWidth: 30, + odometerAnimation: finishedCountAnimation, + numberTextStyle: const TextStyle( + fontSize: 45, + color: Colors.green, + fontWeight: FontWeight.bold, ), ), ), diff --git a/lib/views/onboarding/onboarding_screen.dart b/lib/views/onboarding/onboarding_screen.dart index a3da4c34..b210b1ab 100644 --- a/lib/views/onboarding/onboarding_screen.dart +++ b/lib/views/onboarding/onboarding_screen.dart @@ -126,13 +126,6 @@ class _OnboardingScreenState extends State { image: _buildImage('add_friend.png'), decoration: pageDecoration, ), - PageViewModel( - title: "الفيسبوك", - body: - "يمكنك ربط حساب الفيسبوك الخاص بك حتى تتمكن من دعوة أصدقائك على الفيسبوك الذين قاموا بالفعل بتنزيل التطبيق وربط حسابات الفيسبوك الخاصة بهم بالتطبيق", - image: _buildImage('add_facebook_friend.png'), - decoration: pageDecoration, - ), PageViewModel( title: "تحدي الأصدقاء", body: "يمكنك اختيار الأصدقاء وتحدِّيهم لقراءة بعض الأذكار وتكرارها", diff --git a/pubspec.yaml b/pubspec.yaml index 295b743c..c45f3fc8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ description: A new Flutter project. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.14.7+148 +version: 1.14.8+149 environment: sdk: '>=2.12.0 <3.0.0' @@ -62,15 +62,9 @@ dependencies: intl: ^0.18.1 odometer: ^3.0.0 permission_handler: ^11.3.1 - collection: any - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. flutter: # The following line ensures that the Material Icons font is @@ -87,7 +81,6 @@ flutter: - assets/images/logo_fore.png - assets/images/username.png - assets/images/add_friend.png - - assets/images/add_facebook_friend.png - assets/images/challenge.png - assets/images/friends.png - assets/gts-root-r1.crt