From 1bddda45aa9a29b046d3c368b61311b6e6b7dd8b Mon Sep 17 00:00:00 2001 From: erveykee Date: Sun, 5 Feb 2023 22:34:02 -0800 Subject: [PATCH 01/30] added the flags --- .../widgets/crowdaction_details_banner.dart | 34 +++++++----- .../shared_widgets/country_icon.dart | 18 ++++++ .../shared_widgets/crowdaction_card.dart | 55 ++++++++++--------- .../micro_crowdaction_card.dart | 7 +++ 4 files changed, 76 insertions(+), 38 deletions(-) create mode 100644 lib/presentation/shared_widgets/country_icon.dart diff --git a/lib/presentation/crowdaction/crowdaction_details/widgets/crowdaction_details_banner.dart b/lib/presentation/crowdaction/crowdaction_details/widgets/crowdaction_details_banner.dart index 63f58090..7b86ec60 100644 --- a/lib/presentation/crowdaction/crowdaction_details/widgets/crowdaction_details_banner.dart +++ b/lib/presentation/crowdaction/crowdaction_details/widgets/crowdaction_details_banner.dart @@ -6,6 +6,7 @@ import 'package:shimmer/shimmer.dart'; import '../../../../core/core.dart'; import '../../../../domain/crowdaction/crowdaction.dart'; import '../../../core/collaction_icons.dart'; +import '../../../shared_widgets/country_icon.dart'; import '../../../shared_widgets/custom_fab.dart'; import '../../../shared_widgets/image_skeleton_loader.dart'; import '../../../themes/constants.dart'; @@ -73,21 +74,28 @@ class CrowdActionDetailsBanner extends StatelessWidget { ), ), ), - if (crowdAction?.hasPassword ?? false) ...[ - const Positioned( + Positioned( bottom: 10, right: 10, - child: CustomFAB( - heroTag: 'locked', - isMini: true, - color: kSecondaryColor, - child: Icon( - CollactionIcons.lock, - color: kPrimaryColor300, - ), - ), - ), - ], + child: Row( + children: [ + if (crowdAction?.hasPassword ?? false) + CustomFAB( + heroTag: 'locked', + isMini: true, + color: kSecondaryColor, + child: Icon( + CollactionIcons.lock, + color: kPrimaryColor300, + ), + ), + if (crowdAction?.location != null) + CountryIcon( + countryCode: crowdAction?.location.code ?? 'nl', + radius: 20, + ) + ], + )), ], ), ), diff --git a/lib/presentation/shared_widgets/country_icon.dart b/lib/presentation/shared_widgets/country_icon.dart new file mode 100644 index 00000000..f10106aa --- /dev/null +++ b/lib/presentation/shared_widgets/country_icon.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +class CountryIcon extends StatelessWidget { + final String countryCode; + final double? radius; + + const CountryIcon({super.key, required this.countryCode, this.radius}); + + @override + Widget build(BuildContext context) { + Image countryImg = Image.asset( + 'icons/flags/png/${countryCode.toLowerCase()}.png', + package: 'country_icons'); + + return CircleAvatar( + radius: radius ?? 20, backgroundImage: countryImg.image); + } +} diff --git a/lib/presentation/shared_widgets/crowdaction_card.dart b/lib/presentation/shared_widgets/crowdaction_card.dart index 20c748b5..cd215ce4 100644 --- a/lib/presentation/shared_widgets/crowdaction_card.dart +++ b/lib/presentation/shared_widgets/crowdaction_card.dart @@ -11,6 +11,7 @@ import '../crowdaction/crowdaction_details/widgets/participants.dart'; import '../home/widgets/password_modal.dart'; import '../routes/app_routes.gr.dart'; import '../themes/constants.dart'; +import 'country_icon.dart'; import 'custom_fab.dart'; class CrowdActionCard extends StatefulWidget { @@ -59,30 +60,31 @@ class _CrowdActionCardState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - height: 215, - decoration: BoxDecoration( - color: Colors.grey[300], - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(20.0), - topRight: Radius.circular(20.0), - ), - image: DecorationImage( - fit: BoxFit.cover, - image: CachedNetworkImageProvider( - '${dotenv.get('BASE_STATIC_ENDPOINT_URL')}/${widget.crowdAction.images.card}', - imageRenderMethodForWeb: - ImageRenderMethodForWeb.HttpGet, - errorListener: () {}, + height: 215, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20.0), + topRight: Radius.circular(20.0), + ), + image: DecorationImage( + fit: BoxFit.cover, + image: CachedNetworkImageProvider( + '${dotenv.get('BASE_STATIC_ENDPOINT_URL')}/${widget.crowdAction.images.card}', + imageRenderMethodForWeb: + ImageRenderMethodForWeb.HttpGet, + errorListener: () {}, + ), ), ), - ), - child: widget.crowdAction.hasPassword - ? Stack( - children: const [ - Positioned( - bottom: 10, - right: 10, - child: CustomFAB( + child: Stack( + children: [ + Positioned( + bottom: 10, + right: 10, + child: Wrap(spacing: 10, children: [ + if (widget.crowdAction.hasPassword) + CustomFAB( heroTag: 'locked', isMini: true, color: kSecondaryColor, @@ -91,11 +93,14 @@ class _CrowdActionCardState extends State color: kPrimaryColor300, ), ), + CountryIcon( + countryCode: widget.crowdAction.location.code, + radius: 20, ) - ], + ]), ) - : null, - ), + ], + )), const SizedBox(height: 5.0), Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/presentation/shared_widgets/micro_crowdaction_card.dart b/lib/presentation/shared_widgets/micro_crowdaction_card.dart index e00e67db..c6babe97 100644 --- a/lib/presentation/shared_widgets/micro_crowdaction_card.dart +++ b/lib/presentation/shared_widgets/micro_crowdaction_card.dart @@ -7,6 +7,7 @@ import '../home/widgets/password_modal.dart'; import '../routes/app_routes.gr.dart'; import '../themes/constants.dart'; import 'accent_chip.dart'; +import 'country_icon.dart'; import 'micro_lock.dart'; class MicroCrowdActionCard extends StatelessWidget { @@ -79,6 +80,12 @@ class MicroCrowdActionCard extends StatelessWidget { ), noMaterialTapTargetSize: true, ), + ...[ + const SizedBox(width: 10), + CountryIcon( + countryCode: crowdAction.location.code, + radius: 15), + ], if (crowdAction.hasPassword) ...[ const SizedBox(width: 10), const MicroLock(), From cf6b6acc7c21add70db567b04a02ab1f785bdf56 Mon Sep 17 00:00:00 2001 From: Isaac Obella Date: Wed, 8 Feb 2023 13:51:57 +0300 Subject: [PATCH 02/30] core: Upgrade to flutter 3.7.1 Signed-off-by: Isaac Obella --- .github/workflows/ci.yml | 2 +- ios/Podfile.lock | 144 ++--- ios/Runner.xcodeproj/project.pbxproj | 4 +- ios/Runner/Info.plist | 6 +- pubspec.lock | 838 +++++++++++++++++---------- pubspec.yaml | 22 +- 6 files changed, 609 insertions(+), 407 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdf2de8c..6791942f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ on: workflow_dispatch: env: - flutter_version: "3.3.4" + flutter_version: "3.7.1" flutter_channel: "stable" java_version: "12.x" diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 7a085a69..b41523c5 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,80 +1,74 @@ PODS: - country_codes (0.0.1): - Flutter - - Firebase/Auth (9.6.0): + - Firebase/Auth (10.3.0): - Firebase/CoreOnly - - FirebaseAuth (~> 9.6.0) - - Firebase/CoreOnly (9.6.0): - - FirebaseCore (= 9.6.0) - - Firebase/Crashlytics (9.6.0): + - FirebaseAuth (~> 10.3.0) + - Firebase/CoreOnly (10.3.0): + - FirebaseCore (= 10.3.0) + - Firebase/Crashlytics (10.3.0): - Firebase/CoreOnly - - FirebaseCrashlytics (~> 9.6.0) - - firebase_auth (3.11.2): - - Firebase/Auth (= 9.6.0) + - FirebaseCrashlytics (~> 10.3.0) + - firebase_auth (4.2.2): + - Firebase/Auth (= 10.3.0) - firebase_core - Flutter - - firebase_core (1.24.0): - - Firebase/CoreOnly (= 9.6.0) + - firebase_core (2.4.1): + - Firebase/CoreOnly (= 10.3.0) - Flutter - - firebase_crashlytics (2.9.0): - - Firebase/Crashlytics (= 9.6.0) + - firebase_crashlytics (3.0.11): + - Firebase/Crashlytics (= 10.3.0) - firebase_core - Flutter - - FirebaseAuth (9.6.0): - - FirebaseCore (~> 9.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/Environment (~> 7.7) - - GTMSessionFetcher/Core (< 3.0, >= 1.7) - - FirebaseCore (9.6.0): - - FirebaseCoreDiagnostics (~> 9.0) - - FirebaseCoreInternal (~> 9.0) - - GoogleUtilities/Environment (~> 7.7) - - GoogleUtilities/Logger (~> 7.7) - - FirebaseCoreDiagnostics (9.6.0): - - GoogleDataTransport (< 10.0.0, >= 9.1.4) - - GoogleUtilities/Environment (~> 7.7) - - GoogleUtilities/Logger (~> 7.7) - - nanopb (< 2.30910.0, >= 2.30908.0) - - FirebaseCoreInternal (9.6.0): - - "GoogleUtilities/NSData+zlib (~> 7.7)" - - FirebaseCrashlytics (9.6.0): - - FirebaseCore (~> 9.0) - - FirebaseInstallations (~> 9.0) - - GoogleDataTransport (< 10.0.0, >= 9.1.4) - - GoogleUtilities/Environment (~> 7.7) + - FirebaseAuth (10.3.0): + - FirebaseCore (~> 10.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/Environment (~> 7.8) + - GTMSessionFetcher/Core (< 4.0, >= 2.1) + - FirebaseCore (10.3.0): + - FirebaseCoreInternal (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/Logger (~> 7.8) + - FirebaseCoreInternal (10.5.0): + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseCrashlytics (10.3.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleDataTransport (~> 9.2) + - GoogleUtilities/Environment (~> 7.8) - nanopb (< 2.30910.0, >= 2.30908.0) - PromisesObjC (~> 2.1) - - FirebaseInstallations (9.6.0): - - FirebaseCore (~> 9.0) - - GoogleUtilities/Environment (~> 7.7) - - GoogleUtilities/UserDefaults (~> 7.7) + - FirebaseInstallations (10.5.0): + - FirebaseCore (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/UserDefaults (~> 7.8) - PromisesObjC (~> 2.1) - Flutter (1.0.0) - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) - - GoogleDataTransport (9.2.0): + - GoogleDataTransport (9.2.1): - GoogleUtilities/Environment (~> 7.7) - nanopb (< 2.30910.0, >= 2.30908.0) - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/AppDelegateSwizzler (7.10.0): + - GoogleUtilities/AppDelegateSwizzler (7.11.0): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (7.10.0): + - GoogleUtilities/Environment (7.11.0): - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.10.0): + - GoogleUtilities/Logger (7.11.0): - GoogleUtilities/Environment - - GoogleUtilities/Network (7.10.0): + - GoogleUtilities/Network (7.11.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (7.10.0)" - - GoogleUtilities/Reachability (7.10.0): + - "GoogleUtilities/NSData+zlib (7.11.0)" + - GoogleUtilities/Reachability (7.11.0): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (7.10.0): + - GoogleUtilities/UserDefaults (7.11.0): - GoogleUtilities/Logger - - GTMSessionFetcher/Core (2.3.0) + - GTMSessionFetcher/Core (3.1.0) - image_cropper (0.0.4): - Flutter - TOCropViewController (~> 2.6.1) @@ -87,17 +81,21 @@ PODS: - nanopb/encode (2.30909.0) - package_info_plus (0.4.5): - Flutter - - path_provider_ios (0.0.1): + - path_provider_foundation (0.0.1): - Flutter + - FlutterMacOS - phone_number (1.0.0): - Flutter - PhoneNumberKit/PhoneNumberKitCore (= 3.3.4) - PhoneNumberKit/PhoneNumberKitCore (3.3.4) - PromisesObjC (2.1.1) + - rive_common (0.0.1): + - Flutter - share_plus (0.0.1): - Flutter - - shared_preferences_ios (0.0.1): + - shared_preferences_foundation (0.0.1): - Flutter + - FlutterMacOS - sqflite (0.0.2): - Flutter - FMDB (>= 2.7.5) @@ -116,10 +114,11 @@ DEPENDENCIES: - image_cropper (from `.symlinks/plugins/image_cropper/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) - phone_number (from `.symlinks/plugins/phone_number/ios`) + - rive_common (from `.symlinks/plugins/rive_common/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`) @@ -129,7 +128,6 @@ SPEC REPOS: - Firebase - FirebaseAuth - FirebaseCore - - FirebaseCoreDiagnostics - FirebaseCoreInternal - FirebaseCrashlytics - FirebaseInstallations @@ -159,14 +157,16 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/image_picker_ios/ios" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" - path_provider_ios: - :path: ".symlinks/plugins/path_provider_ios/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/ios" phone_number: :path: ".symlinks/plugins/phone_number/ios" + rive_common: + :path: ".symlinks/plugins/rive_common/ios" share_plus: :path: ".symlinks/plugins/share_plus/ios" - shared_preferences_ios: - :path: ".symlinks/plugins/shared_preferences_ios/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/ios" sqflite: :path: ".symlinks/plugins/sqflite/ios" url_launcher_ios: @@ -176,34 +176,34 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: country_codes: b0900f46ad686281d5dab438e354e44ad10f5941 - Firebase: 5ae8b7cf8efce559a653aef0ad95bab3f427c351 - firebase_auth: 07a4db69cfa447ac42cb7faa560fc100708b707c - firebase_core: 7c28ecc1e5dd74e03829ac3e9ff5ba3314e737a9 - firebase_crashlytics: 520a59314eaaadb34f9be4c2a285d99cfa88ebdb - FirebaseAuth: e4a5d3c36e778e41141b91cc861103a441d80bcc - FirebaseCore: 2082fffcd855f95f883c0a1641133eb9bbe76d40 - FirebaseCoreDiagnostics: 99a495094b10a57eeb3ae8efa1665700ad0bdaa6 - FirebaseCoreInternal: bca76517fe1ed381e989f5e7d8abb0da8d85bed3 - FirebaseCrashlytics: 3210572ddb77801e5a0bd9d7bc890769f2066a0c - FirebaseInstallations: 0a115432c4e223c5ab20b0dbbe4cbefa793a0e8e + Firebase: f92fc551ead69c94168d36c2b26188263860acd9 + firebase_auth: d1a0302ad8f1d642fc1cb53c43d25a951d70e190 + firebase_core: bf59c32d2e53814f558efa20840c1902fa2fe461 + firebase_crashlytics: b19405931669c882e0d08542e69ae9e86633c636 + FirebaseAuth: 0e415d29d846c1dce2fb641e46f35e9888d9bec6 + FirebaseCore: 988754646ab3bd4bdcb740f1bfe26b9f6c0d5f2a + FirebaseCoreInternal: e463f41bb935cd049505bf7e9a5bdd7dcea90df6 + FirebaseCrashlytics: f20d956f8229010b645e534693c39e0b7843c268 + FirebaseInstallations: 935bc4abb6f7a035cab7a0c31cb777b2be3dd254 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f - GoogleUtilities: bad72cb363809015b1f7f19beb1f1cd23c589f95 - GTMSessionFetcher: 3a63d75eecd6aa32c2fc79f578064e1214dfdec2 + GoogleDataTransport: ea169759df570f4e37bdee1623ec32a7e64e67c4 + GoogleUtilities: c2bdc4cf2ce786c4d2e6b3bcfd599a25ca78f06f + GTMSessionFetcher: c9e714f7eec91a55641e2bab9f45fd83a219b882 image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98 image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e - path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 + path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 phone_number: ad6f917e4fd2e507a198b93dca5eadcb463ec8c2 PhoneNumberKit: 441e8b26ec88d598e3591de9061eff18f5dd12e8 PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb + rive_common: e35144d3b86972f8760b6c9f8b120dfd5fadfa13 share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 - shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad + shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863 - url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de + url_launcher_ios: fb12c43172927bb5cf75aeebd073f883801f1993 webview_flutter_wkwebview: b7e70ef1ddded7e69c796c7390ee74180182971f PODFILE CHECKSUM: f10c0438b63bc24e6bbc207956dc27d16c4408f2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 81e97647..4da0fede 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -208,6 +208,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -222,6 +223,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index a986a2a3..1e5b133b 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -70,5 +70,7 @@ com.apple.developer.associated-domains - - \ No newline at end of file + UIApplicationSupportsIndirectInputEvents + + + diff --git a/pubspec.lock b/pubspec.lock index 04868a93..bbb6d5a5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,415 +5,474 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - url: "https://pub.dartlang.org" + sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8" + url: "https://pub.dev" source: hosted version: "47.0.0" _flutterfire_internals: dependency: transitive description: name: _flutterfire_internals - url: "https://pub.dartlang.org" + sha256: "3ff770dfff04a67b0863dff205a0936784de1b87a5e99b11c693fc10e66a9ce3" + url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.12" + adaptive_number: + dependency: transitive + description: + name: adaptive_number + sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77" + url: "https://pub.dev" + source: hosted + version: "1.0.0" analyzer: dependency: transitive description: name: analyzer - url: "https://pub.dartlang.org" + sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80" + url: "https://pub.dev" source: hosted version: "4.7.0" archive: dependency: transitive description: name: archive - url: "https://pub.dartlang.org" + sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d + url: "https://pub.dev" source: hosted - version: "3.3.2" + version: "3.3.6" args: dependency: transitive description: name: args - url: "https://pub.dartlang.org" + sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611" + url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" async: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.10.0" auto_route: dependency: "direct main" description: name: auto_route - url: "https://pub.dartlang.org" + sha256: "12047baeca0e01df93165ef33275b32119d72699ab9a49dc64c20e78f586f96d" + url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "5.0.4" auto_route_generator: dependency: "direct dev" description: name: auto_route_generator - url: "https://pub.dartlang.org" + sha256: c66eaa20dbba3211cac656037f88ba836a633dda953d9f4f9f9f5809b57e4278 + url: "https://pub.dev" source: hosted version: "5.0.2" bloc: dependency: "direct main" description: name: bloc - url: "https://pub.dartlang.org" + sha256: "658a5ae59edcf1e58aac98b000a71c762ad8f46f1394c34a52050cafb3e11a80" + url: "https://pub.dev" source: hosted - version: "8.1.0" + version: "8.1.1" bloc_test: dependency: "direct dev" description: name: bloc_test - url: "https://pub.dartlang.org" + sha256: ffbb60c17ee3d8e3784cb78071088e353199057233665541e8ac6cd438dca8ad + url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "9.1.1" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" build: dependency: transitive description: name: build - url: "https://pub.dartlang.org" + sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + url: "https://pub.dev" source: hosted version: "2.3.1" build_config: dependency: transitive description: name: build_config - url: "https://pub.dartlang.org" + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" source: hosted version: "1.1.1" build_daemon: dependency: transitive description: name: build_daemon - url: "https://pub.dartlang.org" + sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf" + url: "https://pub.dev" source: hosted version: "3.1.0" build_resolvers: dependency: transitive description: name: build_resolvers - url: "https://pub.dartlang.org" + sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6" + url: "https://pub.dev" source: hosted version: "2.0.10" build_runner: dependency: "direct dev" description: name: build_runner - url: "https://pub.dartlang.org" + sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 + url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.3" build_runner_core: dependency: transitive description: name: build_runner_core - url: "https://pub.dartlang.org" + sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + url: "https://pub.dev" source: hosted - version: "7.2.6" + version: "7.2.7" built_collection: dependency: transitive description: name: built_collection - url: "https://pub.dartlang.org" + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" source: hosted version: "5.1.1" built_value: dependency: transitive description: name: built_value - url: "https://pub.dartlang.org" + sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725" + url: "https://pub.dev" source: hosted - version: "8.4.1" + version: "8.4.3" cached_network_image: dependency: "direct main" description: name: cached_network_image - url: "https://pub.dartlang.org" + sha256: fd3d0dc1d451f9a252b32d95d3f0c3c487bc41a75eba2e6097cb0b9c71491b15 + url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.2.3" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - url: "https://pub.dartlang.org" + sha256: bb2b8403b4ccdc60ef5f25c70dead1f3d32d24b9d6117cfc087f496b178594a7 + url: "https://pub.dev" source: hosted version: "2.0.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - url: "https://pub.dartlang.org" + sha256: b8eb814ebfcb4dea049680f8c1ffb2df399e4d03bf7a352c775e26fa06e02fa0 + url: "https://pub.dev" source: hosted version: "1.0.2" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" source: hosted version: "1.2.1" checked_yaml: dependency: transitive description: name: checked_yaml - url: "https://pub.dartlang.org" + sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" + url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" cli_util: dependency: transitive description: name: cli_util - url: "https://pub.dartlang.org" + sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c" + url: "https://pub.dev" source: hosted version: "0.3.5" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted version: "1.1.1" - cloud_firestore_platform_interface: - dependency: transitive - description: - name: cloud_firestore_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "5.7.7" - cloud_firestore_web: - dependency: transitive - description: - name: cloud_firestore_web - url: "https://pub.dartlang.org" - source: hosted - version: "2.8.10" code_builder: dependency: transitive description: name: code_builder - url: "https://pub.dartlang.org" + sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "4.4.0" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" convert: dependency: transitive description: name: convert - url: "https://pub.dartlang.org" + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" source: hosted version: "3.1.1" country_codes: dependency: "direct main" description: name: country_codes - url: "https://pub.dartlang.org" + sha256: d6d1a9c3c12577b24eb7f6160768b06a9d8fd3f73ad6b24dcc5b7e0ac4910056 + url: "https://pub.dev" source: hosted version: "2.2.0" country_icons: dependency: "direct main" description: name: country_icons - url: "https://pub.dartlang.org" + sha256: "836435012b42c7dcc6d585d1420ce2310d70396569ef70cf5d74c740919f7320" + url: "https://pub.dev" source: hosted version: "2.0.2" coverage: dependency: transitive description: name: coverage - url: "https://pub.dartlang.org" + sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" + url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.6.3" cross_file: dependency: transitive description: name: cross_file - url: "https://pub.dartlang.org" + sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9" + url: "https://pub.dev" source: hosted - version: "0.3.3+2" + version: "0.3.3+4" crypto: dependency: transitive description: name: crypto - url: "https://pub.dartlang.org" + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" source: hosted version: "3.0.2" dart_jsonwebtoken: dependency: transitive description: name: dart_jsonwebtoken - url: "https://pub.dartlang.org" + sha256: "2a42e97c0b8b4e9a42b24a71453635f445c7b66c7d7c81e3a429f37ead9fe778" + url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.7.1" dart_pubspec_licenses: dependency: transitive description: name: dart_pubspec_licenses - url: "https://pub.dartlang.org" + sha256: "38680e2d2fc41df3a0d435d0955b91acc382aeefcb89ef4738f8167c8288a29d" + url: "https://pub.dev" source: hosted version: "2.0.2" dart_style: dependency: transitive description: name: dart_style - url: "https://pub.dartlang.org" + sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4" + url: "https://pub.dev" source: hosted version: "2.2.4" dartz: dependency: "direct main" description: name: dartz - url: "https://pub.dartlang.org" + sha256: e6acf34ad2e31b1eb00948692468c30ab48ac8250e0f0df661e29f12dd252168 + url: "https://pub.dev" source: hosted version: "0.10.1" device_frame: dependency: transitive description: name: device_frame - url: "https://pub.dartlang.org" + sha256: afe76182aec178d171953d9b4a50a43c57c7cf3c77d8b09a48bf30c8fa04dd9d + url: "https://pub.dev" source: hosted version: "1.1.0" diff_match_patch: dependency: transitive description: name: diff_match_patch - url: "https://pub.dartlang.org" + sha256: "2efc9e6e8f449d0abe15be240e2c2a3bcd977c8d126cfd70598aee60af35c0a4" + url: "https://pub.dev" source: hosted version: "0.4.1" dots_indicator: dependency: "direct main" description: name: dots_indicator - url: "https://pub.dartlang.org" + sha256: e59dfc90030ee5a4fd4c53144a8ce97cc7a823c2067b8fb9814960cd1ae63f89 + url: "https://pub.dev" source: hosted version: "2.1.0" + ed25519_edwards: + dependency: transitive + description: + name: ed25519_edwards + sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd" + url: "https://pub.dev" + source: hosted + version: "0.3.1" email_validator: dependency: "direct main" description: name: email_validator - url: "https://pub.dartlang.org" + sha256: e9a90f27ab2b915a27d7f9c2a7ddda5dd752d6942616ee83529b686fc086221b + url: "https://pub.dev" source: hosted version: "2.1.17" equatable: dependency: "direct main" description: name: equatable - url: "https://pub.dartlang.org" + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" source: hosted version: "2.0.5" expandable_page_view: dependency: "direct main" description: name: expandable_page_view - url: "https://pub.dartlang.org" + sha256: "210dc6961cfc29f7ed42867824eb699c9a4b9b198a7c04b8bdc1c05844969dc6" + url: "https://pub.dev" source: hosted version: "1.0.17" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted version: "1.3.1" ffi: dependency: transitive description: name: ffi - url: "https://pub.dartlang.org" + sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + url: "https://pub.dev" source: hosted version: "2.0.1" file: dependency: transitive description: name: file - url: "https://pub.dartlang.org" + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" source: hosted version: "6.1.4" firebase_auth: dependency: "direct main" description: name: firebase_auth - url: "https://pub.dartlang.org" + sha256: e946a21254784116d32e497e09b851b4d03a3c65880e80d6939a720dfce88aed + url: "https://pub.dev" source: hosted - version: "3.11.2" + version: "4.2.2" firebase_auth_mocks: dependency: "direct dev" description: name: firebase_auth_mocks - url: "https://pub.dartlang.org" + sha256: c4398019066d1bd0e91a0ba6067272a3a60c7ff8b211aa9c2139b273abe98914 + url: "https://pub.dev" source: hosted - version: "0.8.6" + version: "0.10.3" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - url: "https://pub.dartlang.org" + sha256: "325d934e21826b3e7030f5018ef61927e2083b4c4fb25218ddef6ffc0012b717" + url: "https://pub.dev" source: hosted - version: "6.10.1" + version: "6.11.7" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - url: "https://pub.dartlang.org" + sha256: bf8f3093c141abd0a624e0244864154d9db694682ba0cc1fcfdf60ecb6f7f2e3 + url: "https://pub.dev" source: hosted - version: "4.6.1" + version: "5.2.2" firebase_core: dependency: "direct main" description: name: firebase_core - url: "https://pub.dartlang.org" + sha256: c129209ba55f3d4272c89fb4a4994c15bea77fb6de63a82d45fb6bc5c94e4355 + url: "https://pub.dev" source: hosted - version: "1.24.0" + version: "2.4.1" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - url: "https://pub.dartlang.org" + sha256: "5fab93f5b354648efa62e7cc829c90efb68c8796eecf87e0888cae2d5f3accd4" + url: "https://pub.dev" source: hosted - version: "4.5.1" + version: "4.5.2" firebase_core_web: dependency: transitive description: name: firebase_core_web - url: "https://pub.dartlang.org" + sha256: "18b35ce111b0a4266abf723c825bcf9d4e2519d13638cc7f06f2a8dd960c75bc" + url: "https://pub.dev" source: hosted - version: "1.7.3" + version: "2.1.0" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - url: "https://pub.dartlang.org" + sha256: "9a73325afa984d2f6599a96b4b076f27a4e571172eb0056151522e3daace43ce" + url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "3.0.11" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - url: "https://pub.dartlang.org" + sha256: bccde26b6d63447c4f1f3d0ae5d78c958eb7bcef48239eb3aa8844675684c055 + url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.3.11" fixnum: dependency: transitive description: name: fixnum - url: "https://pub.dartlang.org" + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -423,56 +482,64 @@ packages: dependency: "direct main" description: name: flutter_bloc - url: "https://pub.dartlang.org" + sha256: "434951eea948dbe87f737b674281465f610b8259c16c097b8163ce138749a775" + url: "https://pub.dev" source: hosted - version: "8.1.1" + version: "8.1.2" flutter_blurhash: dependency: transitive description: name: flutter_blurhash - url: "https://pub.dartlang.org" + sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6" + url: "https://pub.dev" source: hosted version: "0.7.0" flutter_cache_manager: dependency: transitive description: name: flutter_cache_manager - url: "https://pub.dartlang.org" + sha256: "32cd900555219333326a2d0653aaaf8671264c29befa65bbd9856d204a4c9fb3" + url: "https://pub.dev" source: hosted version: "3.3.0" flutter_dotenv: dependency: "direct main" description: name: flutter_dotenv - url: "https://pub.dartlang.org" + sha256: d9283d92059a22e9834bc0a31336658ffba77089fb6f3cc36751f1fc7c6661a3 + url: "https://pub.dev" source: hosted version: "5.0.2" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons - url: "https://pub.dartlang.org" + sha256: ce0e501cfc258907842238e4ca605e74b7fd1cdf04b3b43e86c43f3e40a1592c + url: "https://pub.dev" source: hosted - version: "0.10.0" + version: "0.11.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.dartlang.org" + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" source: hosted version: "2.0.1" flutter_oss_licenses: dependency: "direct dev" description: name: flutter_oss_licenses - url: "https://pub.dartlang.org" + sha256: eb15e1146b101dca063a177f6d339dfb85b639a9e2d2b71c18188d5fb2144869 + url: "https://pub.dev" source: hosted version: "2.0.1" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - url: "https://pub.dartlang.org" + sha256: "60fc7b78455b94e6de2333d2f95196d32cf5c22f4b0b0520a628804cb463503b" + url: "https://pub.dev" source: hosted version: "2.0.7" flutter_test: @@ -489,546 +556,632 @@ packages: dependency: "direct dev" description: name: freezed - url: "https://pub.dartlang.org" + sha256: "4179d41127bc7a67dc3f58ceec1d22f1cdf10470653cb86eda2a63f81b4920c7" + url: "https://pub.dev" source: hosted version: "2.2.0" freezed_annotation: dependency: "direct main" description: name: freezed_annotation - url: "https://pub.dartlang.org" + sha256: aeac15850ef1b38ee368d4c53ba9a847e900bb2c53a4db3f6881cbb3cb684338 + url: "https://pub.dev" source: hosted version: "2.2.0" frontend_server_client: dependency: transitive description: name: frontend_server_client - url: "https://pub.dartlang.org" + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "3.2.0" get_it: dependency: "direct main" description: name: get_it - url: "https://pub.dartlang.org" + sha256: "290fde3a86072e4b37dbb03c07bec6126f0ecc28dad403c12ffe2e5a2d751ab7" + url: "https://pub.dev" source: hosted version: "7.2.0" glob: dependency: transitive description: name: glob - url: "https://pub.dartlang.org" + sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" go_router: dependency: transitive description: name: go_router - url: "https://pub.dartlang.org" + sha256: "25ae21384b758eb80daff113fe8bfb785c2dd17b69fe4885008fe764b26fd1ca" + url: "https://pub.dev" source: hosted version: "3.1.1" graphs: dependency: transitive description: name: graphs - url: "https://pub.dartlang.org" + sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" http: dependency: "direct main" description: name: http - url: "https://pub.dartlang.org" + sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + url: "https://pub.dev" source: hosted version: "0.13.5" http_multi_server: dependency: transitive description: name: http_multi_server - url: "https://pub.dartlang.org" + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" source: hosted version: "3.2.1" http_parser: dependency: transitive description: name: http_parser - url: "https://pub.dartlang.org" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" source: hosted version: "4.0.2" image: dependency: "direct main" description: name: image - url: "https://pub.dartlang.org" + sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" + url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.3.0" image_cropper: dependency: "direct main" description: name: image_cropper - url: "https://pub.dartlang.org" + sha256: "85324928ee8a8be35a529446435ca53067865b9353c8681983472eeec66d780f" + url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.0.1" image_cropper_for_web: dependency: transitive description: name: image_cropper_for_web - url: "https://pub.dartlang.org" + sha256: "09e93a8ec0435adcaa23622ac090442872f18145d70b9ff605ffedcf97d56255" + url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.3" image_cropper_platform_interface: dependency: transitive description: name: image_cropper_platform_interface - url: "https://pub.dartlang.org" + sha256: "62349e3aab63873ea9b9ab9f69d036ab8a0d74b3004beec4303981386cb9273f" + url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" image_picker: dependency: "direct main" description: name: image_picker - url: "https://pub.dartlang.org" + sha256: f98d76672d309c8b7030c323b3394669e122d52b307d2bbd8d06bd70f5b2aabe + url: "https://pub.dev" source: hosted - version: "0.8.6" + version: "0.8.6+1" image_picker_android: dependency: transitive description: name: image_picker_android - url: "https://pub.dartlang.org" + sha256: b1cbfec0f5aef427a18eb573f5445af8c9c568626bf3388553e40c263d3f7368 + url: "https://pub.dev" source: hosted - version: "0.8.5+3" + version: "0.8.5+5" image_picker_for_web: dependency: transitive description: name: image_picker_for_web - url: "https://pub.dartlang.org" + sha256: "7d319fb74955ca46d9bf7011497860e3923bb67feebcf068f489311065863899" + url: "https://pub.dev" source: hosted version: "2.1.10" image_picker_ios: dependency: transitive description: name: image_picker_ios - url: "https://pub.dartlang.org" + sha256: "8ffb14b43713d7c43fb21299cc18181cc5b39bd3ea1cc427a085c6400fe5aa52" + url: "https://pub.dev" source: hosted - version: "0.8.6+1" + version: "0.8.6+7" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface - url: "https://pub.dartlang.org" + sha256: "7cef2f28f4f2fef99180f636c3d446b4ccbafd6ba0fad2adc9a80c4040f656b8" + url: "https://pub.dev" source: hosted version: "2.6.2" infinite_scroll_pagination: dependency: "direct main" description: name: infinite_scroll_pagination - url: "https://pub.dartlang.org" + sha256: "9517328f4e373f08f57dbb11c5aac5b05554142024d6b60c903f3b73476d52db" + url: "https://pub.dev" source: hosted version: "3.2.0" injectable: dependency: "direct main" description: name: injectable - url: "https://pub.dartlang.org" + sha256: "7dab7d341feb40a0590d9ff6261aea9495522005e2c6763f9161a4face916f7b" + url: "https://pub.dev" source: hosted - version: "1.5.3" + version: "2.1.0" injectable_generator: dependency: "direct dev" description: name: injectable_generator - url: "https://pub.dartlang.org" + sha256: "9a3bbd2c3ba821e31ef6cea3fc535c17e3a25c74e173b6cefa05f466c8338bc8" + url: "https://pub.dev" source: hosted - version: "1.5.4" + version: "2.1.3" intl: dependency: "direct main" description: name: intl - url: "https://pub.dartlang.org" + sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + url: "https://pub.dev" source: hosted - version: "0.17.0" + version: "0.18.0" io: dependency: transitive description: name: io - url: "https://pub.dartlang.org" + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" ionicons: dependency: "direct main" description: name: ionicons - url: "https://pub.dartlang.org" + sha256: "5496bc65a16115ecf05b15b78f494ee4a8869504357668f0a11d689e970523cf" + url: "https://pub.dev" source: hosted - version: "0.2.1" + version: "0.2.2" js: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" source: hosted - version: "0.6.4" + version: "0.6.5" json_annotation: dependency: "direct main" description: name: json_annotation - url: "https://pub.dartlang.org" + sha256: "3520fa844009431b5d4491a5a778603520cdc399ab3406332dcc50f93547258c" + url: "https://pub.dev" source: hosted version: "4.7.0" json_serializable: dependency: "direct dev" description: name: json_serializable - url: "https://pub.dartlang.org" + sha256: f3c2c18a7889580f71926f30c1937727c8c7d4f3a435f8f5e8b0ddd25253ef5d + url: "https://pub.dev" source: hosted - version: "6.5.3" + version: "6.5.4" lint: dependency: "direct dev" description: name: lint - url: "https://pub.dartlang.org" + sha256: "3e9343b1cededcfb1e8b40d0dbd3592b7a1c6c0121545663a991433390c2bc97" + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "2.0.1" lints: dependency: transitive description: name: lints - url: "https://pub.dartlang.org" + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" source: hosted version: "2.0.1" logging: dependency: transitive description: name: logging - url: "https://pub.dartlang.org" + sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.dev" source: hosted - version: "0.12.12" + version: "0.12.13" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.2.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" source: hosted version: "1.8.0" mime: dependency: transitive description: name: mime - url: "https://pub.dartlang.org" + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.4" + mock_exceptions: + dependency: transitive + description: + name: mock_exceptions + sha256: "6e3e623712d2c6106ffe9e14732912522b565ddaa82a8dcee6cd4441b5984056" + url: "https://pub.dev" + source: hosted + version: "0.8.2" mocktail: dependency: "direct main" description: name: mocktail - url: "https://pub.dartlang.org" + sha256: "80a996cd9a69284b3dc521ce185ffe9150cde69767c2d3a0720147d93c0cef53" + url: "https://pub.dev" source: hosted version: "0.3.0" nested: dependency: transitive description: name: nested - url: "https://pub.dartlang.org" + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" source: hosted version: "1.0.0" node_preamble: dependency: transitive description: name: node_preamble - url: "https://pub.dartlang.org" + sha256: "8ebdbaa3b96d5285d068f80772390d27c21e1fa10fb2df6627b1b9415043608d" + url: "https://pub.dev" source: hosted version: "2.0.1" octo_image: dependency: transitive description: name: octo_image - url: "https://pub.dartlang.org" + sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143" + url: "https://pub.dev" source: hosted version: "1.0.2" package_config: dependency: transitive description: name: package_config - url: "https://pub.dartlang.org" + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" source: hosted version: "2.1.0" package_info_plus: dependency: "direct main" description: name: package_info_plus - url: "https://pub.dartlang.org" + sha256: f619162573096d428ccde2e33f92e05b5a179cd6f0e3120c1005f181bee8ed16 + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - url: "https://pub.dartlang.org" + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + url: "https://pub.dev" source: hosted version: "2.0.1" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.dev" source: hosted version: "1.8.2" path_provider: dependency: transitive description: name: path_provider - url: "https://pub.dartlang.org" + sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.0.12" path_provider_android: dependency: transitive description: name: path_provider_android - url: "https://pub.dartlang.org" + sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + url: "https://pub.dev" source: hosted - version: "2.0.20" - path_provider_ios: + version: "2.0.22" + path_provider_foundation: dependency: transitive description: - name: path_provider_ios - url: "https://pub.dartlang.org" + name: path_provider_foundation + sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" + url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.1.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - url: "https://pub.dartlang.org" + sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 + url: "https://pub.dev" source: hosted version: "2.1.7" - path_provider_macos: - dependency: transitive - description: - name: path_provider_macos - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.6" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - url: "https://pub.dartlang.org" + sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + url: "https://pub.dev" source: hosted version: "2.0.5" path_provider_windows: dependency: transitive description: name: path_provider_windows - url: "https://pub.dartlang.org" + sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c + url: "https://pub.dev" source: hosted version: "2.1.3" pedantic: dependency: transitive description: name: pedantic - url: "https://pub.dartlang.org" + sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602" + url: "https://pub.dev" source: hosted version: "1.11.1" petitparser: dependency: transitive description: name: petitparser - url: "https://pub.dartlang.org" + sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.1.0" phone_number: dependency: "direct main" description: name: phone_number - url: "https://pub.dartlang.org" + sha256: "42cac4ca32146d83cc8a9c8432dc2d901d40334f0e2b02a3ec7d8237b80ebec3" + url: "https://pub.dev" source: hosted version: "1.0.0" platform: dependency: transitive description: name: platform - url: "https://pub.dartlang.org" + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" source: hosted version: "3.1.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.dartlang.org" + sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + url: "https://pub.dev" source: hosted version: "2.1.3" pointycastle: dependency: transitive description: name: pointycastle - url: "https://pub.dartlang.org" + sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346 + url: "https://pub.dev" source: hosted version: "3.6.2" pool: dependency: transitive description: name: pool - url: "https://pub.dartlang.org" + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" source: hosted version: "1.5.1" process: dependency: transitive description: name: process - url: "https://pub.dartlang.org" + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" source: hosted version: "4.2.4" provider: dependency: transitive description: name: provider - url: "https://pub.dartlang.org" + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" source: hosted - version: "6.0.4" + version: "6.0.5" pub_semver: dependency: transitive description: name: pub_semver - url: "https://pub.dartlang.org" + sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" pubspec_parse: dependency: transitive description: name: pubspec_parse - url: "https://pub.dartlang.org" + sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a" + url: "https://pub.dev" source: hosted version: "1.2.1" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" rive: dependency: "direct main" description: name: rive - url: "https://pub.dartlang.org" + sha256: c4293ea52f9bf45edeebad3b1eb0bddc284bd11f14ede54780bf5efa1c491b4a + url: "https://pub.dev" source: hosted - version: "0.9.1" + version: "0.10.1" + rive_common: + dependency: transitive + description: + name: rive_common + sha256: f98a451a5d6c8c82693b4eab1dd65413800bab69ac7f9f8fad79fd39bb5ba3ab + url: "https://pub.dev" + source: hosted + version: "0.0.2" rxdart: dependency: "direct main" description: name: rxdart - url: "https://pub.dartlang.org" + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" source: hosted - version: "0.27.5" + version: "0.27.7" share_plus: dependency: "direct main" description: name: share_plus - url: "https://pub.dartlang.org" + sha256: e387077716f80609bb979cd199331033326033ecd1c8f200a90c5f57b1c9f55e + url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "6.3.0" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - url: "https://pub.dartlang.org" + sha256: "82ddd4ab9260c295e6e39612d4ff00390b9a7a21f1bb1da771e2f232d80ab8a1" + url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.2.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - url: "https://pub.dartlang.org" + sha256: "5949029e70abe87f75cfe59d17bf5c397619c4b74a099b10116baeb34786fad9" + url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.0.17" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - url: "https://pub.dartlang.org" + sha256: "955e9736a12ba776bdd261cf030232b30eadfcd9c79b32a3250dd4a494e8c8f7" + url: "https://pub.dev" source: hosted - version: "2.0.14" - shared_preferences_ios: + version: "2.0.15" + shared_preferences_foundation: dependency: transitive description: - name: shared_preferences_ios - url: "https://pub.dartlang.org" + name: shared_preferences_foundation + sha256: "2b55c18636a4edc529fa5cd44c03d3f3100c00513f518c5127c951978efcccd0" + url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.3" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - url: "https://pub.dartlang.org" + sha256: f8ea038aa6da37090093974ebdcf4397010605fd2ff65c37a66f9d28394cb874 + url: "https://pub.dev" source: hosted - version: "2.1.1" - shared_preferences_macos: - dependency: transitive - description: - name: shared_preferences_macos - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.4" + version: "2.1.3" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - url: "https://pub.dartlang.org" + sha256: da9431745ede5ece47bc26d5d73a9d3c6936ef6945c101a5aca46f62e52c1cf3 + url: "https://pub.dev" source: hosted version: "2.1.0" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - url: "https://pub.dartlang.org" + sha256: a4b5bc37fe1b368bbc81f953197d55e12f49d0296e7e412dfe2d2d77d6929958 + url: "https://pub.dev" source: hosted version: "2.0.4" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - url: "https://pub.dartlang.org" + sha256: "5eaf05ae77658d3521d0e993ede1af962d4b326cd2153d312df716dc250f00c9" + url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.3" shelf: dependency: transitive description: name: shelf - url: "https://pub.dartlang.org" + sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + url: "https://pub.dev" source: hosted version: "1.4.0" shelf_packages_handler: dependency: transitive description: name: shelf_packages_handler - url: "https://pub.dartlang.org" + sha256: aef74dc9195746a384843102142ab65b6a4735bb3beea791e63527b88cc83306 + url: "https://pub.dev" source: hosted version: "3.0.1" shelf_static: dependency: transitive description: name: shelf_static - url: "https://pub.dartlang.org" + sha256: e792b76b96a36d4a41b819da593aff4bdd413576b3ba6150df5d8d9996d2e74c + url: "https://pub.dev" source: hosted version: "1.1.1" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - url: "https://pub.dartlang.org" + sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.3" shimmer: dependency: "direct main" description: name: shimmer - url: "https://pub.dartlang.org" + sha256: "1f1009b5845a1f88f1c5630212279540486f97409e9fc3f63883e71070d107bf" + url: "https://pub.dev" source: hosted version: "2.0.0" sky_engine: @@ -1040,317 +1193,362 @@ packages: dependency: transitive description: name: sliver_tools - url: "https://pub.dartlang.org" + sha256: bfa69411ebb203cc1a115b1e8bc7935b893eb381fdcf8d453b40c0d4e1db3247 + url: "https://pub.dev" source: hosted - version: "0.2.8" + version: "0.2.9" source_gen: dependency: transitive description: name: source_gen - url: "https://pub.dartlang.org" + sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d" + url: "https://pub.dev" source: hosted version: "1.2.6" source_helper: dependency: transitive description: name: source_helper - url: "https://pub.dartlang.org" + sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f" + url: "https://pub.dev" source: hosted version: "1.3.3" source_map_stack_trace: dependency: transitive description: name: source_map_stack_trace - url: "https://pub.dartlang.org" + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + url: "https://pub.dev" source: hosted version: "2.1.1" source_maps: dependency: transitive description: name: source_maps - url: "https://pub.dartlang.org" + sha256: "490098075234dcedb83c5d949b4c93dad5e6b7702748de000be2b57b8e6b2427" + url: "https://pub.dev" source: hosted - version: "0.10.10" + version: "0.10.11" source_span: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" sqflite: dependency: transitive description: name: sqflite - url: "https://pub.dartlang.org" + sha256: "78324387dc81df14f78df06019175a86a2ee0437624166c382e145d0a7fd9a4f" + url: "https://pub.dev" source: hosted - version: "2.1.0+1" + version: "2.2.4+1" sqflite_common: dependency: transitive description: name: sqflite_common - url: "https://pub.dartlang.org" + sha256: bfd6973aaeeb93475bc0d875ac9aefddf7965ef22ce09790eb963992ffc5183f + url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.4.2+2" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" stream_transform: dependency: transitive description: name: stream_transform - url: "https://pub.dartlang.org" + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" synchronized: dependency: transitive description: name: synchronized - url: "https://pub.dartlang.org" + sha256: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b" + url: "https://pub.dev" source: hosted - version: "3.0.0+3" + version: "3.0.1" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted version: "1.2.1" test: dependency: transitive description: name: test - url: "https://pub.dartlang.org" + sha256: a5fcd2d25eeadbb6589e80198a47d6a464ba3e2049da473943b8af9797900c2d + url: "https://pub.dev" source: hosted - version: "1.21.4" + version: "1.22.0" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.dev" source: hosted - version: "0.4.12" + version: "0.4.16" test_core: dependency: transitive description: name: test_core - url: "https://pub.dartlang.org" + sha256: "0ef9755ec6d746951ba0aabe62f874b707690b5ede0fecc818b138fcc9b14888" + url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.4.20" timing: dependency: transitive description: name: timing - url: "https://pub.dartlang.org" + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.1" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" source: hosted version: "1.3.1" url_launcher: dependency: "direct main" description: name: url_launcher - url: "https://pub.dartlang.org" + sha256: e8f2efc804810c0f2f5b485f49e7942179f56eabcfe81dce3387fec4bb55876b + url: "https://pub.dev" source: hosted - version: "6.1.6" + version: "6.1.9" url_launcher_android: dependency: transitive description: name: url_launcher_android - url: "https://pub.dartlang.org" + sha256: "3e2f6dfd2c7d9cd123296cab8ef66cfc2c1a13f5845f42c7a0f365690a8a7dd1" + url: "https://pub.dev" source: hosted - version: "6.0.19" + version: "6.0.23" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - url: "https://pub.dartlang.org" + sha256: "0a5af0aefdd8cf820dd739886efb1637f1f24489900204f50984634c07a54815" + url: "https://pub.dev" source: hosted - version: "6.0.17" + version: "6.1.0" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - url: "https://pub.dartlang.org" + sha256: "318c42cba924e18180c029be69caf0a1a710191b9ec49bb42b5998fdcccee3cc" + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - url: "https://pub.dartlang.org" + sha256: "41988b55570df53b3dd2a7fc90c76756a963de6a8c5f8e113330cb35992e2094" + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - url: "https://pub.dartlang.org" + sha256: "4eae912628763eb48fc214522e58e942fd16ce195407dbf45638239523c759a6" + url: "https://pub.dev" source: hosted version: "2.1.1" url_launcher_web: dependency: transitive description: name: url_launcher_web - url: "https://pub.dartlang.org" + sha256: "44d79408ce9f07052095ef1f9a693c258d6373dc3944249374e30eff7219ccb0" + url: "https://pub.dev" source: hosted - version: "2.0.13" + version: "2.0.14" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - url: "https://pub.dartlang.org" + sha256: b6217370f8eb1fd85c8890c539f5a639a01ab209a36db82c921ebeacefc7a615 + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.3" uuid: dependency: transitive description: name: uuid - url: "https://pub.dartlang.org" + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.7" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" vm_service: dependency: transitive description: name: vm_service - url: "https://pub.dartlang.org" + sha256: e7fb6c2282f7631712b69c19d1bff82f3767eea33a2321c14fa59ad67ea391c7 + url: "https://pub.dev" source: hosted - version: "8.3.0" + version: "9.4.0" watcher: dependency: transitive description: name: watcher - url: "https://pub.dartlang.org" + sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + url: "https://pub.dev" source: hosted version: "1.0.2" web_socket_channel: dependency: transitive description: name: web_socket_channel - url: "https://pub.dartlang.org" + sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b + url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.0" webkit_inspection_protocol: dependency: transitive description: name: webkit_inspection_protocol - url: "https://pub.dartlang.org" + sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" + url: "https://pub.dev" source: hosted version: "1.2.0" webview_flutter: dependency: "direct main" description: name: webview_flutter - url: "https://pub.dartlang.org" + sha256: f7ec234830f86d0ef2bd664e8460b0038b8c1a83ff076035cad74ac70273753c + url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "4.0.2" webview_flutter_android: dependency: transitive description: name: webview_flutter_android - url: "https://pub.dartlang.org" + sha256: "5f49a6e5fc59e21fcec5e1bbcd401afbee9792a24a4f3d9cef9b5bb0cd1e3767" + url: "https://pub.dev" source: hosted - version: "2.10.4" + version: "3.2.4" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - url: "https://pub.dartlang.org" + sha256: "8b2262dda5d26eabc600a7282a8c16a9473a0c765526afb0ffc33eef912f7968" + url: "https://pub.dev" source: hosted - version: "1.9.5" + version: "2.0.1" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - url: "https://pub.dartlang.org" + sha256: "92e7e7fa468f1df597fb9d37bcf1f303175cbe147c4dbdf06ecc323d950116eb" + url: "https://pub.dev" source: hosted - version: "2.9.5" + version: "3.0.5" widgetbook: dependency: "direct dev" description: name: widgetbook - url: "https://pub.dartlang.org" + sha256: c42a3d414d19c008d90f0b6df669c18bb151c5f3012ed073498d96cf133e9caf + url: "https://pub.dev" source: hosted version: "2.4.1" widgetbook_annotation: dependency: "direct main" description: name: widgetbook_annotation - url: "https://pub.dartlang.org" + sha256: "1f12e090865200191ab6b79b7ed4b108a191bf5de3140f92917c8c75e7e3f916" + url: "https://pub.dev" source: hosted version: "2.1.0" widgetbook_generator: dependency: "direct dev" description: name: widgetbook_generator - url: "https://pub.dartlang.org" + sha256: b6d00fef564fa5c0b98e26a72a27d52d03400c36409e56be25c0f09cfee3307b + url: "https://pub.dev" source: hosted version: "2.4.1" widgetbook_models: dependency: transitive description: name: widgetbook_models - url: "https://pub.dartlang.org" + sha256: "40899314ab0cce1a52b189ee12b79138ba59f445229f850fb326f579c8f63045" + url: "https://pub.dev" source: hosted version: "0.0.7" win32: dependency: transitive description: name: win32 - url: "https://pub.dartlang.org" + sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.3" xdg_directories: dependency: transitive description: name: xdg_directories - url: "https://pub.dartlang.org" + sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + url: "https://pub.dev" source: hosted - version: "0.2.0+2" + version: "0.2.0+3" xml: dependency: transitive description: name: xml - url: "https://pub.dartlang.org" + sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.2.2" yaml: dependency: transitive description: name: yaml - url: "https://pub.dartlang.org" + sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + url: "https://pub.dev" source: hosted version: "3.1.1" sdks: - dart: ">=2.18.2 <3.0.0" + dart: ">=2.19.0 <3.0.0" flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index 93ad1741..9d472b2e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,9 +31,9 @@ dependencies: email_validator: ^2.0.1 equatable: ^2.0.3 expandable_page_view: ^1.0.10 - firebase_auth: ^3.11.2 - firebase_core: ^1.24.0 - firebase_crashlytics: ^2.9.0 + firebase_auth: ^4.2.2 + firebase_core: ^2.4.1 + firebase_crashlytics: ^3.0.11 flutter: sdk: flutter flutter_bloc: ^8.1.1 @@ -45,36 +45,36 @@ dependencies: image_cropper: ^3.0.0 image_picker: ^0.8.6 infinite_scroll_pagination: ^3.2.0 - injectable: ^1.5.3 - intl: ^0.17.0 + injectable: ^2.1.0 + intl: ^0.18.0 ionicons: ^0.2.1 json_annotation: ^4.7.0 mocktail: ^0.3.0 package_info_plus: ^3.0.0 phone_number: ^1.0.0 - rive: ^0.9.1 + rive: ^0.10.1 rxdart: ^0.27.5 share_plus: ^6.0.0 shared_preferences: ^2.0.15 shimmer: ^2.0.0 url_launcher: ^6.1.6 - webview_flutter: ^3.0.4 + webview_flutter: ^4.0.2 widgetbook_annotation: ^2.1.0 dev_dependencies: auto_route_generator: ^5.0.2 bloc_test: ^9.1.0 build_runner: ^2.3.0 - firebase_auth_mocks: ^0.8.6 - flutter_launcher_icons: ^0.10.0 + firebase_auth_mocks: ^0.10.3 + flutter_launcher_icons: ^0.11.0 flutter_lints: ^2.0.1 flutter_oss_licenses: ^2.0.1 flutter_test: sdk: flutter freezed: ^2.2.0 - injectable_generator: ^1.5.4 + injectable_generator: ^2.1.3 json_serializable: ^6.5.3 - lint: ^1.10.0 + lint: ^2.0.1 widgetbook: ^2.4.1 widgetbook_generator: ^2.4.1 From cc59396c9c2bca74b98296f689cf0ac9f943aa65 Mon Sep 17 00:00:00 2001 From: Isaac Obella Date: Wed, 8 Feb 2023 13:52:22 +0300 Subject: [PATCH 03/30] fix: Webview upgrade Signed-off-by: Isaac Obella --- .../shared_widgets/web_view_page.dart | 83 ++++++++++++++-- .../shared_widgets/web_view.mocks.dart | 99 +++++++++++++++++++ .../shared_widgets/web_view_page_test.dart | 26 ++++- 3 files changed, 196 insertions(+), 12 deletions(-) create mode 100644 test/presentation/shared_widgets/web_view.mocks.dart diff --git a/lib/presentation/shared_widgets/web_view_page.dart b/lib/presentation/shared_widgets/web_view_page.dart index 6525a308..9a64228f 100644 --- a/lib/presentation/shared_widgets/web_view_page.dart +++ b/lib/presentation/shared_widgets/web_view_page.dart @@ -5,24 +5,93 @@ import 'package:webview_flutter/webview_flutter.dart'; import 'custom_app_bars/custom_appbar.dart'; -class WebViewPage extends StatelessWidget { +class WebViewPage extends StatefulWidget { final String url; final String title; const WebViewPage({super.key, required this.url, this.title = ""}); @override - Widget build(BuildContext context) { - if (Platform.isAndroid) { - WebView.platform = AndroidWebView(); - } + State createState() => _WebViewPageState(); +} + +class _WebViewPageState extends State { + late final WebViewController _controller; + late final ValueNotifier _loading; + late final ValueNotifier _error; + + @override + void initState() { + super.initState(); + _loading = ValueNotifier(false); + _error = ValueNotifier(false); + _controller = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setNavigationDelegate( + NavigationDelegate( + onPageStarted: (_) => _loading.value = true, + onPageFinished: (_) => _loading.value = false, + onWebResourceError: (_) { + _error.value = true; + _loading.value = false; + }, + ), + ); + _loadUrl(); + } + + @override + void dispose() { + _loading.dispose(); + super.dispose(); + } + + void _loadUrl() { + _error.value = false; + _controller.loadRequest(Uri.parse(widget.url)); + } + + @override + Widget build(BuildContext context) { return Scaffold( extendBodyBehindAppBar: true, appBar: CustomAppBar( - title: title, + title: widget.title, closable: true, ), - body: WebView(initialUrl: url), + body: Stack( + children: [ + WebViewWidget(controller: _controller), + ValueListenableBuilder( + valueListenable: _loading, + builder: (context, loading, _) { + if (!loading) return const SizedBox.shrink(); + + return Align(child: CircularProgressIndicator()); + }, + ), + ValueListenableBuilder( + valueListenable: _error, + builder: (context, error, _) { + if (!error) return const SizedBox.shrink(); + + return Align( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text("No internet connection!!"), + const SizedBox(height: 10), + ElevatedButton( + onPressed: _loadUrl, + child: Text("Retry"), + ), + ], + ), + ); + }, + ) + ], + ), ); } } diff --git a/test/presentation/shared_widgets/web_view.mocks.dart b/test/presentation/shared_widgets/web_view.mocks.dart new file mode 100644 index 00000000..9d5a362e --- /dev/null +++ b/test/presentation/shared_widgets/web_view.mocks.dart @@ -0,0 +1,99 @@ +part of 'web_view_page_test.dart'; + +class FakeWebViewPlatform extends WebViewPlatform { + final String url; + + FakeWebViewPlatform(this.url); + + @override + PlatformWebViewController createPlatformWebViewController( + PlatformWebViewControllerCreationParams params, + ) { + return FakeWebViewController(params, url); + } + + @override + PlatformWebViewWidget createPlatformWebViewWidget( + PlatformWebViewWidgetCreationParams params, + ) { + return FakeWebViewWidget(params); + } + + @override + PlatformWebViewCookieManager createPlatformCookieManager( + PlatformWebViewCookieManagerCreationParams params, + ) { + return FakeCookieManager(params); + } + + @override + PlatformNavigationDelegate createPlatformNavigationDelegate( + PlatformNavigationDelegateCreationParams params, + ) { + return FakeNavigationDelegate(params); + } +} + +class FakeWebViewController extends PlatformWebViewController { + final String url; + FakeWebViewController(super.params, this.url) : super.implementation(); + + @override + Future setJavaScriptMode(JavaScriptMode javaScriptMode) async {} + + @override + Future setBackgroundColor(Color color) async {} + + @override + Future setPlatformNavigationDelegate( + PlatformNavigationDelegate handler, + ) async {} + + @override + Future addJavaScriptChannel( + JavaScriptChannelParams javaScriptChannelParams) async {} + + @override + Future loadRequest(LoadRequestParams params) async {} + + @override + Future currentUrl() async { + return url; + } +} + +class FakeCookieManager extends PlatformWebViewCookieManager { + FakeCookieManager(super.params) : super.implementation(); +} + +class FakeWebViewWidget extends PlatformWebViewWidget { + FakeWebViewWidget(super.params) : super.implementation(); + + @override + Widget build(BuildContext context) { + return Container(); + } +} + +class FakeNavigationDelegate extends PlatformNavigationDelegate { + FakeNavigationDelegate(super.params) : super.implementation(); + + @override + Future setOnNavigationRequest( + NavigationRequestCallback onNavigationRequest, + ) async {} + + @override + Future setOnPageFinished(PageEventCallback onPageFinished) async {} + + @override + Future setOnPageStarted(PageEventCallback onPageStarted) async {} + + @override + Future setOnProgress(ProgressCallback onProgress) async {} + + @override + Future setOnWebResourceError( + WebResourceErrorCallback onWebResourceError, + ) async {} +} diff --git a/test/presentation/shared_widgets/web_view_page_test.dart b/test/presentation/shared_widgets/web_view_page_test.dart index db6a67b4..a09c35ed 100644 --- a/test/presentation/shared_widgets/web_view_page_test.dart +++ b/test/presentation/shared_widgets/web_view_page_test.dart @@ -1,16 +1,27 @@ import 'package:collaction_app/presentation/shared_widgets/web_view_page.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:webview_flutter/webview_flutter.dart'; +// ignore: depend_on_referenced_packages +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import '../../test_helper.dart'; +part 'web_view.mocks.dart'; + void main() { + final testUrl = 'https://collaction.org'; + + setUp(() { + WebViewPlatform.instance = FakeWebViewPlatform(testUrl); + }); + group('WebViewPage tests:', () { testWidgets('can render', (WidgetTester tester) async { await buildAndPump( tester: tester, widget: WebViewPage( - url: 'https://collaction.org', + url: testUrl, title: 'title', ), ); @@ -24,16 +35,21 @@ void main() { await buildAndPump( tester: tester, widget: WebViewPage( - url: 'https://collaction.org', + url: testUrl, title: 'title', ), ); await tester.pumpAndSettle(); - expect(find.byType(WebView), findsOneWidget); + expect(find.byType(WebViewWidget), findsOneWidget); + + final webView = + tester.firstWidget(find.byType(WebViewWidget)); + final url = await webView.platform.params.controller.currentUrl(); + expect( - tester.firstWidget(find.byType(WebView)).initialUrl, - equals('https://collaction.org'), + url, + equals(testUrl), ); }); }); From 0690cae0e1d9c434e03b9e048bfa6f9f396abed5 Mon Sep 17 00:00:00 2001 From: Isaac Obella Date: Wed, 8 Feb 2023 13:58:08 +0300 Subject: [PATCH 04/30] fix: Formating Signed-off-by: Isaac Obella --- test/test_utilities.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_utilities.dart b/test/test_utilities.dart index 984a937e..cd0d48d8 100644 --- a/test/test_utilities.dart +++ b/test/test_utilities.dart @@ -20,8 +20,8 @@ import 'package:mocktail/mocktail.dart'; import 'domain/crowdaction/crowdaction_test_fixtures.dart'; import 'utils/crowdactions.dart'; -class MockCrowdActionRepository extends Mock implements ICrowdActionRepository { -} +class MockCrowdActionRepository extends Mock + implements ICrowdActionRepository {} class MockAuthRepository extends Mock implements IAuthRepository {} From 3f70938668cb940d2d32d68b961e2a8471967e2b Mon Sep 17 00:00:00 2001 From: Isaac Obella Date: Wed, 8 Feb 2023 14:15:06 +0300 Subject: [PATCH 05/30] fix: Deprecations Signed-off-by: Isaac Obella --- .../contact_form/widgets/contact_form.dart | 2 +- .../parts/comment_item.dart | 2 +- .../widgets/flag_comment.dart | 10 +++---- .../crowdaction_details_screen.dart | 10 +++---- .../widgets/commitment_badges.dart | 29 ++++++++++--------- .../widgets/confirm_participation.dart | 14 ++++----- .../widgets/crowdaction_title.dart | 2 +- .../widgets/participation_count_text.dart | 2 +- .../widgets/withdraw_participation.dart | 17 ++++++----- .../widgets/in_spotlight_header.dart | 2 +- .../widgets/share_collaction_card.dart | 4 +-- .../crowdaction_participants_screen.dart | 2 +- .../components_demo_screen.dart | 2 +- .../home/widgets/current_upcoming_layout.dart | 12 ++++---- .../home/widgets/password_modal.dart | 4 +-- lib/presentation/profile/profile_screen.dart | 6 ++-- .../shared_widgets/accent_chip.dart | 2 +- .../shared_widgets/commitment_card.dart | 4 +-- .../commitments/commitment_card.dart | 4 +-- .../shared_widgets/crowdaction_card.dart | 2 +- .../custom_app_bars/clean_app_bar.dart | 2 +- .../custom_app_bars/scrollable_app_bar.dart | 2 +- .../shared_widgets/no_ripple_behavior.dart | 4 +-- .../shared_widgets/photo_selector.dart | 2 +- .../shared_widgets/secondary_chip.dart | 2 +- .../shared_widgets/selectable_chip.dart | 2 +- .../shared_widgets/web_view_page.dart | 2 -- .../in_spotlight_header_test.dart | 1 - 28 files changed, 76 insertions(+), 73 deletions(-) diff --git a/lib/presentation/contact_form/widgets/contact_form.dart b/lib/presentation/contact_form/widgets/contact_form.dart index 044fdd07..a7260a40 100644 --- a/lib/presentation/contact_form/widgets/contact_form.dart +++ b/lib/presentation/contact_form/widgets/contact_form.dart @@ -111,7 +111,7 @@ class _ContactFormState extends State { "We’ll get back to you by email", style: Theme.of(context) .textTheme - .caption! + .bodySmall! .copyWith(color: kPrimaryColor300), maxLines: 2, textAlign: TextAlign.left, diff --git a/lib/presentation/crowdaction/crowdaction_comments/parts/comment_item.dart b/lib/presentation/crowdaction/crowdaction_comments/parts/comment_item.dart index cbb695a6..f65a7af1 100644 --- a/lib/presentation/crowdaction/crowdaction_comments/parts/comment_item.dart +++ b/lib/presentation/crowdaction/crowdaction_comments/parts/comment_item.dart @@ -35,7 +35,7 @@ class CommentItem extends StatelessWidget { trimLines: 6, style: Theme.of(context) .textTheme - .bodyText2 + .bodyMedium ?.copyWith( fontSize: 15, fontWeight: FontWeight.w300, diff --git a/lib/presentation/crowdaction/crowdaction_comments/widgets/flag_comment.dart b/lib/presentation/crowdaction/crowdaction_comments/widgets/flag_comment.dart index f156fd67..18a46210 100644 --- a/lib/presentation/crowdaction/crowdaction_comments/widgets/flag_comment.dart +++ b/lib/presentation/crowdaction/crowdaction_comments/widgets/flag_comment.dart @@ -83,7 +83,7 @@ class FlagDialogState extends State { textAlign: TextAlign.center, style: Theme.of(context) .textTheme - .subtitle1 + .titleMedium ?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox( @@ -99,7 +99,7 @@ class FlagDialogState extends State { child: Text( "Modal description. Nam quis nulla. Integer malesuada. In in enim a arcu imperdiet malesuada. Sed vel lectus. Donec odio uma, tempus molestie, porttitor ut, iaculis quis.", overflow: TextOverflow.fade, - style: Theme.of(context).textTheme.caption?.copyWith( + style: Theme.of(context).textTheme.bodySmall?.copyWith( color: kPrimaryColor400, ), textAlign: TextAlign.center, @@ -184,7 +184,7 @@ class FlagSuccess extends StatelessWidget { textAlign: TextAlign.center, style: Theme.of(context) .textTheme - .subtitle1 + .titleMedium ?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox( @@ -193,7 +193,7 @@ class FlagSuccess extends StatelessWidget { Text( "Successfully reported", textAlign: TextAlign.center, - style: Theme.of(context).textTheme.subtitle1?.copyWith( + style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, fontSize: 28, ), @@ -204,7 +204,7 @@ class FlagSuccess extends StatelessWidget { Container( child: Text( "Thank you for letting us know. We will look into it and dlete the comment if necessary.", - style: Theme.of(context).textTheme.caption?.copyWith( + style: Theme.of(context).textTheme.bodySmall?.copyWith( color: kPrimaryColor400, ), textAlign: TextAlign.center, diff --git a/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart b/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart index cf6a7deb..d1da17dd 100644 --- a/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart +++ b/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart @@ -201,7 +201,7 @@ class CrowdActionDetailsPageState extends State { 'My commitments', style: Theme.of(context) .textTheme - .headline1! + .displayLarge! .copyWith( fontWeight: FontWeight.w700, fontSize: 28, @@ -218,7 +218,7 @@ class CrowdActionDetailsPageState extends State { 'Your challenge, your rules.\nChoose which commitment(s) you want to make for this CrowdAction.', style: Theme.of(context) .textTheme - .caption! + .bodySmall! .copyWith( color: kPrimaryColor300, fontWeight: FontWeight.w400, @@ -322,13 +322,13 @@ class CrowdActionDetailsPageState extends State { "Register", style: Theme.of(context) .textTheme - .subtitle1 + .titleMedium ?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 30), Text( "You need to create an account in order to participate in a crowdaction. If you have an account already, please log in.", - style: Theme.of(context).textTheme.caption?.copyWith( + style: Theme.of(context).textTheme.bodySmall?.copyWith( color: kPrimaryColor400, ), ), @@ -384,7 +384,7 @@ class CrowdActionDescription extends StatelessWidget { return ExpandableText( crowdAction!.description, trimLines: 5, - style: Theme.of(context).textTheme.bodyText2?.copyWith( + style: Theme.of(context).textTheme.bodyMedium?.copyWith( fontSize: 17, fontWeight: FontWeight.w300, height: 1.5, diff --git a/lib/presentation/crowdaction/crowdaction_details/widgets/commitment_badges.dart b/lib/presentation/crowdaction/crowdaction_details/widgets/commitment_badges.dart index dfe226a9..0ec7f79c 100644 --- a/lib/presentation/crowdaction/crowdaction_details/widgets/commitment_badges.dart +++ b/lib/presentation/crowdaction/crowdaction_details/widgets/commitment_badges.dart @@ -135,11 +135,13 @@ class _BadgesPopupCard extends StatelessWidget { padding: const EdgeInsets.all(8.0), child: Text( 'Badges', - style: - Theme.of(context).textTheme.headline1!.copyWith( - fontWeight: FontWeight.w700, - fontSize: 28, - ), + style: Theme.of(context) + .textTheme + .displayLarge! + .copyWith( + fontWeight: FontWeight.w700, + fontSize: 28, + ), ), ), Container( @@ -148,11 +150,12 @@ class _BadgesPopupCard extends StatelessWidget { ), child: Text( 'Different commitments give you a different set of points. These points correspond with badges.\nWhich one will you attain this month?', - style: Theme.of(context).textTheme.caption!.copyWith( - fontWeight: FontWeight.w400, - fontSize: 12, - color: kPrimaryColor300, - ), + style: + Theme.of(context).textTheme.bodySmall!.copyWith( + fontWeight: FontWeight.w400, + fontSize: 12, + color: kPrimaryColor300, + ), textAlign: TextAlign.center, ), ), @@ -172,7 +175,7 @@ class _BadgesPopupCard extends StatelessWidget { 'Gold', style: Theme.of(context) .textTheme - .caption! + .bodySmall! .copyWith( fontWeight: FontWeight.w400, fontSize: 12, @@ -206,7 +209,7 @@ class _BadgesPopupCard extends StatelessWidget { 'Silver', style: Theme.of(context) .textTheme - .caption! + .bodySmall! .copyWith( fontWeight: FontWeight.w400, fontSize: 12, @@ -239,7 +242,7 @@ class _BadgesPopupCard extends StatelessWidget { 'Bronze', style: Theme.of(context) .textTheme - .caption! + .bodySmall! .copyWith( fontWeight: FontWeight.w400, fontSize: 12, diff --git a/lib/presentation/crowdaction/crowdaction_details/widgets/confirm_participation.dart b/lib/presentation/crowdaction/crowdaction_details/widgets/confirm_participation.dart index dba15289..38b39bfa 100644 --- a/lib/presentation/crowdaction/crowdaction_details/widgets/confirm_participation.dart +++ b/lib/presentation/crowdaction/crowdaction_details/widgets/confirm_participation.dart @@ -63,7 +63,7 @@ class ParticipationSuccess extends StatelessWidget { textAlign: TextAlign.center, style: Theme.of(context) .textTheme - .subtitle1 + .titleMedium ?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox( @@ -72,7 +72,7 @@ class ParticipationSuccess extends StatelessWidget { Text( "All set!", textAlign: TextAlign.center, - style: Theme.of(context).textTheme.subtitle1?.copyWith( + style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, fontSize: 28, ), @@ -82,7 +82,7 @@ class ParticipationSuccess extends StatelessWidget { ), Text( "You have successfully registered for this CrowdAction", - style: Theme.of(context).textTheme.caption?.copyWith( + style: Theme.of(context).textTheme.bodySmall?.copyWith( color: kPrimaryColor400, ), ), @@ -102,7 +102,7 @@ class ParticipationSuccess extends StatelessWidget { constraints: const BoxConstraints(maxWidth: 250), child: Text( "You can change your commitment until the CrowdAction starts", - style: Theme.of(context).textTheme.caption?.copyWith( + style: Theme.of(context).textTheme.bodySmall?.copyWith( color: kPrimaryColor200, ), textAlign: TextAlign.center, @@ -152,7 +152,7 @@ class ParticipationDialog extends StatelessWidget { textAlign: TextAlign.center, style: Theme.of(context) .textTheme - .subtitle1 + .titleMedium ?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox( @@ -169,7 +169,7 @@ class ParticipationDialog extends StatelessWidget { child: Text( "You’re almost there! You’ve selected the displayed commitment${selectedCommitments.length > 1 ? 's' : ''} to stick to through this CrowdAction. By clicking “Confirm” you will officially commit to this CrowdAction.", overflow: TextOverflow.fade, - style: Theme.of(context).textTheme.caption?.copyWith( + style: Theme.of(context).textTheme.bodySmall?.copyWith( color: kPrimaryColor400, ), ), @@ -180,7 +180,7 @@ class ParticipationDialog extends StatelessWidget { textAlign: TextAlign.center, style: Theme.of(context) .textTheme - .caption + .bodySmall ?.copyWith(color: kPrimaryColor300, fontSize: 12), ), const SizedBox(height: 20), diff --git a/lib/presentation/crowdaction/crowdaction_details/widgets/crowdaction_title.dart b/lib/presentation/crowdaction/crowdaction_details/widgets/crowdaction_title.dart index d5925e2d..1210969e 100644 --- a/lib/presentation/crowdaction/crowdaction_details/widgets/crowdaction_title.dart +++ b/lib/presentation/crowdaction/crowdaction_details/widgets/crowdaction_title.dart @@ -16,7 +16,7 @@ class CrowdActionTitle extends StatelessWidget { return title != null ? Text( title!, - style: Theme.of(context).textTheme.headline4?.copyWith( + style: Theme.of(context).textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.bold, fontSize: 28, ), diff --git a/lib/presentation/crowdaction/crowdaction_details/widgets/participation_count_text.dart b/lib/presentation/crowdaction/crowdaction_details/widgets/participation_count_text.dart index 23d027d6..38fe58cb 100644 --- a/lib/presentation/crowdaction/crowdaction_details/widgets/participation_count_text.dart +++ b/lib/presentation/crowdaction/crowdaction_details/widgets/participation_count_text.dart @@ -81,7 +81,7 @@ class ParticipationCountText extends StatelessWidget { Text participantCountText(BuildContext context, int participantCount) => Text( "${!isEnded ? 'Join ' : ''}$participantCount ${participantCount > 1 ? 'people' : 'person'} ${!isEnded ? 'participating' : 'participated'}", - style: Theme.of(context).textTheme.caption?.copyWith( + style: Theme.of(context).textTheme.bodySmall?.copyWith( fontSize: 14, color: kPrimaryColor300, height: 1.2, diff --git a/lib/presentation/crowdaction/crowdaction_details/widgets/withdraw_participation.dart b/lib/presentation/crowdaction/crowdaction_details/widgets/withdraw_participation.dart index 9a9a3fb1..5caaafb4 100644 --- a/lib/presentation/crowdaction/crowdaction_details/widgets/withdraw_participation.dart +++ b/lib/presentation/crowdaction/crowdaction_details/widgets/withdraw_participation.dart @@ -28,7 +28,7 @@ class WithdrawParticipation extends StatelessWidget { padding: const EdgeInsets.only(top: 16.0), child: Text( 'Cancel my participation', - style: Theme.of(context).textTheme.headline3!.copyWith( + style: Theme.of(context).textTheme.displaySmall!.copyWith( fontSize: 15, fontWeight: FontWeight.w700, color: kSuccessColor, @@ -78,7 +78,7 @@ class WithdrawParticipation extends StatelessWidget { textAlign: TextAlign.center, style: Theme.of(context) .textTheme - .subtitle1 + .titleMedium ?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox( @@ -87,10 +87,11 @@ class WithdrawParticipation extends StatelessWidget { Text( "We are sad to see you withdraw!", textAlign: TextAlign.center, - style: Theme.of(context).textTheme.subtitle1?.copyWith( - fontWeight: FontWeight.bold, - fontSize: 28, - ), + style: + Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + fontSize: 28, + ), ), const SizedBox( @@ -137,7 +138,7 @@ class WithdrawParticipation extends StatelessWidget { textAlign: TextAlign.center, style: Theme.of(context) .textTheme - .subtitle1 + .titleMedium ?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox( @@ -145,7 +146,7 @@ class WithdrawParticipation extends StatelessWidget { ), Text( "You are about to cancel your participation. You are free to sign up for this CrowdAction again any time before it starts.", - style: Theme.of(context).textTheme.caption?.copyWith( + style: Theme.of(context).textTheme.bodySmall?.copyWith( color: kPrimaryColor400, ), ), diff --git a/lib/presentation/crowdaction/crowdaction_home/widgets/in_spotlight_header.dart b/lib/presentation/crowdaction/crowdaction_home/widgets/in_spotlight_header.dart index 1eff6b68..03a69c4c 100644 --- a/lib/presentation/crowdaction/crowdaction_home/widgets/in_spotlight_header.dart +++ b/lib/presentation/crowdaction/crowdaction_home/widgets/in_spotlight_header.dart @@ -37,7 +37,7 @@ class _InSpotLightHeaderState extends State { padding: const EdgeInsets.only(left: 12, top: 8), child: Text( sectionHeadingText(), - style: Theme.of(context).textTheme.headline5?.copyWith( + style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, color: kPrimaryColor400, ), diff --git a/lib/presentation/crowdaction/crowdaction_home/widgets/share_collaction_card.dart b/lib/presentation/crowdaction/crowdaction_home/widgets/share_collaction_card.dart index b3faedc4..5d16883e 100644 --- a/lib/presentation/crowdaction/crowdaction_home/widgets/share_collaction_card.dart +++ b/lib/presentation/crowdaction/crowdaction_home/widgets/share_collaction_card.dart @@ -35,7 +35,7 @@ class ShareCollActionCard extends StatelessWidget { Text( "Share CollAction with\n your friends!", textAlign: TextAlign.center, - style: Theme.of(context).textTheme.headline5?.copyWith( + style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, color: kSecondaryColor, ), @@ -50,7 +50,7 @@ class ShareCollActionCard extends StatelessWidget { textAlign: TextAlign.center, style: Theme.of(context) .textTheme - .caption + .bodySmall ?.copyWith(color: kSecondaryColor), ), ), diff --git a/lib/presentation/crowdaction/crowdaction_participants/crowdaction_participants_screen.dart b/lib/presentation/crowdaction/crowdaction_participants/crowdaction_participants_screen.dart index 2dcfd9f0..aaab2124 100644 --- a/lib/presentation/crowdaction/crowdaction_participants/crowdaction_participants_screen.dart +++ b/lib/presentation/crowdaction/crowdaction_participants/crowdaction_participants_screen.dart @@ -102,7 +102,7 @@ class CrowdActionParticipantsPage extends StatelessWidget { participation.fullName, style: Theme.of(context) .textTheme - .bodyText2 + .bodyMedium ?.copyWith(fontSize: 17, fontWeight: FontWeight.w300), ), ), diff --git a/lib/presentation/demo/components_demo/components_demo_screen.dart b/lib/presentation/demo/components_demo/components_demo_screen.dart index 19495e87..965b3899 100644 --- a/lib/presentation/demo/components_demo/components_demo_screen.dart +++ b/lib/presentation/demo/components_demo/components_demo_screen.dart @@ -318,7 +318,7 @@ class ComponentsDemoPageState extends State { "Delete comment", style: Theme.of(context) .textTheme - .subtitle1 + .titleMedium ?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 30), diff --git a/lib/presentation/home/widgets/current_upcoming_layout.dart b/lib/presentation/home/widgets/current_upcoming_layout.dart index 723d8c9c..593bddc1 100644 --- a/lib/presentation/home/widgets/current_upcoming_layout.dart +++ b/lib/presentation/home/widgets/current_upcoming_layout.dart @@ -45,11 +45,13 @@ class _CurrentAndUpcomingLayoutState extends State { children: [ Text( widget.isCurrent ? 'Currently running' : 'Upcoming', - style: - Theme.of(context).textTheme.headline5?.copyWith( - fontWeight: FontWeight.bold, - color: kPrimaryColor400, - ), + style: Theme.of(context) + .textTheme + .headlineSmall + ?.copyWith( + fontWeight: FontWeight.bold, + color: kPrimaryColor400, + ), ), TextButton( onPressed: () => diff --git a/lib/presentation/home/widgets/password_modal.dart b/lib/presentation/home/widgets/password_modal.dart index 89f89809..26648b71 100644 --- a/lib/presentation/home/widgets/password_modal.dart +++ b/lib/presentation/home/widgets/password_modal.dart @@ -51,7 +51,7 @@ class _PasswordModalState extends State { children: [ Text( 'Enter password', - style: Theme.of(context).textTheme.headline1!.copyWith( + style: Theme.of(context).textTheme.displayLarge!.copyWith( color: kPrimaryColor400, fontSize: 28, fontWeight: FontWeight.w700, @@ -60,7 +60,7 @@ class _PasswordModalState extends State { const SizedBox(height: 10), Text( 'This crowdaction is private. Please enter the password to see it.', - style: Theme.of(context).textTheme.headline1!.copyWith( + style: Theme.of(context).textTheme.displayLarge!.copyWith( color: kPrimaryColor300, fontSize: 12, ), diff --git a/lib/presentation/profile/profile_screen.dart b/lib/presentation/profile/profile_screen.dart index 52fd7b0c..4d697258 100644 --- a/lib/presentation/profile/profile_screen.dart +++ b/lib/presentation/profile/profile_screen.dart @@ -203,7 +203,7 @@ class _UserProfilePageState extends State { maxLines: 5, style: Theme.of(context) .textTheme - .bodyText2! + .bodyMedium! .copyWith( fontWeight: FontWeight.w300, fontSize: 17, @@ -221,7 +221,7 @@ class _UserProfilePageState extends State { 'Maximum 150 characters', style: Theme.of(context) .textTheme - .bodyText2! + .bodyMedium! .copyWith( fontSize: 12, fontWeight: FontWeight.w400, @@ -235,7 +235,7 @@ class _UserProfilePageState extends State { state.userProfile?.profile.bio ?? '', style: Theme.of(context) .textTheme - .bodyText2! + .bodyMedium! .copyWith( fontWeight: FontWeight.w300, fontSize: 17, diff --git a/lib/presentation/shared_widgets/accent_chip.dart b/lib/presentation/shared_widgets/accent_chip.dart index 9220dc10..d347b1ca 100644 --- a/lib/presentation/shared_widgets/accent_chip.dart +++ b/lib/presentation/shared_widgets/accent_chip.dart @@ -29,7 +29,7 @@ class AccentChip extends StatelessWidget { shape: StadiumBorder(side: BorderSide(color: color)), label: Text( text, - style: Theme.of(context).textTheme.caption?.copyWith( + style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Colors.white, ), ), diff --git a/lib/presentation/shared_widgets/commitment_card.dart b/lib/presentation/shared_widgets/commitment_card.dart index 1a80b4b0..30eae687 100644 --- a/lib/presentation/shared_widgets/commitment_card.dart +++ b/lib/presentation/shared_widgets/commitment_card.dart @@ -134,14 +134,14 @@ class _CommitmentCardState extends State<_CommitmentCard> { children: [ Text( widget.commitment.title, - style: textTheme.headline6!.copyWith(fontSize: 16), + style: textTheme.titleLarge!.copyWith(fontSize: 16), maxLines: 1, overflow: TextOverflow.ellipsis, ), if (widget.commitment.description != null) Text( widget.commitment.description!, - style: textTheme.bodyText2!.copyWith(fontSize: 13), + style: textTheme.bodyMedium!.copyWith(fontSize: 13), softWrap: true, maxLines: 3, overflow: TextOverflow.ellipsis, diff --git a/lib/presentation/shared_widgets/commitments/commitment_card.dart b/lib/presentation/shared_widgets/commitments/commitment_card.dart index 3118e7da..d1923f87 100644 --- a/lib/presentation/shared_widgets/commitments/commitment_card.dart +++ b/lib/presentation/shared_widgets/commitments/commitment_card.dart @@ -77,7 +77,7 @@ class CommitmentCard extends StatelessWidget { children: [ Text( commitment.label, - style: textTheme.caption!.copyWith( + style: textTheme.bodySmall!.copyWith( fontSize: 16, fontWeight: FontWeight.bold, color: deactivated ? kPrimaryColor300 : kPrimaryColor400, @@ -89,7 +89,7 @@ class CommitmentCard extends StatelessWidget { const SizedBox(height: 5), Text( commitment.description!, - style: textTheme.caption!.copyWith( + style: textTheme.bodySmall!.copyWith( fontSize: 13, color: deactivated ? kPrimaryColor200 : kPrimaryColor300, diff --git a/lib/presentation/shared_widgets/crowdaction_card.dart b/lib/presentation/shared_widgets/crowdaction_card.dart index 20c748b5..f3910a40 100644 --- a/lib/presentation/shared_widgets/crowdaction_card.dart +++ b/lib/presentation/shared_widgets/crowdaction_card.dart @@ -139,7 +139,7 @@ class _CrowdActionCardState extends State overflow: TextOverflow.ellipsis, style: Theme.of(context) .textTheme - .bodyText2 + .bodyMedium ?.copyWith(color: kInactiveColor), ), ), diff --git a/lib/presentation/shared_widgets/custom_app_bars/clean_app_bar.dart b/lib/presentation/shared_widgets/custom_app_bars/clean_app_bar.dart index ad557048..bdfcc8bf 100644 --- a/lib/presentation/shared_widgets/custom_app_bars/clean_app_bar.dart +++ b/lib/presentation/shared_widgets/custom_app_bars/clean_app_bar.dart @@ -50,7 +50,7 @@ class CleanAppBar extends StatelessWidget implements PreferredSizeWidget { titleTextStyle: titleTextStyle ?? Theme.of(context) .textTheme - .headline6 + .titleLarge ?.copyWith(color: kPrimaryColor), ), ); diff --git a/lib/presentation/shared_widgets/custom_app_bars/scrollable_app_bar.dart b/lib/presentation/shared_widgets/custom_app_bars/scrollable_app_bar.dart index f96701dd..80faef6e 100644 --- a/lib/presentation/shared_widgets/custom_app_bars/scrollable_app_bar.dart +++ b/lib/presentation/shared_widgets/custom_app_bars/scrollable_app_bar.dart @@ -105,7 +105,7 @@ class ScrollableAppBarState extends State { titleTextStyle: widget.titleTextStyle ?? Theme.of(context) .textTheme - .headline6 + .titleLarge ?.copyWith(color: kPrimaryColor), ), ); diff --git a/lib/presentation/shared_widgets/no_ripple_behavior.dart b/lib/presentation/shared_widgets/no_ripple_behavior.dart index 266191e9..996de3fd 100644 --- a/lib/presentation/shared_widgets/no_ripple_behavior.dart +++ b/lib/presentation/shared_widgets/no_ripple_behavior.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; class NoRippleBehavior extends ScrollBehavior { @override - Widget buildViewportChrome( + Widget buildOverscrollIndicator( BuildContext context, Widget child, - AxisDirection axisDirection, + ScrollableDetails details, ) { return child; } diff --git a/lib/presentation/shared_widgets/photo_selector.dart b/lib/presentation/shared_widgets/photo_selector.dart index 9d644872..2c4b16b8 100644 --- a/lib/presentation/shared_widgets/photo_selector.dart +++ b/lib/presentation/shared_widgets/photo_selector.dart @@ -40,7 +40,7 @@ class PhotoSelectorState extends State { children: [ Text( "Upload a profile photo", - style: Theme.of(context).textTheme.headline6, + style: Theme.of(context).textTheme.titleLarge, ), const SizedBox( height: 20, diff --git a/lib/presentation/shared_widgets/secondary_chip.dart b/lib/presentation/shared_widgets/secondary_chip.dart index e5a63f1f..ffcb2cb6 100644 --- a/lib/presentation/shared_widgets/secondary_chip.dart +++ b/lib/presentation/shared_widgets/secondary_chip.dart @@ -21,7 +21,7 @@ class SecondaryChip extends StatelessWidget { shape: const StadiumBorder(side: BorderSide(color: kAlmostTransparent)), label: Text( text, - style: Theme.of(context).textTheme.caption, + style: Theme.of(context).textTheme.bodySmall, ), deleteIcon: onDeleted != null ? const Icon(Icons.cancel) : null, onDeleted: onDeleted, diff --git a/lib/presentation/shared_widgets/selectable_chip.dart b/lib/presentation/shared_widgets/selectable_chip.dart index 150811d9..465db34a 100644 --- a/lib/presentation/shared_widgets/selectable_chip.dart +++ b/lib/presentation/shared_widgets/selectable_chip.dart @@ -41,7 +41,7 @@ class SelectableChip extends StatelessWidget { ), child: Text( text, - style: Theme.of(context).textTheme.caption?.copyWith( + style: Theme.of(context).textTheme.bodySmall?.copyWith( fontSize: 11, color: selected ? kSecondaryColor : kPrimaryColor400, fontWeight: FontWeight.w700, diff --git a/lib/presentation/shared_widgets/web_view_page.dart b/lib/presentation/shared_widgets/web_view_page.dart index 9a64228f..40481cf9 100644 --- a/lib/presentation/shared_widgets/web_view_page.dart +++ b/lib/presentation/shared_widgets/web_view_page.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; diff --git a/test/presentation/crowdaction/crowdaction_home/in_spotlight_header_test.dart b/test/presentation/crowdaction/crowdaction_home/in_spotlight_header_test.dart index 427573ac..ec8750e7 100644 --- a/test/presentation/crowdaction/crowdaction_home/in_spotlight_header_test.dart +++ b/test/presentation/crowdaction/crowdaction_home/in_spotlight_header_test.dart @@ -3,7 +3,6 @@ import 'package:collaction_app/application/crowdaction/crowdaction_details/crowd import 'package:collaction_app/application/crowdaction/spotlight/spotlight_bloc.dart'; import 'package:collaction_app/application/participation/top_participants/top_participants_bloc.dart'; import 'package:collaction_app/domain/crowdaction/crowdaction_failures.dart'; -import 'package:collaction_app/presentation/crowdaction/crowdaction_home/widgets/spotlight_crowdactions/spotlight_crowdactions.dart'; import 'package:collaction_app/presentation/crowdaction/crowdaction_home/widgets/in_spotlight_header.dart'; import 'package:collaction_app/presentation/crowdaction/crowdaction_home/widgets/spotlight_crowdactions/spotlight_crowdactions.dart'; import 'package:collaction_app/presentation/shared_widgets/content_placeholder.dart'; From 38c7c47cd0bd75270e18e7bf4e7b8cb09c68c7a8 Mon Sep 17 00:00:00 2001 From: Isaac Obella Date: Wed, 8 Feb 2023 14:34:38 +0300 Subject: [PATCH 06/30] fix: url_launch test Signed-off-by: Isaac Obella --- .../settings/settings_screen_test.dart | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/test/presentation/settings/settings_screen_test.dart b/test/presentation/settings/settings_screen_test.dart index 1db6affe..6e87405a 100644 --- a/test/presentation/settings/settings_screen_test.dart +++ b/test/presentation/settings/settings_screen_test.dart @@ -6,13 +6,13 @@ import 'package:collaction_app/application/settings/build_information/build_info import 'package:collaction_app/application/user/profile/profile_bloc.dart'; import 'package:collaction_app/domain/settings/build_information.dart'; import 'package:collaction_app/infrastructure/core/injection.dart'; -import 'package:collaction_app/presentation/settings/settings_screen.dart'; import 'package:collaction_app/presentation/routes/app_routes.gr.dart'; +import 'package:collaction_app/presentation/settings/settings_screen.dart'; import 'package:collaction_app/presentation/settings/widgets/build_information_tile.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:mocktail/mocktail.dart'; @@ -30,22 +30,29 @@ void main() { late ProfileBloc profileBloc; late BuildInformationBloc buildInformationBloc; - final channel = MethodChannel( - Platform.isLinux - ? 'plugins.flutter.io/url_launcher_linux' - : Platform.isWindows - ? 'plugins.flutter.io/url_launcher_windows' - : 'plugins.flutter.io/url_launcher_macos', - ); + final channels = [ + MethodChannel( + Platform.isLinux + ? 'plugins.flutter.io/url_launcher_linux' + : Platform.isWindows + ? 'plugins.flutter.io/url_launcher_windows' + : 'plugins.flutter.io/url_launcher_macos', + ), + MethodChannel('plugins.flutter.io/url_launcher') + ]; setUp(() async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return methodCall.method == 'canLaunch' ? true : null; - }); + for (final channel in channels) { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return methodCall.method == 'canLaunch' ? true : null; + }); + } }); tearDown(() { - channel.setMockMethodCallHandler(null); + for (final channel in channels) { + channel.setMockMethodCallHandler(null); + } }); setUpAll(() { From 3519ad0728f206ba139bd86b4d3272c5935145a1 Mon Sep 17 00:00:00 2001 From: Isaac Obella Date: Wed, 8 Feb 2023 14:48:14 +0300 Subject: [PATCH 07/30] fix: GetIt update Signed-off-by: Isaac Obella --- lib/infrastructure/core/injection.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/infrastructure/core/injection.dart b/lib/infrastructure/core/injection.dart index 94f3d2c8..98ad4f10 100644 --- a/lib/infrastructure/core/injection.dart +++ b/lib/infrastructure/core/injection.dart @@ -7,5 +7,5 @@ final GetIt getIt = GetIt.instance; @injectableInit void configureInjection() { - $initGetIt(getIt); + getIt.init(); } From ade153d121ea0a47d5d88cbbfdbce6b24c447965 Mon Sep 17 00:00:00 2001 From: Mathias Mogensencd collaction_website Date: Sat, 4 Feb 2023 23:38:32 +0100 Subject: [PATCH 08/30] tests: add and fix tests --- .../user/profile_tab/profile_tab_bloc.dart | 2 +- lib/domain/user/user.dart | 1 - lib/presentation/core/collaction_icons.dart | 2 - .../widgets/withdraw_participation.dart | 5 - lib/presentation/profile/profile_screen.dart | 1 - ...th_bloc_tests.dart => auth_bloc_test.dart} | 3 +- .../build_information_bloc_test.dart | 41 +++++ .../contact_form/contact_form_test.dart | 49 ++++-- .../username/username_bloc_test.dart | 69 ++++++++ .../core/ionicons_utils_test.dart | 18 ++ test/presentation/home/home_screen_test.dart | 81 +++++++++ .../profile/profile_screen_test.dart | 164 ++++++++++++++++++ .../user_repository_tests.dart | 80 --------- 13 files changed, 409 insertions(+), 107 deletions(-) rename test/application/auth/{auth_bloc_tests.dart => auth_bloc_test.dart} (97%) create mode 100644 test/application/build_information/build_information_bloc_test.dart create mode 100644 test/application/username/username_bloc_test.dart create mode 100644 test/presentation/core/ionicons_utils_test.dart create mode 100644 test/presentation/home/home_screen_test.dart create mode 100644 test/presentation/profile/profile_screen_test.dart delete mode 100644 test/user_repository/user_repository_tests.dart diff --git a/lib/application/user/profile_tab/profile_tab_bloc.dart b/lib/application/user/profile_tab/profile_tab_bloc.dart index 39227e7c..9b53aceb 100644 --- a/lib/application/user/profile_tab/profile_tab_bloc.dart +++ b/lib/application/user/profile_tab/profile_tab_bloc.dart @@ -11,7 +11,7 @@ part 'profile_tab_state.dart'; class ProfileTabBloc extends Bloc { final ICrowdActionRepository crowdActionRepository; - ProfileTabBloc({required this.crowdActionRepository}) + ProfileTabBloc(this.crowdActionRepository) : super(ProfileTabState.initial()) { on((event, emit) async { final crowdActionsOrFailure = diff --git a/lib/domain/user/user.dart b/lib/domain/user/user.dart index 7548b019..d5b80ec5 100644 --- a/lib/domain/user/user.dart +++ b/lib/domain/user/user.dart @@ -19,7 +19,6 @@ class User with _$User { bool get isAnonymous => this == anonymous; - /// TODO: Refactor after MVP to only neccessary fields (use profile microservice for details) const factory User({ required String id, required Future Function([bool forceRefresh]) getIdToken, diff --git a/lib/presentation/core/collaction_icons.dart b/lib/presentation/core/collaction_icons.dart index 000c8e86..2d0fb003 100644 --- a/lib/presentation/core/collaction_icons.dart +++ b/lib/presentation/core/collaction_icons.dart @@ -17,8 +17,6 @@ import 'package:flutter/widgets.dart'; class CollactionIcons { - const CollactionIcons._(); - static const _kFontFam = 'CollactionIcons'; static const IconData eye = IconData(0xe800, fontFamily: _kFontFam); diff --git a/lib/presentation/crowdaction/crowdaction_details/widgets/withdraw_participation.dart b/lib/presentation/crowdaction/crowdaction_details/widgets/withdraw_participation.dart index 5caaafb4..513b3f92 100644 --- a/lib/presentation/crowdaction/crowdaction_details/widgets/withdraw_participation.dart +++ b/lib/presentation/crowdaction/crowdaction_details/widgets/withdraw_participation.dart @@ -93,7 +93,6 @@ class WithdrawParticipation extends StatelessWidget { fontSize: 28, ), ), - const SizedBox( height: 20, ), @@ -105,10 +104,6 @@ class WithdrawParticipation extends StatelessWidget { margin: EdgeInsets.zero, ), const SizedBox(height: 20), - //TODO - Add message to change commitments const Divider(), - // const SizedBox( - // height: 15, - // ), ], ), ); diff --git a/lib/presentation/profile/profile_screen.dart b/lib/presentation/profile/profile_screen.dart index 4d697258..43035b7c 100644 --- a/lib/presentation/profile/profile_screen.dart +++ b/lib/presentation/profile/profile_screen.dart @@ -350,7 +350,6 @@ class _UserProfilePageState extends State { ), ], ] else ...[ - // TODO only for MVP (remove later) const SizedBox(height: 40), PillButton( text: 'Sign in', diff --git a/test/application/auth/auth_bloc_tests.dart b/test/application/auth/auth_bloc_test.dart similarity index 97% rename from test/application/auth/auth_bloc_tests.dart rename to test/application/auth/auth_bloc_test.dart index 1e68ca32..9bd1e4b0 100644 --- a/test/application/auth/auth_bloc_tests.dart +++ b/test/application/auth/auth_bloc_test.dart @@ -6,9 +6,8 @@ import 'package:collaction_app/domain/auth/i_auth_repository.dart'; import 'package:collaction_app/domain/user/i_avatar_repository.dart'; import 'package:collaction_app/domain/user/i_user_repository.dart'; import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -// ignore: depend_on_referenced_packages -import 'package:test/test.dart'; class MockAuthRepository extends Mock implements IAuthRepository {} diff --git a/test/application/build_information/build_information_bloc_test.dart b/test/application/build_information/build_information_bloc_test.dart new file mode 100644 index 00000000..aef0139e --- /dev/null +++ b/test/application/build_information/build_information_bloc_test.dart @@ -0,0 +1,41 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:collaction_app/application/settings/build_information/build_information_bloc.dart'; + +import 'package:collaction_app/domain/settings/build_information.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../domain/core/i_settings_repository.mocks.dart'; + +void main() { + group('Testing Build Information BLoC', () { + late final SettingsRepositoryMock settingsRepository; + late final BuildInformationBloc bloc; + + setUpAll(() { + settingsRepository = SettingsRepositoryMock(); + bloc = BuildInformationBloc(settingsRepository); + }); + + test('Initial state', () { + expect(bloc.state, const BuildInformationState.loading()); + }); + + blocTest( + 'Fetch', + build: () => bloc, + act: (bloc) { + when(() => settingsRepository.getBuildInformation()) + .thenAnswer((_) => Future.value(tBuildInformation)); + + bloc.add(BuildInformationEvent.fetch()); + }, + expect: () => [ + const BuildInformationState.loading(), + BuildInformationState.fetched(tBuildInformation), + ], + ); + }); +} + +final tBuildInformation = BuildInformation(version: '1.0.0', buildNumber: '1'); diff --git a/test/application/contact_form/contact_form_test.dart b/test/application/contact_form/contact_form_test.dart index 1d8e54d0..7fc11962 100644 --- a/test/application/contact_form/contact_form_test.dart +++ b/test/application/contact_form/contact_form_test.dart @@ -1,5 +1,6 @@ import 'package:bloc_test/bloc_test.dart'; import 'package:collaction_app/application/contact_form/contact_form_bloc.dart'; +import 'package:collaction_app/domain/contact_form/contact_failures.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; @@ -11,20 +12,38 @@ void main() { expect(tContactFormBloc.state, const ContactFormState.initial()); }); - { - when(() => tContactFormApi.sendContactFormContents(tContactFormDtoS)) - .thenAnswer((_) => Future.value(right(unit))); - blocTest( - 'Testing submit event success', - build: () => ContactFormBloc(tContactFormApi), - act: (ContactFormBloc bloc) { - bloc.add(ContactFormEvent.submitted(tContactFormDtoS)); - }, - expect: () => [ - const ContactFormState.submitting(), - const ContactFormState.submissionSuccessful(), - ], - ); - } + blocTest( + 'Testing submit event success', + build: () => ContactFormBloc(tContactFormApi), + act: (ContactFormBloc bloc) { + when(() => tContactFormApi.sendContactFormContents(tContactFormDtoS)) + .thenAnswer((_) => Future.value(right(unit))); + + bloc.add(ContactFormEvent.submitted(tContactFormDtoS)); + }, + expect: () => [ + const ContactFormState.submitting(), + const ContactFormState.submissionSuccessful(), + ], + ); + + blocTest( + 'Testing submit event failure', + build: () => ContactFormBloc(tContactFormApi), + act: (ContactFormBloc bloc) { + when(() => tContactFormApi.sendContactFormContents(tContactFormDtoS)) + .thenAnswer( + (_) => Future.value( + left(ContactFailure.serverError()), + ), + ); + + bloc.add(ContactFormEvent.submitted(tContactFormDtoS)); + }, + expect: () => [ + const ContactFormState.submitting(), + const ContactFormState.failed('Submission failed'), + ], + ); }); } diff --git a/test/application/username/username_bloc_test.dart b/test/application/username/username_bloc_test.dart new file mode 100644 index 00000000..8fb3b78f --- /dev/null +++ b/test/application/username/username_bloc_test.dart @@ -0,0 +1,69 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:collaction_app/application/username/username_bloc.dart'; +import 'package:collaction_app/domain/user/profile_failure.dart'; + +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../test_utilities.dart'; + +void main() { + group('Testing Username BLoC', () { + late final MockProfileRepository profileRepository; + late UsernameBloc bloc; + + setUpAll(() { + profileRepository = MockProfileRepository(); + bloc = UsernameBloc(profileRepository); + }); + + setUp(() { + // Reset BLOC + bloc = UsernameBloc(profileRepository); + }); + + test('Initial state', () { + expect(bloc.state, const UsernameState.initial()); + }); + + blocTest( + 'Update username success', + build: () => bloc, + act: (bloc) { + when(() => profileRepository.updateUsername( + firstName: tFirstName, lastName: tLastName)).thenAnswer( + (_) => Future.value(right(unit)), + ); + + bloc.add(UsernameEvent.updateUsername( + firstName: tFirstName, lastName: tLastName)); + }, + expect: () => [ + const UsernameState.updateInProgress(), + UsernameState.updateSuccess('$tFirstName $tLastName'), + ], + ); + + blocTest( + 'Update username failure', + build: () => bloc, + act: (bloc) { + when(() => profileRepository.updateUsername( + firstName: tFirstName, lastName: tLastName)).thenAnswer( + (_) => Future.value(left(ProfileFailure.unexpected())), + ); + + bloc.add(UsernameEvent.updateUsername( + firstName: tFirstName, lastName: tLastName)); + }, + expect: () => [ + const UsernameState.updateInProgress(), + const UsernameState.updateFailure(), + ], + ); + }); +} + +final tFirstName = "Mathias"; +final tLastName = "M"; diff --git a/test/presentation/core/ionicons_utils_test.dart b/test/presentation/core/ionicons_utils_test.dart new file mode 100644 index 00000000..449a67e4 --- /dev/null +++ b/test/presentation/core/ionicons_utils_test.dart @@ -0,0 +1,18 @@ +import 'package:collaction_app/presentation/core/collaction_icons.dart'; +import 'package:collaction_app/presentation/core/ionicons_utils.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:ionicons/ionicons.dart'; + +void main() { + group('IonIcons Utils tests', () { + test('does exist', () { + final icon = IconUtil.fromString('accessibility-outline'); + expect(icon, Ionicons.accessibility_outline); + }); + + test('does not exist', () { + final icon = IconUtil.fromString('collaction-no-icon'); + expect(icon, CollactionIcons.collaction); + }); + }); +} diff --git a/test/presentation/home/home_screen_test.dart b/test/presentation/home/home_screen_test.dart new file mode 100644 index 00000000..408015c4 --- /dev/null +++ b/test/presentation/home/home_screen_test.dart @@ -0,0 +1,81 @@ +import 'package:collaction_app/application/crowdaction/spotlight/spotlight_bloc.dart'; +import 'package:collaction_app/domain/core/i_settings_repository.dart'; +import 'package:collaction_app/domain/crowdaction/crowdaction_failures.dart'; +import 'package:collaction_app/presentation/home/home_screen.dart'; +import 'package:collaction_app/presentation/onboarding/onboarding_screen.dart'; +import 'package:collaction_app/presentation/routes/app_routes.gr.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get_it/get_it.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../test_helper.dart'; +import '../../test_utilities.dart'; + +void main() { + late final AppRouter appRouter; + late final MockSettingsRepository settingsRepository; + late final MockCrowdActionRepository crowdActionRepository; + late SpotlightBloc spotlightBloc; + + setUpAll(() { + appRouter = AppRouter(); + settingsRepository = MockSettingsRepository(); + crowdActionRepository = MockCrowdActionRepository(); + spotlightBloc = SpotlightBloc(crowdActionRepository); + + GetIt.I.registerSingleton(spotlightBloc); + GetIt.I.registerSingleton(settingsRepository); + + when(() => crowdActionRepository.getSpotlightCrowdActions()).thenAnswer( + (invocation) => Future.value(left(CrowdActionFailure.serverError()))); + }); + + tearDownAll(() { + GetIt.I.unregister(); + GetIt.I.unregister(); + }); + + tearDown(() { + GetIt.I.unregister(); + spotlightBloc = SpotlightBloc(crowdActionRepository); + GetIt.I.registerSingleton(spotlightBloc); + }); + + testWidgets('can render', (tester) async { + when(() => settingsRepository.getWasUserOnboarded()) + .thenAnswer((_) => Future.value(true)); + + await buildAndPump( + tester: tester, + widget: MaterialApp.router( + color: Colors.white, + title: 'CollAction', + routerDelegate: appRouter.delegate(), + routeInformationParser: appRouter.defaultRouteParser(), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(HomePage), findsOneWidget); + }); + + testWidgets('show onboarding', (tester) async { + when(() => settingsRepository.getWasUserOnboarded()) + .thenAnswer((_) => Future.value(false)); + + await buildAndPump( + tester: tester, + widget: MaterialApp.router( + color: Colors.white, + title: 'CollAction', + routerDelegate: appRouter.delegate(), + routeInformationParser: appRouter.defaultRouteParser(), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(OnboardingPage), findsOneWidget); + }); +} diff --git a/test/presentation/profile/profile_screen_test.dart b/test/presentation/profile/profile_screen_test.dart new file mode 100644 index 00000000..45960ed8 --- /dev/null +++ b/test/presentation/profile/profile_screen_test.dart @@ -0,0 +1,164 @@ +import 'package:collaction_app/application/auth/auth_bloc.dart'; +import 'package:collaction_app/application/crowdaction/spotlight/spotlight_bloc.dart'; +import 'package:collaction_app/application/user/profile/profile_bloc.dart'; +import 'package:collaction_app/application/user/profile_tab/profile_tab_bloc.dart'; +import 'package:collaction_app/domain/core/i_settings_repository.dart'; +import 'package:collaction_app/domain/crowdaction/crowdaction_failures.dart'; +import 'package:collaction_app/domain/profile/profile.dart'; +import 'package:collaction_app/domain/profile/user_profile.dart'; +import 'package:collaction_app/domain/user/user.dart'; +import 'package:collaction_app/presentation/core/collaction_icons.dart'; +import 'package:collaction_app/presentation/home/home_screen.dart'; +import 'package:collaction_app/presentation/profile/profile_screen.dart'; +import 'package:collaction_app/presentation/routes/app_routes.gr.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get_it/get_it.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../application/auth/auth_bloc.mocks.dart'; +import '../../test_helper.dart'; +import '../../test_utilities.dart'; + +void main() { + late final AppRouter appRouter; + late final MockSettingsRepository settingsRepository; + late final MockCrowdActionRepository crowdActionRepository; + late final MockProfileRepository profileRepository; + late final MockAvatarRepository avatarRepository; + late ProfileTabBloc profileTabBloc; + late SpotlightBloc spotlightBloc; + late ProfileBloc profileBloc; + late MockAuthBloc authBloc; + + setUpAll(() { + appRouter = AppRouter(); + settingsRepository = MockSettingsRepository(); + crowdActionRepository = MockCrowdActionRepository(); + profileRepository = MockProfileRepository(); + avatarRepository = MockAvatarRepository(); + authBloc = MockAuthBloc(); + spotlightBloc = SpotlightBloc(crowdActionRepository); + profileBloc = ProfileBloc(profileRepository, avatarRepository); + profileTabBloc = ProfileTabBloc(crowdActionRepository); + + GetIt.I.registerSingleton(profileBloc); + GetIt.I.registerSingleton(profileTabBloc); + GetIt.I.registerSingleton(authBloc); + GetIt.I.registerSingleton(spotlightBloc); + GetIt.I.registerSingleton(settingsRepository); + + when(() => crowdActionRepository.getSpotlightCrowdActions()).thenAnswer( + (invocation) => Future.value(left(CrowdActionFailure.serverError()))); + + when(() => crowdActionRepository.getCrowdActionsForUser()).thenAnswer( + (invocation) => Future.value(left(CrowdActionFailure.serverError()))); + }); + + tearDownAll(() { + GetIt.I.unregister(); + GetIt.I.unregister(); + GetIt.I.unregister(); + GetIt.I.unregister(); + GetIt.I.unregister(); + }); + + tearDown(() { + GetIt.I.unregister(); + GetIt.I.unregister(); + GetIt.I.unregister(); + + profileBloc = ProfileBloc(profileRepository, avatarRepository); + profileTabBloc = ProfileTabBloc(crowdActionRepository); + spotlightBloc = SpotlightBloc(crowdActionRepository); + + GetIt.I.registerSingleton(profileBloc); + GetIt.I.registerSingleton(profileTabBloc); + GetIt.I.registerSingleton(spotlightBloc); + }); + + testWidgets('navigate to profile screen', (tester) async { + when(() => settingsRepository.getWasUserOnboarded()) + .thenAnswer((_) => Future.value(true)); + + await buildAndPump( + tester: tester, + widget: MultiBlocProvider( + providers: [ + BlocProvider(create: (context) => profileBloc), + BlocProvider(create: (context) => profileTabBloc), + ], + child: MaterialApp.router( + color: Colors.white, + title: 'CollAction', + routerDelegate: appRouter.delegate(), + routeInformationParser: appRouter.defaultRouteParser(), + ), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(HomePage), findsOneWidget); + + await tester.tap(find.byIcon(CollactionIcons.user)); + await tester.pumpAndSettle(); + + expect(find.byType(UserProfilePage), findsOneWidget); + }); + + testWidgets('profile screen no user', (tester) async { + await buildAndPump( + tester: tester, + widget: MultiBlocProvider( + providers: [ + BlocProvider(create: (context) => profileBloc), + BlocProvider(create: (context) => profileTabBloc), + ], + child: UserProfilePage(), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(UserProfilePage), findsOneWidget); + }); + + testWidgets('profile screen w/ user', (tester) async { + when(() => profileRepository.getUserProfile()).thenAnswer( + (_) => Future.value( + right( + UserProfile( + user: User.anonymous, + profile: Profile( + userId: 'userId', + firstName: 'firstName', + lastName: 'lastName', + avatar: 'avatar', + bio: 'bio', + ), + ), + ), + ), + ); + + await buildAndPump( + tester: tester, + widget: MultiBlocProvider( + providers: [ + BlocProvider(create: (context) => profileBloc), + BlocProvider(create: (context) => profileTabBloc), + ], + child: UserProfilePage(), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(UserProfilePage), findsOneWidget); + + profileBloc.add(EditProfile()); + await tester.pumpAndSettle(); + + expect(profileBloc.state.isEditing, true); + }); +} diff --git a/test/user_repository/user_repository_tests.dart b/test/user_repository/user_repository_tests.dart deleted file mode 100644 index 51e12808..00000000 --- a/test/user_repository/user_repository_tests.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:collaction_app/domain/user/i_user_repository.dart'; -import 'package:collaction_app/domain/user/user.dart'; -import 'package:collaction_app/infrastructure/user/user_repository.dart'; -import 'package:firebase_auth/firebase_auth.dart' as firebase_auth; -import 'package:firebase_auth_mocks/firebase_auth_mocks.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; - -class CustomMockFirebaseAuth extends Mock - implements firebase_auth.FirebaseAuth {} - -class CustomMockUserCredential extends Mock - implements firebase_auth.UserCredential {} - -void main() { - setUpAll(() { - registerFallbackValue( - const firebase_auth.AuthCredential( - signInMethod: 'mock', - providerId: 'mock', - ), - ); - }); - - group('User Repository Tests:', () { - test( - 'New user (anonymous)', - () async { - // ignore: avoid_redundant_argument_values - final mockFirebaseAuth = MockFirebaseAuth(signedIn: false); - final userRepository = UserRepository(firebaseAuth: mockFirebaseAuth); - final user = await userRepository.observeUser().first; - expect(user, User.anonymous); - expect(user.isAnonymous, true); - }, - timeout: const Timeout(Duration(seconds: 5)), - ); - - test( - 'Returning user', - () async { - // ignore: avoid_redundant_argument_values - final mockFirebaseAuth = MockFirebaseAuth(signedIn: true); - final userRepository = UserRepository(firebaseAuth: mockFirebaseAuth); - final user = await userRepository.observeUser().skip(1).first; - expect(user, isNot(User.anonymous)); - expect(user.isAnonymous, false); - }, - timeout: const Timeout(Duration(seconds: 5)), - ); - - Future testRegistration({required bool isNewUser}) async { - final mockFirebaseAuth = CustomMockFirebaseAuth(); - final mockUserCredential = CustomMockUserCredential(); - when(() => mockFirebaseAuth.signInWithCredential(any())) - .thenAnswer((_) => Future.value(mockUserCredential)); - when(() => mockFirebaseAuth.authStateChanges()) - .thenAnswer((_) => const Stream.empty()); - when(() => mockUserCredential.additionalUserInfo) - .thenReturn(firebase_auth.AdditionalUserInfo(isNewUser: isNewUser)); - final userRepository = UserRepository(firebaseAuth: mockFirebaseAuth); - // First part of registration cannot be meaningfully tested using mock - final signInResult = await userRepository.signIn( - const Credential( - verificationId: 'someVerificationId', - smsCode: 'someSmsCode', - ), - ); - expect(signInResult.isNewUser, isNewUser); - } - - test('Register with phone number', () async { - await testRegistration(isNewUser: true); - }); - - test('Log in with phone number', () async { - await testRegistration(isNewUser: false); - }); - }); -} From c7855ac68200f61c56a7755629fa43b302109dca Mon Sep 17 00:00:00 2001 From: Mathias Mogensencd collaction_website Date: Sun, 12 Feb 2023 13:49:57 +0100 Subject: [PATCH 09/30] tests: crowdaction repository tests --- .../crowdaction/i_crowdaction_repository.dart | 10 - .../crowdaction/crowdaction_repository.dart | 72 ------ .../crowdaction_repository_fixtures.dart | 131 +++++++++++ .../crowdaction_repository_test.dart | 221 ++++++++++++++++++ test/test_utilities.dart | 6 - 5 files changed, 352 insertions(+), 88 deletions(-) create mode 100644 test/infrastructure/crowdaction/crowdaction_repository_fixtures.dart create mode 100644 test/infrastructure/crowdaction/crowdaction_repository_test.dart diff --git a/lib/domain/crowdaction/i_crowdaction_repository.dart b/lib/domain/crowdaction/i_crowdaction_repository.dart index 1ead614c..7492c669 100644 --- a/lib/domain/crowdaction/i_crowdaction_repository.dart +++ b/lib/domain/crowdaction/i_crowdaction_repository.dart @@ -16,16 +16,6 @@ abstract class ICrowdActionRepository { Future>> getSpotlightCrowdActions(); - Future> subscribeToCrowdAction( - CrowdAction crowdAction, - List commitments, - String? password, - ); - - Future> unsubscribeFromCrowdAction( - CrowdAction crowdAction, - ); - Future>> getCrowdActionsForUser(); } diff --git a/lib/infrastructure/crowdaction/crowdaction_repository.dart b/lib/infrastructure/crowdaction/crowdaction_repository.dart index 638601c4..0c8e948e 100644 --- a/lib/infrastructure/crowdaction/crowdaction_repository.dart +++ b/lib/infrastructure/crowdaction/crowdaction_repository.dart @@ -91,78 +91,6 @@ class CrowdActionRepository implements ICrowdActionRepository { } } - @override - Future> subscribeToCrowdAction( - CrowdAction crowdAction, - List commitments, - String? password, - ) async { - try { - final user = (await _authRepository.getSignedInUser()) - .getOrElse(() => throw NotAuthenticatedError()); - final tokenId = await user.getIdToken(); - - final uri = Uri.parse( - '${await _settingsRepository.baseApiEndpointUrl}/v1/participations', - ); - - final response = await _client.post( - uri, - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer $tokenId' - }, - body: jsonEncode({ - 'crowdActionId': crowdAction.id, - 'password': password, - 'commitmentOptions': commitments - }), - ); - - if (response.statusCode == 200) { - return right(unit); - } else { - return left(const CrowdActionFailure.networkRequestFailed()); - } - } catch (e) { - return left(const CrowdActionFailure.networkRequestFailed()); - } - } - - @override - Future> unsubscribeFromCrowdAction( - CrowdAction crowdAction, - ) async { - try { - final user = (await _authRepository.getSignedInUser()) - .getOrElse(() => throw NotAuthenticatedError()); - final tokenId = await user.getIdToken(); - - final uri = Uri.parse( - '${await _settingsRepository.baseApiEndpointUrl}/v1/participations', - ); - - final response = await _client.post( - uri, - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer $tokenId' - }, - body: jsonEncode({ - 'crowdActionId': crowdAction.id, - }), - ); - - if (response.statusCode == 200) { - return right(unit); - } else { - return left(const CrowdActionFailure.networkRequestFailed()); - } - } catch (e) { - return left(const CrowdActionFailure.networkRequestFailed()); - } - } - @override Future>> getSpotlightCrowdActions() async { diff --git a/test/infrastructure/crowdaction/crowdaction_repository_fixtures.dart b/test/infrastructure/crowdaction/crowdaction_repository_fixtures.dart new file mode 100644 index 00000000..5e417623 --- /dev/null +++ b/test/infrastructure/crowdaction/crowdaction_repository_fixtures.dart @@ -0,0 +1,131 @@ +final crowdActionRaw = """{ + "id": "12345678", + "type": "TYPE", + "title": "Veganuary", + "description": "description...", + "category": "CATEGORY", + "location": { + "code": "NL", + "name": "Netherlands" + }, + "commitmentOptions": [ + { + "id": "12345678", + "type": "TYPE", + "label": "label", + "points": 50, + "blocks": [ + "123456789" + ], + "description": "description..." + }, + { + "id": "123456789", + "type": "TYPE", + "label": "label", + "points": 50, + "blocks": [], + "description": "description..." + } + ], + "images": { + "card": "https://example.com/card", + "banner": "https://example.com/banner" + }, + "participantCount": 100, + "status": "STARTED", + "joinStatus": "CLOSED", + "endAt": "2024-01-01T00:00:00.000+00:00", + "subcategory": "SUBCATEGORY" +}"""; + +final crowdActionsRaw = """{ + "pageInfo": { + "page": 1, + "pageSize": 2, + "totalPages": 1, + "totalItems": 2 + }, + "items": [ + { + "id": "123456789", + "type": "TYPE", + "title": "Veganuary", + "description": "description...", + "category": "CATEGORY", + "location": { + "code": "NL", + "name": "Netherlands" + }, + "commitmentOptions": [ + { + "id": "12345678", + "type": "TYPE", + "label": "label", + "points": 50, + "blocks": [ + "123456789" + ], + "description": "description..." + }, + { + "id": "123456789", + "type": "TYPE", + "label": "label", + "points": 50, + "blocks": [], + "description": "description..." + } + ], + "images": { + "card": "https://example.com/card", + "banner": "https://example.com/banner" + }, + "participantCount": 100, + "status": "STARTED", + "joinStatus": "CLOSED", + "endAt": "2024-01-01T00:00:00.000+00:00", + "subcategory": "SUBCATEGORY" + }, + { + "id": "123456789", + "type": "TYPE", + "title": "Veganuary", + "description": "description...", + "category": "CATEGORY", + "location": { + "code": "NL", + "name": "Netherlands" + }, + "commitmentOptions": [ + { + "id": "12345678", + "type": "TYPE", + "label": "label", + "points": 50, + "blocks": [ + "123456789" + ], + "description": "description..." + }, + { + "id": "123456789", + "type": "TYPE", + "label": "label", + "points": 50, + "blocks": [], + "description": "description..." + } + ], + "images": { + "card": "https://example.com/card", + "banner": "https://example.com/banner" + }, + "participantCount": 100, + "status": "STARTED", + "joinStatus": "CLOSED", + "endAt": "2024-01-01T00:00:00.000+00:00", + "subcategory": "SUBCATEGORY" + } + ] +}"""; diff --git a/test/infrastructure/crowdaction/crowdaction_repository_test.dart b/test/infrastructure/crowdaction/crowdaction_repository_test.dart new file mode 100644 index 00000000..390e8c3f --- /dev/null +++ b/test/infrastructure/crowdaction/crowdaction_repository_test.dart @@ -0,0 +1,221 @@ +import 'package:collaction_app/domain/auth/i_auth_repository.dart'; +import 'package:collaction_app/domain/core/i_settings_repository.dart'; +import 'package:collaction_app/domain/user/user.dart'; +import 'package:collaction_app/infrastructure/crowdaction/crowdaction_repository.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../domain/core/i_settings_repository.mocks.dart'; +import '../../test_utilities.dart'; +import 'crowdaction_repository_fixtures.dart'; + +void main() { + late final IAuthRepository authRepository; + late final ISettingsRepository settingsRepository; + late final MockHttpClient client; + late final CrowdActionRepository crowdActionRepository; + + setUpAll(() { + registerFallbackValue(Uri()); + + authRepository = MockAuthRepository(); + settingsRepository = SettingsRepositoryMock(); + client = MockHttpClient(); + + crowdActionRepository = CrowdActionRepository( + client, + authRepository, + settingsRepository, + ); + + when(() => settingsRepository.baseApiEndpointUrl) + .thenAnswer((invocation) => Future.value('https://example.com')); + }); + + group('CrowdActionRepository tests', () { + test('getCrowdAction success', () async { + when(() => client.get(any())).thenAnswer( + (invocation) => Future.value(Response(crowdActionRaw, 200))); + + final crowdActionEither = + await crowdActionRepository.getCrowdAction('12345678'); + + expect(crowdActionEither.isRight(), true); + + final crowdAction = crowdActionEither.fold((l) => null, (r) => r); + expect(crowdAction, isNotNull); + }); + + test('getCrowdAction failure', () async { + when(() => client.get(any())) + .thenAnswer((invocation) => Future.value(Response('', 400))); + + final crowdActionEither = + await crowdActionRepository.getCrowdAction('12345678'); + + expect(crowdActionEither.isLeft(), true); + }); + + test('getCrowdAction failure by exception', () async { + when(() => client.get(any())) + .thenAnswer((invocation) => throw Exception()); + + final crowdActionEither = + await crowdActionRepository.getCrowdAction('12345678'); + + expect(crowdActionEither.isLeft(), true); + }); + + test('getCrowdActions success', () async { + when(() => client.get(any())).thenAnswer( + (invocation) => Future.value(Response(crowdActionsRaw, 200))); + + final paginatedCrowdActionEither = + await crowdActionRepository.getCrowdActions(pageNumber: 1); + + expect(paginatedCrowdActionEither.isRight(), true); + + final paginatedCrowdActions = + paginatedCrowdActionEither.fold((l) => null, (r) => r); + expect(paginatedCrowdActions, isNotNull); + + expect(paginatedCrowdActions!.crowdActions.length, 2); + }); + + test('getCrowdActions failure', () async { + when(() => client.get(any())) + .thenAnswer((invocation) => Future.value(Response('', 400))); + + final paginatedCrowdActionEither = + await crowdActionRepository.getCrowdActions(pageNumber: 1); + + expect(paginatedCrowdActionEither.isLeft(), true); + + final paginatedCrowdActions = + paginatedCrowdActionEither.fold((l) => null, (r) => r); + expect(paginatedCrowdActions, isNull); + }); + + test('getCrowdActions failure by exception', () async { + when(() => client.get(any())) + .thenAnswer((invocation) => throw Exception()); + + final paginatedCrowdActionEither = + await crowdActionRepository.getCrowdActions(pageNumber: 1); + + expect(paginatedCrowdActionEither.isLeft(), true); + + final paginatedCrowdActions = + paginatedCrowdActionEither.fold((l) => null, (r) => r); + expect(paginatedCrowdActions, isNull); + }); + + test('getSpotlightCrowdActions success', () async { + when(() => client.get(any())).thenAnswer( + (invocation) => Future.value(Response(crowdActionsRaw, 200))); + + final paginatedCrowdActionEither = + await crowdActionRepository.getSpotlightCrowdActions(); + + expect(paginatedCrowdActionEither.isRight(), true); + + final paginatedCrowdActions = + paginatedCrowdActionEither.fold((l) => null, (r) => r); + expect(paginatedCrowdActions, isNotNull); + }); + + test('getSpotlightCrowdActions failure', () async { + when(() => client.get(any())) + .thenAnswer((invocation) => Future.value(Response('', 400))); + + final paginatedCrowdActionEither = + await crowdActionRepository.getSpotlightCrowdActions(); + + expect(paginatedCrowdActionEither.isLeft(), true); + + final paginatedCrowdActions = + paginatedCrowdActionEither.fold((l) => null, (r) => r); + expect(paginatedCrowdActions, isNull); + }); + + test('getSpotlightCrowdActions failure by exception', () async { + when(() => client.get(any())) + .thenAnswer((invocation) => throw Exception()); + + final paginatedCrowdActionEither = + await crowdActionRepository.getSpotlightCrowdActions(); + + expect(paginatedCrowdActionEither.isLeft(), true); + + final paginatedCrowdActions = + paginatedCrowdActionEither.fold((l) => null, (r) => r); + expect(paginatedCrowdActions, isNull); + }); + + test('getCrowdActionsForUser success', () async { + when(() => client.get(any(), headers: any(named: 'headers'))).thenAnswer( + (invocation) => Future.value(Response(crowdActionsRaw, 200))); + + when(() => authRepository.getSignedInUser()) + .thenAnswer((invocation) => Future.value(optionOf(User.anonymous))); + + final crowdActionEither = + await crowdActionRepository.getCrowdActionsForUser(); + + expect(crowdActionEither.isRight(), true); + + final crowdAction = crowdActionEither.fold((l) => null, (r) => r); + expect(crowdAction, isNotNull); + }); + + test('getCrowdActionsForUser failure', () async { + when(() => client.get(any(), headers: any(named: 'headers'))) + .thenAnswer((invocation) => Future.value(Response('', 400))); + + when(() => authRepository.getSignedInUser()) + .thenAnswer((invocation) => Future.value(optionOf(User.anonymous))); + + final crowdActionEither = + await crowdActionRepository.getCrowdActionsForUser(); + + expect(crowdActionEither.isLeft(), true); + + final crowdAction = crowdActionEither.fold((l) => null, (r) => r); + expect(crowdAction, isNull); + }); + + test('getCrowdActionsForUser failure by null user', () async { + when(() => client.get(any(), headers: any(named: 'headers'))).thenAnswer( + (invocation) => Future.value(Response(crowdActionsRaw, 200))); + + when(() => authRepository.getSignedInUser()) + .thenAnswer((invocation) => Future.value(optionOf(null))); + + final crowdActionEither = + await crowdActionRepository.getCrowdActionsForUser(); + + expect(crowdActionEither.isLeft(), true); + + final crowdAction = crowdActionEither.fold((l) => null, (r) => r); + expect(crowdAction, isNull); + }); + + test('getCrowdActionsForUser failure by excpetion', () async { + when(() => client.get(any(), headers: any(named: 'headers'))).thenAnswer( + (invocation) => Future.value(Response(crowdActionsRaw, 200))); + + when(() => authRepository.getSignedInUser()) + .thenAnswer((invocation) => throw Exception()); + + final crowdActionEither = + await crowdActionRepository.getCrowdActionsForUser(); + + expect(crowdActionEither.isLeft(), true); + + final crowdAction = crowdActionEither.fold((l) => null, (r) => r); + expect(crowdAction, isNull); + }); + }); +} diff --git a/test/test_utilities.dart b/test/test_utilities.dart index cd0d48d8..73b88766 100644 --- a/test/test_utilities.dart +++ b/test/test_utilities.dart @@ -78,12 +78,6 @@ class TestUtilities { (_) async => right(crowdActions.map((u) => u.toDomain()).toList()), ); - when(() => crowdActionRepo.subscribeToCrowdAction(any(), any(), any())) - .thenAnswer((_) async => right(unit)); - - when(() => crowdActionRepo.unsubscribeFromCrowdAction(any())) - .thenAnswer((_) async => right(unit)); - GetIt.instance.registerSingleton(crowdActionRepo); } From f9ce74a584ba52b724efa3e219d54910ef19b473 Mon Sep 17 00:00:00 2001 From: Mathias Mogensencd collaction_website Date: Sun, 12 Feb 2023 14:25:48 +0100 Subject: [PATCH 10/30] tests: add participation repository tests --- .../participation_repository_fixtures.dart | 41 ++++ .../participation_repository_test.dart | 220 ++++++++++++++++++ 2 files changed, 261 insertions(+) create mode 100644 test/infrastructure/participation/participation_repository_fixtures.dart create mode 100644 test/infrastructure/participation/participation_repository_test.dart diff --git a/test/infrastructure/participation/participation_repository_fixtures.dart b/test/infrastructure/participation/participation_repository_fixtures.dart new file mode 100644 index 00000000..a1e56ee7 --- /dev/null +++ b/test/infrastructure/participation/participation_repository_fixtures.dart @@ -0,0 +1,41 @@ +final participationRaw = """{ + "id": "12345678", + "crowdActionId": "123456", + "fullName": "John Doe", + "avatar": "/avatar/123456.png", + "userId": "1234-1234-1234-1234", + "commitmentOptions": ["1234"], + "joinDate": "2024-01-01T00:00:00.000+00:00", + "dailyCheckIns": 10 +}"""; + +final participationsRaw = """{ + "pageInfo": { + "page": 1, + "pageSize": 2, + "totalPages": 1, + "totalItems": 2 + }, + "items": [ + { + "id": "12345678", + "crowdActionId": "123456", + "fullName": "John Doe", + "avatar": "/avatar/123456.png", + "userId": "1234-1234-1234-1234", + "commitmentOptions": ["1234"], + "joinDate": "2024-01-01T00:00:00.000+00:00", + "dailyCheckIns": 10 + }, + { + "id": "123456789", + "crowdActionId": "123456", + "fullName": "John Doe", + "avatar": "/avatar/123456.png", + "userId": "1234-1234-1234-1234", + "commitmentOptions": ["1234"], + "joinDate": "2024-01-01T00:00:00.000+00:00", + "dailyCheckIns": 10 + } + ] +}"""; diff --git a/test/infrastructure/participation/participation_repository_test.dart b/test/infrastructure/participation/participation_repository_test.dart new file mode 100644 index 00000000..b6112ce6 --- /dev/null +++ b/test/infrastructure/participation/participation_repository_test.dart @@ -0,0 +1,220 @@ +import 'package:collaction_app/domain/auth/i_auth_repository.dart'; +import 'package:collaction_app/domain/core/i_settings_repository.dart'; +import 'package:collaction_app/domain/user/user.dart'; +import 'package:collaction_app/infrastructure/participation/participation_repository.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../domain/core/i_settings_repository.mocks.dart'; +import '../../test_utilities.dart'; +import 'participation_repository_fixtures.dart'; + +void main() { + late final IAuthRepository authRepository; + late final ISettingsRepository settingsRepository; + late final MockHttpClient client; + late final ParticipationRepository participationRepository; + + setUpAll(() { + registerFallbackValue(Uri()); + + authRepository = MockAuthRepository(); + settingsRepository = SettingsRepositoryMock(); + client = MockHttpClient(); + + participationRepository = ParticipationRepository( + client, + authRepository, + settingsRepository, + ); + + when(() => settingsRepository.baseApiEndpointUrl) + .thenAnswer((invocation) => Future.value('https://example.com')); + }); + + group('ParticipationRepository tests', () { + test('getParticipation success', () async { + when(() => client.get(any(), headers: any(named: 'headers'))).thenAnswer( + (invocation) => Future.value(Response(participationRaw, 200))); + + when(() => authRepository.getSignedInUser()) + .thenAnswer((invocation) => Future.value(optionOf(User.anonymous))); + + final participationOrFailure = await participationRepository + .getParticipation(crowdActionId: '123456'); + + expect(participationOrFailure.isRight(), true); + }); + + test('getParticipation failure by 500', () async { + when(() => client.get(any(), headers: any(named: 'headers'))) + .thenAnswer((invocation) => Future.value(Response('', 500))); + + when(() => authRepository.getSignedInUser()) + .thenAnswer((invocation) => Future.value(optionOf(User.anonymous))); + + final participationOrFailure = await participationRepository + .getParticipation(crowdActionId: '123456'); + + expect(participationOrFailure.isLeft(), true); + }); + + test('getParticipation failure', () async { + when(() => client.get(any(), headers: any(named: 'headers'))) + .thenAnswer((invocation) => Future.value(Response('', 400))); + + when(() => authRepository.getSignedInUser()) + .thenAnswer((invocation) => Future.value(optionOf(User.anonymous))); + + final participationOrFailure = await participationRepository + .getParticipation(crowdActionId: '123456'); + + expect(participationOrFailure.isLeft(), true); + }); + + test('getParticipation failure by exception', () async { + when(() => client.get(any(), headers: any(named: 'headers'))).thenAnswer( + (invocation) => Future.value(Response(participationRaw, 200))); + + when(() => authRepository.getSignedInUser()) + .thenAnswer((invocation) => throw Exception()); + + final participationOrFailure = await participationRepository + .getParticipation(crowdActionId: '123456'); + + expect(participationOrFailure.isLeft(), true); + }); + + test('toggleParticipation success', () async { + when(() => client.post(any(), + body: any(named: 'body'), headers: any(named: 'headers'))) + .thenAnswer((invocation) => Future.value(Response('', 200))); + + when(() => authRepository.getSignedInUser()) + .thenAnswer((invocation) => Future.value(optionOf(User.anonymous))); + + final participationOrFailure = await participationRepository + .toggleParticipation(crowdActionId: '123456'); + + expect(participationOrFailure.isRight(), true); + }); + + test('toggleParticipation failure', () async { + when(() => client.post(any(), + body: any(named: 'body'), headers: any(named: 'headers'))) + .thenAnswer((invocation) => Future.value(Response('', 400))); + + when(() => authRepository.getSignedInUser()) + .thenAnswer((invocation) => Future.value(optionOf(User.anonymous))); + + final participationOrFailure = await participationRepository + .toggleParticipation(crowdActionId: '123456'); + + expect(participationOrFailure.isLeft(), true); + }); + + test('toggleParticipation failure by exception', () async { + when(() => client.post(any(), + body: any(named: 'body'), headers: any(named: 'headers'))) + .thenAnswer((invocation) => Future.value(Response('', 200))); + + when(() => authRepository.getSignedInUser()) + .thenAnswer((invocation) => throw Exception()); + + final participationOrFailure = await participationRepository + .toggleParticipation(crowdActionId: '123456'); + + expect(participationOrFailure.isLeft(), true); + }); + + test('getParticipations success', () async { + when(() => client.get(any(), headers: any(named: 'headers'))).thenAnswer( + (invocation) => Future.value(Response(participationsRaw, 200))); + + final paginatedParticipationsOrFailure = await participationRepository + .getParticipations(crowdActionId: '123456'); + + expect(paginatedParticipationsOrFailure.isRight(), true); + + final paginatedParticipations = + paginatedParticipationsOrFailure.fold((l) => null, (r) => r); + expect(paginatedParticipations, isNotNull); + + expect(paginatedParticipations!.participations.length, 2); + }); + + test('getParticipations failure', () async { + when(() => client.get(any(), headers: any(named: 'headers'))) + .thenAnswer((invocation) => Future.value(Response('', 400))); + + final paginatedParticipationsOrFailure = await participationRepository + .getParticipations(crowdActionId: '123456'); + + expect(paginatedParticipationsOrFailure.isLeft(), true); + + final paginatedParticipations = + paginatedParticipationsOrFailure.fold((l) => null, (r) => r); + expect(paginatedParticipations, isNull); + }); + + test('getParticipations failure by exception', () async { + when(() => client.get(any(), headers: any(named: 'headers'))) + .thenAnswer((invocation) => throw Exception()); + + final paginatedParticipationsOrFailure = await participationRepository + .getParticipations(crowdActionId: '123456'); + + expect(paginatedParticipationsOrFailure.isLeft(), true); + + final paginatedParticipations = + paginatedParticipationsOrFailure.fold((l) => null, (r) => r); + expect(paginatedParticipations, isNull); + }); + + test('getTopParticipants success', () async { + when(() => client.get(any(), headers: any(named: 'headers'))).thenAnswer( + (invocation) => Future.value(Response(participationsRaw, 200))); + + final participationsOrFailure = await participationRepository + .getTopParticipants(crowdActionId: '123456'); + + expect(participationsOrFailure.isRight(), true); + + final participations = + participationsOrFailure.fold((l) => null, (r) => r); + expect(participations, isNotNull); + + expect(participations!.length, 2); + }); + + test('getTopParticipants failure', () async { + when(() => client.get(any(), headers: any(named: 'headers'))) + .thenAnswer((invocation) => Future.value(Response('', 400))); + + final participationsOrFailure = await participationRepository + .getTopParticipants(crowdActionId: '123456'); + + expect(participationsOrFailure.isLeft(), true); + + final participations = + participationsOrFailure.fold((l) => null, (r) => r); + expect(participations, isNull); + }); + + test('getTopParticipants failure by exception', () async { + when(() => client.get(any(), headers: any(named: 'headers'))) + .thenAnswer((invocation) => throw Exception()); + + final participationsOrFailure = await participationRepository + .getTopParticipants(crowdActionId: '123456'); + + expect(participationsOrFailure.isLeft(), true); + + final participations = + participationsOrFailure.fold((l) => null, (r) => r); + expect(participations, isNull); + }); + }); +} From bf7cb5dd98e8ef4f4a9a821f68b4d2ebdb36bf19 Mon Sep 17 00:00:00 2001 From: Sarah Audina Date: Mon, 13 Feb 2023 22:01:46 +0800 Subject: [PATCH 11/30] feat: updated authentication flow to force sign in --- lib/application/auth/auth_bloc.dart | 12 ++++++++++++ .../auth/unauthenticated_screen.dart | 18 ++++++++++++++++++ lib/presentation/core/app_widget.dart | 6 ++++++ lib/presentation/routes/app_routes.dart | 2 ++ 4 files changed, 38 insertions(+) create mode 100644 lib/presentation/auth/unauthenticated_screen.dart diff --git a/lib/application/auth/auth_bloc.dart b/lib/application/auth/auth_bloc.dart index 973fd1bf..85498ec8 100644 --- a/lib/application/auth/auth_bloc.dart +++ b/lib/application/auth/auth_bloc.dart @@ -26,11 +26,14 @@ class AuthBloc extends Bloc { String? _phone; StreamSubscription>? _verifyStreamSubscription; + StreamSubscription? _authenticationStateSubscription; AuthBloc( this._authRepository, this._avatarRepository, ) : super(const AuthState.initial()) { + _subscribeToAuthStateChanges(); + on( (event, emit) async { await event.map( @@ -51,6 +54,14 @@ class AuthBloc extends Bloc { ); } + Future _subscribeToAuthStateChanges() async { + _authenticationStateSubscription = _authRepository.observeUser().listen((event) { + if (event.isAnonymous) { + emit(const AuthState.unauthenticated()); + } + }); + } + FutureOr _mapResendCodeToState( Emitter emit, _ResendCode event, @@ -181,6 +192,7 @@ class AuthBloc extends Bloc { @override Future close() async { _verifyStreamSubscription?.cancel(); + _authenticationStateSubscription?.cancel(); super.close(); } } diff --git a/lib/presentation/auth/unauthenticated_screen.dart b/lib/presentation/auth/unauthenticated_screen.dart new file mode 100644 index 00000000..e75b67af --- /dev/null +++ b/lib/presentation/auth/unauthenticated_screen.dart @@ -0,0 +1,18 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; + +import '../routes/app_routes.gr.dart'; + +class UnauthenticatedPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: InkWell( + onTap: () => context.router.push(const AuthRoute()), + child: Text("You need to be authenticated to enter the app.\nLogin")), + ), + ); + } + +} \ No newline at end of file diff --git a/lib/presentation/core/app_widget.dart b/lib/presentation/core/app_widget.dart index 9b3d9cdb..959648ee 100644 --- a/lib/presentation/core/app_widget.dart +++ b/lib/presentation/core/app_widget.dart @@ -29,6 +29,12 @@ class AppWidget extends StatelessWidget { listener: (context, state) { BlocProvider.of(context).add(GetUserProfile()); BlocProvider.of(context).add(FetchProfileTabInfo()); + + state.whenOrNull( + unauthenticated: () { + _appRouter.replaceAll([const UnauthenticatedRoute()]); + }, + ); }, child: MaterialApp.router( color: Colors.white, diff --git a/lib/presentation/routes/app_routes.dart b/lib/presentation/routes/app_routes.dart index f852a68f..4bfe0835 100644 --- a/lib/presentation/routes/app_routes.dart +++ b/lib/presentation/routes/app_routes.dart @@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:auto_route/empty_router_widgets.dart'; import '../../../presentation/profile/profile_screen.dart'; +import '../auth/unauthenticated_screen.dart'; import '../auth/auth_screen.dart'; import '../auth/widgets/verified.dart'; import '../contact_form/contact_form_screen.dart'; @@ -74,6 +75,7 @@ import '../shared_widgets/web_view_page.dart'; AutoRoute(path: 'settings-layout', page: SettingsLayout), AutoRoute(path: 'contact-form', page: ContactFormPage), AutoRoute(path: 'webview', page: WebViewPage), + AutoRoute(path: 'unauthenticated', page: UnauthenticatedPage) ], ) class $AppRouter {} From dea823c061726b3f0c1e3498867aceb9e64a8411 Mon Sep 17 00:00:00 2001 From: Sarah Audina Date: Tue, 14 Feb 2023 15:58:54 +0800 Subject: [PATCH 12/30] feat: unauthenticated page ui implementation --- assets/images/logo.png | Bin 0 -> 52845 bytes assets/images/unauthenticated_bg.png | Bin 0 -> 7873 bytes .../auth/unauthenticated_screen.dart | 100 +++++++++++++++++- .../shared_widgets/pill_button.dart | 15 ++- 4 files changed, 105 insertions(+), 10 deletions(-) create mode 100644 assets/images/logo.png create mode 100644 assets/images/unauthenticated_bg.png diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..92bdb93dd3e230481810cb66a258507464f41e91 GIT binary patch literal 52845 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 Sfuo%vkh^L+f8?oJg#3S9`L(?O literal 0 HcmV?d00001 diff --git a/assets/images/unauthenticated_bg.png b/assets/images/unauthenticated_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..fb8b1b6375a912f618167a73bb716081d78f5ea1 GIT binary patch literal 7873 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 L_dPXIwfcVm5OfkA literal 0 HcmV?d00001 diff --git a/lib/presentation/auth/unauthenticated_screen.dart b/lib/presentation/auth/unauthenticated_screen.dart index e75b67af..de79d8b8 100644 --- a/lib/presentation/auth/unauthenticated_screen.dart +++ b/lib/presentation/auth/unauthenticated_screen.dart @@ -2,17 +2,107 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import '../routes/app_routes.gr.dart'; +import '../shared_widgets/pill_button.dart'; +import '../themes/constants.dart'; +import '../../domain/core/i_settings_repository.dart'; +import '../../infrastructure/core/injection.dart'; + +class UnauthenticatedPage extends StatefulWidget { + @override + State createState() { + return UnauthenticatedPageState(); + } +} + +class UnauthenticatedPageState extends State { + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + checkAndMaybeShowOnboarding(); + }); + } -class UnauthenticatedPage extends StatelessWidget { @override Widget build(BuildContext context) { + print('in UnauthenticatedPage'); return Scaffold( - body: Center( - child: InkWell( - onTap: () => context.router.push(const AuthRoute()), - child: Text("You need to be authenticated to enter the app.\nLogin")), + backgroundColor: kAccentColor, + body: Stack( + children: [ + Container( + height: MediaQuery.of(context).size.height*.8, + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + top: 0, + right: 0, + left: 23, + child: Image.asset("assets/images/unauthenticated_bg.png", + fit: BoxFit.contain, + ), + ), + Align( + alignment: Alignment.center, + child: Image.asset("assets/images/logo.png", width: 200,)) + ], + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text("Start acting", style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 28.0, + color: Colors.white, + ),), + Padding( + padding: const EdgeInsets.only(bottom: 64.0, left: 16, right: 16), + child: Text( + "We are ready, join us and others in taking action for a better life, by doing good and having fun!", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + ) + ), + ), + Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.only(bottom: 100.0), + child: PillButton( + text: 'Log In', + isEnabled: true, + isLoading: false, + onTap: () => context.router.push(const AuthRoute()), + darkText: true, + ), + ), + ), + ], + ), + ], + ), + ), + ), + ], ), ); } + Future checkAndMaybeShowOnboarding() async { + // Push onboarding screen if first time launching application + final settingsRepository = getIt(); + if (!(await settingsRepository.getWasUserOnboarded())) { + context.router.push(const OnboardingRoute()); + } + } } \ No newline at end of file diff --git a/lib/presentation/shared_widgets/pill_button.dart b/lib/presentation/shared_widgets/pill_button.dart index 55206920..0483442d 100644 --- a/lib/presentation/shared_widgets/pill_button.dart +++ b/lib/presentation/shared_widgets/pill_button.dart @@ -10,6 +10,7 @@ class PillButton extends StatelessWidget { final EdgeInsets? margin; final double? width; final bool isLoading; + final bool darkText; const PillButton({ super.key, @@ -20,6 +21,7 @@ class PillButton extends StatelessWidget { this.margin, this.width, this.isLoading = false, + this.darkText = false }); const PillButton.icon({ @@ -31,6 +33,7 @@ class PillButton extends StatelessWidget { this.margin, this.width, this.isLoading = false, + this.darkText = false }); @override @@ -48,18 +51,20 @@ class PillButton extends StatelessWidget { ? ElevatedButton( onPressed: () {}, style: ElevatedButton.styleFrom( - backgroundColor: kAccentColor, + backgroundColor: darkText ? Colors.white : kAccentColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(52), ), ), - child: const CircularProgressIndicator( - color: Colors.white, + child: CircularProgressIndicator( + color: darkText ? kAccentColor : Colors.white, ), ) : ElevatedButton.icon( icon: leading ?? const SizedBox(), - label: Text(text), + label: Text(text, style: TextStyle( + color: darkText ? kAccentColor : Colors.white, + )), onPressed: isEnabled ? onTap : null, style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith( @@ -67,7 +72,7 @@ class PillButton extends StatelessWidget { if (states.contains(MaterialState.disabled)) { return kAlmostTransparent; } - return kAccentColor; + return darkText ? Colors.white : kAccentColor; }, ), elevation: MaterialStateProperty.all(0), From 275baa0593706252bb022c6e2c7aa227160e5874 Mon Sep 17 00:00:00 2001 From: Sarah Audina Date: Tue, 14 Feb 2023 17:10:08 +0800 Subject: [PATCH 13/30] feat: change user subject's default value to null --- lib/application/auth/auth_bloc.dart | 2 +- lib/domain/auth/i_auth_repository.dart | 2 +- lib/infrastructure/auth/firebase_auth_repository.dart | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/application/auth/auth_bloc.dart b/lib/application/auth/auth_bloc.dart index 85498ec8..1674b7da 100644 --- a/lib/application/auth/auth_bloc.dart +++ b/lib/application/auth/auth_bloc.dart @@ -56,7 +56,7 @@ class AuthBloc extends Bloc { Future _subscribeToAuthStateChanges() async { _authenticationStateSubscription = _authRepository.observeUser().listen((event) { - if (event.isAnonymous) { + if (event is User && event!.isAnonymous) { emit(const AuthState.unauthenticated()); } }); diff --git a/lib/domain/auth/i_auth_repository.dart b/lib/domain/auth/i_auth_repository.dart index 3e48b3f4..b345fd49 100644 --- a/lib/domain/auth/i_auth_repository.dart +++ b/lib/domain/auth/i_auth_repository.dart @@ -6,7 +6,7 @@ import 'auth_failures.dart'; import 'auth_success.dart'; abstract class IAuthRepository { - Stream observeUser(); + Stream observeUser(); /// TODO - Choose to either observe or getSignedInUser /// Returns [User] if already authenticated diff --git a/lib/infrastructure/auth/firebase_auth_repository.dart b/lib/infrastructure/auth/firebase_auth_repository.dart index 919cd437..0ec66fc7 100644 --- a/lib/infrastructure/auth/firebase_auth_repository.dart +++ b/lib/infrastructure/auth/firebase_auth_repository.dart @@ -17,7 +17,7 @@ import 'firebase_auth_mapper.dart'; @LazySingleton(as: IAuthRepository) class FirebaseAuthRepository implements IAuthRepository, Disposable { final firebase_auth.FirebaseAuth firebaseAuth; - final _userSubject = BehaviorSubject.seeded(User.anonymous); + final _userSubject = BehaviorSubject(); late final StreamSubscription _userStreamSubscription; FirebaseAuthRepository({required this.firebaseAuth}) { @@ -210,7 +210,7 @@ class FirebaseAuthRepository implements IAuthRepository, Disposable { } @override - Stream observeUser() => _userSubject.stream.distinct(); + Stream observeUser() => _userSubject.stream.distinct(); @override FutureOr onDispose() { From 49cdd857462638cb6b56bc20c714706529f0890c Mon Sep 17 00:00:00 2001 From: Sarah Audina Date: Tue, 14 Feb 2023 21:39:27 +0800 Subject: [PATCH 14/30] updated test for auth bloc mocking observeUser --- test/application/auth/auth_bloc_test.dart | 27 +++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/test/application/auth/auth_bloc_test.dart b/test/application/auth/auth_bloc_test.dart index 9bd1e4b0..2a5dbb1a 100644 --- a/test/application/auth/auth_bloc_test.dart +++ b/test/application/auth/auth_bloc_test.dart @@ -5,6 +5,7 @@ import 'package:collaction_app/domain/auth/auth_success.dart'; import 'package:collaction_app/domain/auth/i_auth_repository.dart'; import 'package:collaction_app/domain/user/i_avatar_repository.dart'; import 'package:collaction_app/domain/user/i_user_repository.dart'; +import 'package:collaction_app/domain/user/user.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; @@ -15,8 +16,17 @@ class MockAvatarRepository extends Mock implements IAvatarRepository {} void main() { group('Authentication BLoC', () { + test('Initial auth state', () { - final bloc = AuthBloc(MockAuthRepository(), MockAvatarRepository()); + final userRepository = MockAuthRepository(); + + when( + () => userRepository.observeUser() + ).thenAnswer( + (_) => Stream.empty() + ); + + final bloc = AuthBloc(userRepository, MockAvatarRepository()); expect(bloc.state, const AuthState.initial()); }); @@ -31,6 +41,12 @@ void main() { final avatarRepository = MockAvatarRepository(); { + when( + () => userRepository.observeUser() + ).thenAnswer( + (_) => Stream.empty() + ); + when( () => userRepository.verifyPhone( phoneNumber: any(named: 'phoneNumber'), @@ -84,6 +100,13 @@ void main() { { final userRepository = MockAuthRepository(); + + when( + () => userRepository.observeUser() + ).thenAnswer( + (_) => Stream.empty() + ); + const error = AuthFailure.verificationFailed(); when( () => userRepository.verifyPhone( @@ -106,7 +129,7 @@ void main() { blocTest( 'Reset to initial auth state', - build: () => AuthBloc(MockAuthRepository(), MockAvatarRepository()), + build: () => AuthBloc(userRepository, MockAvatarRepository()), act: (AuthBloc bloc) { bloc.add(const AuthEvent.reset()); }, From 9b5c788f7f59f51ca5b8d24a12a13245c57f477d Mon Sep 17 00:00:00 2001 From: Sarah Audina Date: Tue, 14 Feb 2023 22:04:41 +0800 Subject: [PATCH 15/30] clean: removed unused codes --- .../auth/unauthenticated_screen.dart | 8 +++--- lib/presentation/home/home_screen.dart | 27 +------------------ .../shared_widgets/pill_button.dart | 14 +++++----- 3 files changed, 11 insertions(+), 38 deletions(-) diff --git a/lib/presentation/auth/unauthenticated_screen.dart b/lib/presentation/auth/unauthenticated_screen.dart index de79d8b8..1eaf7a75 100644 --- a/lib/presentation/auth/unauthenticated_screen.dart +++ b/lib/presentation/auth/unauthenticated_screen.dart @@ -15,7 +15,6 @@ class UnauthenticatedPage extends StatefulWidget { } class UnauthenticatedPageState extends State { - @override void initState() { super.initState(); @@ -26,7 +25,6 @@ class UnauthenticatedPageState extends State { @override Widget build(BuildContext context) { - print('in UnauthenticatedPage'); return Scaffold( backgroundColor: kAccentColor, body: Stack( @@ -64,7 +62,7 @@ class UnauthenticatedPageState extends State { color: Colors.white, ),), Padding( - padding: const EdgeInsets.only(bottom: 64.0, left: 16, right: 16), + padding: const EdgeInsets.only(bottom: 64.0, left: 16, right: 16, top: 8), child: Text( "We are ready, join us and others in taking action for a better life, by doing good and having fun!", textAlign: TextAlign.center, @@ -77,13 +75,13 @@ class UnauthenticatedPageState extends State { children: [ Expanded( child: Padding( - padding: const EdgeInsets.only(bottom: 100.0), + padding: const EdgeInsets.only(bottom: 100.0, left: 20, right: 20), child: PillButton( text: 'Log In', isEnabled: true, isLoading: false, onTap: () => context.router.push(const AuthRoute()), - darkText: true, + lightBackground: true, ), ), ), diff --git a/lib/presentation/home/home_screen.dart b/lib/presentation/home/home_screen.dart index eba9de7f..9c04554c 100644 --- a/lib/presentation/home/home_screen.dart +++ b/lib/presentation/home/home_screen.dart @@ -3,27 +3,10 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import '../../../presentation/themes/constants.dart'; -import '../../domain/core/i_settings_repository.dart'; -import '../../infrastructure/core/injection.dart'; import '../core/collaction_icons.dart'; import '../routes/app_routes.gr.dart'; -class HomePage extends StatefulWidget { - const HomePage({super.key}); - - @override - HomePageState createState() => HomePageState(); -} - -class HomePageState extends State { - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - checkAndMaybeShowOnboarding(); - }); - } - +class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return AutoTabsScaffold( @@ -38,14 +21,6 @@ class HomePageState extends State { ); } - Future checkAndMaybeShowOnboarding() async { - // Push onboarding screen if first time launching application - final settingsRepository = getIt(); - if (!(await settingsRepository.getWasUserOnboarded())) { - context.router.push(const OnboardingRoute()); - } - } - Widget bottomNavbar(TabsRouter tabsRouter) { return BottomNavigationBar( backgroundColor: Colors.white, diff --git a/lib/presentation/shared_widgets/pill_button.dart b/lib/presentation/shared_widgets/pill_button.dart index 0483442d..97df75fd 100644 --- a/lib/presentation/shared_widgets/pill_button.dart +++ b/lib/presentation/shared_widgets/pill_button.dart @@ -10,7 +10,7 @@ class PillButton extends StatelessWidget { final EdgeInsets? margin; final double? width; final bool isLoading; - final bool darkText; + final bool lightBackground; const PillButton({ super.key, @@ -21,7 +21,7 @@ class PillButton extends StatelessWidget { this.margin, this.width, this.isLoading = false, - this.darkText = false + this.lightBackground = false }); const PillButton.icon({ @@ -33,7 +33,7 @@ class PillButton extends StatelessWidget { this.margin, this.width, this.isLoading = false, - this.darkText = false + this.lightBackground = false }); @override @@ -51,19 +51,19 @@ class PillButton extends StatelessWidget { ? ElevatedButton( onPressed: () {}, style: ElevatedButton.styleFrom( - backgroundColor: darkText ? Colors.white : kAccentColor, + backgroundColor: lightBackground ? Colors.white : kAccentColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(52), ), ), child: CircularProgressIndicator( - color: darkText ? kAccentColor : Colors.white, + color: lightBackground ? kAccentColor : Colors.white, ), ) : ElevatedButton.icon( icon: leading ?? const SizedBox(), label: Text(text, style: TextStyle( - color: darkText ? kAccentColor : Colors.white, + color: lightBackground ? kAccentColor : Colors.white, )), onPressed: isEnabled ? onTap : null, style: ButtonStyle( @@ -72,7 +72,7 @@ class PillButton extends StatelessWidget { if (states.contains(MaterialState.disabled)) { return kAlmostTransparent; } - return darkText ? Colors.white : kAccentColor; + return lightBackground ? Colors.white : kAccentColor; }, ), elevation: MaterialStateProperty.all(0), From c05c926189cd5b317b38f22f117df88b54c6320c Mon Sep 17 00:00:00 2001 From: Sarah Audina Date: Fri, 17 Feb 2023 20:00:58 +0800 Subject: [PATCH 16/30] fix: added trailing commas for formatting --- lib/application/auth/auth_bloc.dart | 3 +- .../auth/unauthenticated_screen.dart | 34 +++++++----- .../shared_widgets/pill_button.dart | 52 +++++++++---------- test/application/auth/auth_bloc_test.dart | 22 +++----- 4 files changed, 56 insertions(+), 55 deletions(-) diff --git a/lib/application/auth/auth_bloc.dart b/lib/application/auth/auth_bloc.dart index 1674b7da..1f781435 100644 --- a/lib/application/auth/auth_bloc.dart +++ b/lib/application/auth/auth_bloc.dart @@ -55,7 +55,8 @@ class AuthBloc extends Bloc { } Future _subscribeToAuthStateChanges() async { - _authenticationStateSubscription = _authRepository.observeUser().listen((event) { + _authenticationStateSubscription = + _authRepository.observeUser().listen((event) { if (event is User && event!.isAnonymous) { emit(const AuthState.unauthenticated()); } diff --git a/lib/presentation/auth/unauthenticated_screen.dart b/lib/presentation/auth/unauthenticated_screen.dart index 1eaf7a75..2341a5c1 100644 --- a/lib/presentation/auth/unauthenticated_screen.dart +++ b/lib/presentation/auth/unauthenticated_screen.dart @@ -30,7 +30,7 @@ class UnauthenticatedPageState extends State { body: Stack( children: [ Container( - height: MediaQuery.of(context).size.height*.8, + height: MediaQuery.of(context).size.height * .8, child: Stack( alignment: Alignment.center, children: [ @@ -38,13 +38,18 @@ class UnauthenticatedPageState extends State { top: 0, right: 0, left: 23, - child: Image.asset("assets/images/unauthenticated_bg.png", + child: Image.asset( + "assets/images/unauthenticated_bg.png", fit: BoxFit.contain, ), ), Align( alignment: Alignment.center, - child: Image.asset("assets/images/logo.png", width: 200,)) + child: Image.asset( + "assets/images/logo.png", + width: 200, + ), + ), ], ), ), @@ -56,26 +61,31 @@ class UnauthenticatedPageState extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text("Start acting", style: TextStyle( - fontWeight: FontWeight.w700, - fontSize: 28.0, - color: Colors.white, - ),), + Text( + "Start acting", + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 28.0, + color: Colors.white, + ), + ), Padding( - padding: const EdgeInsets.only(bottom: 64.0, left: 16, right: 16, top: 8), + padding: const EdgeInsets.only( + bottom: 64.0, left: 16, right: 16, top: 8), child: Text( "We are ready, join us and others in taking action for a better life, by doing good and having fun!", textAlign: TextAlign.center, style: TextStyle( color: Colors.white, - ) + ), ), ), Row( children: [ Expanded( child: Padding( - padding: const EdgeInsets.only(bottom: 100.0, left: 20, right: 20), + padding: const EdgeInsets.only( + bottom: 100.0, left: 20, right: 20), child: PillButton( text: 'Log In', isEnabled: true, @@ -103,4 +113,4 @@ class UnauthenticatedPageState extends State { context.router.push(const OnboardingRoute()); } } -} \ No newline at end of file +} diff --git a/lib/presentation/shared_widgets/pill_button.dart b/lib/presentation/shared_widgets/pill_button.dart index 97df75fd..5a8b1b8e 100644 --- a/lib/presentation/shared_widgets/pill_button.dart +++ b/lib/presentation/shared_widgets/pill_button.dart @@ -12,29 +12,27 @@ class PillButton extends StatelessWidget { final bool isLoading; final bool lightBackground; - const PillButton({ - super.key, - required this.text, - this.leading, - this.onTap, - this.isEnabled = true, - this.margin, - this.width, - this.isLoading = false, - this.lightBackground = false - }); + const PillButton( + {super.key, + required this.text, + this.leading, + this.onTap, + this.isEnabled = true, + this.margin, + this.width, + this.isLoading = false, + this.lightBackground = false}); - const PillButton.icon({ - super.key, - required this.text, - required this.leading, - this.onTap, - this.isEnabled = true, - this.margin, - this.width, - this.isLoading = false, - this.lightBackground = false - }); + const PillButton.icon( + {super.key, + required this.text, + required this.leading, + this.onTap, + this.isEnabled = true, + this.margin, + this.width, + this.isLoading = false, + this.lightBackground = false}); @override Widget build(BuildContext context) { @@ -51,7 +49,8 @@ class PillButton extends StatelessWidget { ? ElevatedButton( onPressed: () {}, style: ElevatedButton.styleFrom( - backgroundColor: lightBackground ? Colors.white : kAccentColor, + backgroundColor: + lightBackground ? Colors.white : kAccentColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(52), ), @@ -62,9 +61,10 @@ class PillButton extends StatelessWidget { ) : ElevatedButton.icon( icon: leading ?? const SizedBox(), - label: Text(text, style: TextStyle( - color: lightBackground ? kAccentColor : Colors.white, - )), + label: Text(text, + style: TextStyle( + color: lightBackground ? kAccentColor : Colors.white, + )), onPressed: isEnabled ? onTap : null, style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith( diff --git a/test/application/auth/auth_bloc_test.dart b/test/application/auth/auth_bloc_test.dart index 2a5dbb1a..7dac9eed 100644 --- a/test/application/auth/auth_bloc_test.dart +++ b/test/application/auth/auth_bloc_test.dart @@ -16,15 +16,11 @@ class MockAvatarRepository extends Mock implements IAvatarRepository {} void main() { group('Authentication BLoC', () { - test('Initial auth state', () { final userRepository = MockAuthRepository(); - when( - () => userRepository.observeUser() - ).thenAnswer( - (_) => Stream.empty() - ); + when(() => userRepository.observeUser()) + .thenAnswer((_) => Stream.empty()); final bloc = AuthBloc(userRepository, MockAvatarRepository()); expect(bloc.state, const AuthState.initial()); @@ -41,11 +37,8 @@ void main() { final avatarRepository = MockAvatarRepository(); { - when( - () => userRepository.observeUser() - ).thenAnswer( - (_) => Stream.empty() - ); + when(() => userRepository.observeUser()) + .thenAnswer((_) => Stream.empty()); when( () => userRepository.verifyPhone( @@ -101,11 +94,8 @@ void main() { { final userRepository = MockAuthRepository(); - when( - () => userRepository.observeUser() - ).thenAnswer( - (_) => Stream.empty() - ); + when(() => userRepository.observeUser()) + .thenAnswer((_) => Stream.empty()); const error = AuthFailure.verificationFailed(); when( From 0c33e4cb4356cda4f945efab827c56faf773457b Mon Sep 17 00:00:00 2001 From: Sarah Audina Date: Fri, 17 Feb 2023 20:35:42 +0800 Subject: [PATCH 17/30] feat: added auth event to subscribe to user state changes --- lib/application/auth/auth_bloc.dart | 10 ++++++---- lib/application/auth/auth_event.dart | 2 ++ lib/presentation/core/app_widget.dart | 2 +- test/application/auth/auth_bloc_test.dart | 1 - 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/application/auth/auth_bloc.dart b/lib/application/auth/auth_bloc.dart index 1f781435..4439c6c7 100644 --- a/lib/application/auth/auth_bloc.dart +++ b/lib/application/auth/auth_bloc.dart @@ -32,11 +32,11 @@ class AuthBloc extends Bloc { this._authRepository, this._avatarRepository, ) : super(const AuthState.initial()) { - _subscribeToAuthStateChanges(); - on( (event, emit) async { await event.map( + observeUser: (event) async => + await _mapObserveUserToState(emit, event), verifyPhone: (event) async => await _mapVerifyPhoneToState(emit, event), signInWithPhone: (event) async => @@ -54,10 +54,12 @@ class AuthBloc extends Bloc { ); } - Future _subscribeToAuthStateChanges() async { + Future _mapObserveUserToState( + Emitter emit, + _ObserveUser event,) async { _authenticationStateSubscription = _authRepository.observeUser().listen((event) { - if (event is User && event!.isAnonymous) { + if (event is User && event.isAnonymous) { emit(const AuthState.unauthenticated()); } }); diff --git a/lib/application/auth/auth_event.dart b/lib/application/auth/auth_event.dart index 6b11b16f..5cdb11d9 100644 --- a/lib/application/auth/auth_event.dart +++ b/lib/application/auth/auth_event.dart @@ -2,6 +2,8 @@ part of 'auth_bloc.dart'; @freezed class AuthEvent with _$AuthEvent { + const factory AuthEvent.observeUser() = _ObserveUser; + const factory AuthEvent.verifyPhone(String phoneNumber) = _VerifyPhone; const factory AuthEvent.updated( diff --git a/lib/presentation/core/app_widget.dart b/lib/presentation/core/app_widget.dart index 959648ee..44024c78 100644 --- a/lib/presentation/core/app_widget.dart +++ b/lib/presentation/core/app_widget.dart @@ -16,7 +16,7 @@ class AppWidget extends StatelessWidget { return MultiBlocProvider( providers: [ BlocProvider( - create: (_) => getIt(), + create: (_) => getIt()..add(AuthEvent.observeUser()), ), BlocProvider( create: (_) => getIt()..add(GetUserProfile()), diff --git a/test/application/auth/auth_bloc_test.dart b/test/application/auth/auth_bloc_test.dart index 7dac9eed..d81e2830 100644 --- a/test/application/auth/auth_bloc_test.dart +++ b/test/application/auth/auth_bloc_test.dart @@ -5,7 +5,6 @@ import 'package:collaction_app/domain/auth/auth_success.dart'; import 'package:collaction_app/domain/auth/i_auth_repository.dart'; import 'package:collaction_app/domain/user/i_avatar_repository.dart'; import 'package:collaction_app/domain/user/i_user_repository.dart'; -import 'package:collaction_app/domain/user/user.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; From 96d9d003840f6166dbf686526f040b4409c67db0 Mon Sep 17 00:00:00 2001 From: Sarah Audina Date: Fri, 17 Feb 2023 20:50:13 +0800 Subject: [PATCH 18/30] fix: formatting --- lib/application/auth/auth_bloc.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/application/auth/auth_bloc.dart b/lib/application/auth/auth_bloc.dart index 4439c6c7..b9dd2749 100644 --- a/lib/application/auth/auth_bloc.dart +++ b/lib/application/auth/auth_bloc.dart @@ -36,7 +36,7 @@ class AuthBloc extends Bloc { (event, emit) async { await event.map( observeUser: (event) async => - await _mapObserveUserToState(emit, event), + await _mapObserveUserToState(emit, event), verifyPhone: (event) async => await _mapVerifyPhoneToState(emit, event), signInWithPhone: (event) async => @@ -55,8 +55,9 @@ class AuthBloc extends Bloc { } Future _mapObserveUserToState( - Emitter emit, - _ObserveUser event,) async { + Emitter emit, + _ObserveUser event, + ) async { _authenticationStateSubscription = _authRepository.observeUser().listen((event) { if (event is User && event.isAnonymous) { From 53b5d9cd4916c20734e73d06069dfe4cffd66c5f Mon Sep 17 00:00:00 2001 From: Sarah Audina Date: Fri, 17 Feb 2023 21:11:32 +0800 Subject: [PATCH 19/30] test: updated home screen test case --- test/presentation/home/home_screen_test.dart | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/test/presentation/home/home_screen_test.dart b/test/presentation/home/home_screen_test.dart index 408015c4..8bc9799f 100644 --- a/test/presentation/home/home_screen_test.dart +++ b/test/presentation/home/home_screen_test.dart @@ -60,22 +60,4 @@ void main() { expect(find.byType(HomePage), findsOneWidget); }); - - testWidgets('show onboarding', (tester) async { - when(() => settingsRepository.getWasUserOnboarded()) - .thenAnswer((_) => Future.value(false)); - - await buildAndPump( - tester: tester, - widget: MaterialApp.router( - color: Colors.white, - title: 'CollAction', - routerDelegate: appRouter.delegate(), - routeInformationParser: appRouter.defaultRouteParser(), - ), - ); - await tester.pumpAndSettle(); - - expect(find.byType(OnboardingPage), findsOneWidget); - }); } From ef09e255002f1fbd4295deb9831aea8650b6c477 Mon Sep 17 00:00:00 2001 From: Sarah Audina Date: Fri, 17 Feb 2023 21:21:06 +0800 Subject: [PATCH 20/30] clean: removed unused import --- test/presentation/home/home_screen_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/test/presentation/home/home_screen_test.dart b/test/presentation/home/home_screen_test.dart index 8bc9799f..6f8b6cdf 100644 --- a/test/presentation/home/home_screen_test.dart +++ b/test/presentation/home/home_screen_test.dart @@ -2,7 +2,6 @@ import 'package:collaction_app/application/crowdaction/spotlight/spotlight_bloc. import 'package:collaction_app/domain/core/i_settings_repository.dart'; import 'package:collaction_app/domain/crowdaction/crowdaction_failures.dart'; import 'package:collaction_app/presentation/home/home_screen.dart'; -import 'package:collaction_app/presentation/onboarding/onboarding_screen.dart'; import 'package:collaction_app/presentation/routes/app_routes.gr.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; From 2b68a14e06b0285c55b8ac0a7645480200f12b55 Mon Sep 17 00:00:00 2001 From: Sarah Audina Date: Fri, 24 Feb 2023 17:18:21 +0800 Subject: [PATCH 21/30] fix: stateful to stateless, changed event name --- lib/application/auth/auth_bloc.dart | 5 ++--- lib/application/auth/auth_event.dart | 2 +- .../auth/unauthenticated_screen.dart | 19 ++++--------------- lib/presentation/core/app_widget.dart | 2 +- 4 files changed, 8 insertions(+), 20 deletions(-) diff --git a/lib/application/auth/auth_bloc.dart b/lib/application/auth/auth_bloc.dart index b9dd2749..fb746907 100644 --- a/lib/application/auth/auth_bloc.dart +++ b/lib/application/auth/auth_bloc.dart @@ -35,8 +35,7 @@ class AuthBloc extends Bloc { on( (event, emit) async { await event.map( - observeUser: (event) async => - await _mapObserveUserToState(emit, event), + initial: (event) async => await _mapObserveUserToState(emit, event), verifyPhone: (event) async => await _mapVerifyPhoneToState(emit, event), signInWithPhone: (event) async => @@ -56,7 +55,7 @@ class AuthBloc extends Bloc { Future _mapObserveUserToState( Emitter emit, - _ObserveUser event, + _InitialEvent event, ) async { _authenticationStateSubscription = _authRepository.observeUser().listen((event) { diff --git a/lib/application/auth/auth_event.dart b/lib/application/auth/auth_event.dart index 5cdb11d9..ed9a3f2a 100644 --- a/lib/application/auth/auth_event.dart +++ b/lib/application/auth/auth_event.dart @@ -2,7 +2,7 @@ part of 'auth_bloc.dart'; @freezed class AuthEvent with _$AuthEvent { - const factory AuthEvent.observeUser() = _ObserveUser; + const factory AuthEvent.initial() = _InitialEvent; const factory AuthEvent.verifyPhone(String phoneNumber) = _VerifyPhone; diff --git a/lib/presentation/auth/unauthenticated_screen.dart b/lib/presentation/auth/unauthenticated_screen.dart index 2341a5c1..0620981e 100644 --- a/lib/presentation/auth/unauthenticated_screen.dart +++ b/lib/presentation/auth/unauthenticated_screen.dart @@ -7,24 +7,13 @@ import '../themes/constants.dart'; import '../../domain/core/i_settings_repository.dart'; import '../../infrastructure/core/injection.dart'; -class UnauthenticatedPage extends StatefulWidget { +class UnauthenticatedPage extends StatelessWidget { @override - State createState() { - return UnauthenticatedPageState(); - } -} - -class UnauthenticatedPageState extends State { - @override - void initState() { - super.initState(); + Widget build(BuildContext context) { WidgetsBinding.instance.addPostFrameCallback((_) { - checkAndMaybeShowOnboarding(); + checkAndMaybeShowOnboarding(context); }); - } - @override - Widget build(BuildContext context) { return Scaffold( backgroundColor: kAccentColor, body: Stack( @@ -106,7 +95,7 @@ class UnauthenticatedPageState extends State { ); } - Future checkAndMaybeShowOnboarding() async { + Future checkAndMaybeShowOnboarding(BuildContext context) async { // Push onboarding screen if first time launching application final settingsRepository = getIt(); if (!(await settingsRepository.getWasUserOnboarded())) { diff --git a/lib/presentation/core/app_widget.dart b/lib/presentation/core/app_widget.dart index 44024c78..db9b4925 100644 --- a/lib/presentation/core/app_widget.dart +++ b/lib/presentation/core/app_widget.dart @@ -16,7 +16,7 @@ class AppWidget extends StatelessWidget { return MultiBlocProvider( providers: [ BlocProvider( - create: (_) => getIt()..add(AuthEvent.observeUser()), + create: (_) => getIt()..add(AuthEvent.initial()), ), BlocProvider( create: (_) => getIt()..add(GetUserProfile()), From 6cdb5b862d0c77446a8abbf7ad1bd755a10690a1 Mon Sep 17 00:00:00 2001 From: Mathias Mogensencd collaction_website Date: Tue, 28 Feb 2023 16:39:31 +0100 Subject: [PATCH 22/30] refactor: commitmentoptions to commitments --- .../participation/participation_bloc.dart | 4 +- .../participation/participation_event.dart | 2 +- lib/domain/crowdaction/crowdaction.dart | 12 +- .../i_participation_repository.dart | 2 +- lib/domain/participation/participation.dart | 2 +- .../crowdaction/crowdaction_dto.dart | 28 +-- .../participation/participation_dto.dart | 4 +- .../participation_repository.dart | 4 +- .../crowdaction_details_screen.dart | 9 +- .../widgets/confirm_participation.dart | 7 +- .../components_demo_screen.dart | 18 +- .../profile/widget/commitments_tab.dart | 2 +- .../shared_widgets/commitment_card.dart | 3 - .../commitments/commitment_card.dart | 6 +- .../commitments/commitment_card_list.dart | 18 +- pubspec.lock | 210 +++++++++--------- pubspec.yaml | 10 - .../crowdaction_test_fixtures.dart | 5 +- .../commitment_option_dto_fixtures.dart | 11 +- .../commitment_option_dto_test.dart | 12 +- .../crowdaction/crowdaction_dto_fixtures.dart | 8 +- .../crowdaction_repository_fixtures.dart | 27 +-- .../participation_dto_fixtures.dart | 6 +- .../participation_repository_fixtures.dart | 6 +- .../confirm_participation_test.dart | 8 +- .../confirm_participation_test.ext.dart | 2 +- .../profile/commitments_tab_fixtures.dart | 6 +- .../commitment_card_fixtures.dart | 3 +- .../commitment_card_list_test.dart | 33 ++- .../commitment_card_list_test.ext.dart | 6 +- .../shared_widgets/commitment_card_test.dart | 10 +- test/test_utilities.dart | 19 +- test/utils/crowdaction.fixtures.dart | 3 +- test/utils/crowdactions.dart | 48 ++-- test/utils/participation.fixtures.dart | 2 +- 35 files changed, 243 insertions(+), 313 deletions(-) diff --git a/lib/application/participation/participation_bloc.dart b/lib/application/participation/participation_bloc.dart index 6716d617..c9befcf7 100644 --- a/lib/application/participation/participation_bloc.dart +++ b/lib/application/participation/participation_bloc.dart @@ -31,12 +31,12 @@ class ParticipationBloc extends Bloc { checkIn: () { // TODO: Implement once the backend has implemented it }, - toggleParticipation: (crowdActionId, commitmentOptions) async { + toggleParticipation: (crowdActionId, commitments) async { emit(const ParticipationState.loading()); await participationRepository.toggleParticipation( crowdActionId: crowdActionId, - commitmentOptions: commitmentOptions, + commitments: commitments, ); add( diff --git a/lib/application/participation/participation_event.dart b/lib/application/participation/participation_event.dart index a2306840..587341b2 100644 --- a/lib/application/participation/participation_event.dart +++ b/lib/application/participation/participation_event.dart @@ -8,6 +8,6 @@ class ParticipationEvent with _$ParticipationEvent { const factory ParticipationEvent.checkIn() = _CheckIn; const factory ParticipationEvent.toggleParticipation({ required String crowdActionId, - List? commitmentOptions, + List? commitments, }) = _ToggleParticipation; } diff --git a/lib/domain/crowdaction/crowdaction.dart b/lib/domain/crowdaction/crowdaction.dart index 08a25339..bb75439c 100644 --- a/lib/domain/crowdaction/crowdaction.dart +++ b/lib/domain/crowdaction/crowdaction.dart @@ -14,12 +14,11 @@ class CrowdAction with _$CrowdAction { const factory CrowdAction({ required String id, - required String type, required String title, required String description, required String category, required Location location, - required List commitmentOptions, + required List commitments, required Images images, required int participantCount, required Status status, @@ -93,18 +92,17 @@ enum JoinStatus { } @freezed -class CommitmentOption with _$CommitmentOption { - const CommitmentOption._(); +class Commitment with _$Commitment { + const Commitment._(); - factory CommitmentOption({ + factory Commitment({ required String id, - required String type, required String label, required int points, required List blocks, String? description, String? iconId, - }) = _CommitmentOption; + }) = _Commitment; IconData get icon => iconId != null ? IconUtil.fromString(iconId!) diff --git a/lib/domain/participation/i_participation_repository.dart b/lib/domain/participation/i_participation_repository.dart index 23e7dea4..1e8280d9 100644 --- a/lib/domain/participation/i_participation_repository.dart +++ b/lib/domain/participation/i_participation_repository.dart @@ -10,7 +10,7 @@ abstract class IParticipationRepository { Future> toggleParticipation({ required String crowdActionId, - List? commitmentOptions, + List? commitments, }); Future> diff --git a/lib/domain/participation/participation.dart b/lib/domain/participation/participation.dart index 26a55751..c0f88547 100644 --- a/lib/domain/participation/participation.dart +++ b/lib/domain/participation/participation.dart @@ -14,7 +14,7 @@ class Participation with _$Participation { required String fullName, required String avatar, required String userId, - required List commitmentOptions, + required List commitments, required DateTime joinDate, required int dailyCheckIns, }) = _Participation; diff --git a/lib/infrastructure/crowdaction/crowdaction_dto.dart b/lib/infrastructure/crowdaction/crowdaction_dto.dart index f97dcf17..d3f53167 100644 --- a/lib/infrastructure/crowdaction/crowdaction_dto.dart +++ b/lib/infrastructure/crowdaction/crowdaction_dto.dart @@ -14,12 +14,11 @@ class CrowdActionDto with _$CrowdActionDto { const factory CrowdActionDto({ required String id, - required String type, required String title, required String description, required String category, required LocationDto location, - required List commitmentOptions, + required List commitments, required ImagesDto images, required int participantCount, required Status status, @@ -32,13 +31,11 @@ class CrowdActionDto with _$CrowdActionDto { CrowdAction toDomain() { return CrowdAction( id: id, - type: type, title: title, description: description, category: category, location: location.toDomain(), - commitmentOptions: - commitmentOptions.map((option) => option.toDomain()).toList(), + commitments: commitments.map((c) => c.toDomain()).toList(), images: images.toDomain(), participantCount: participantCount, status: status, @@ -94,23 +91,22 @@ class LocationDto with _$LocationDto { } @freezed -class CommitmentOptionDto with _$CommitmentOptionDto { - const CommitmentOptionDto._(); +class CommitmentDto with _$CommitmentDto { + const CommitmentDto._(); - factory CommitmentOptionDto({ - required String id, - required String type, + factory CommitmentDto({ + // ignore: invalid_annotation_target + @JsonKey(name: '_id') required String id, required String label, required int points, required List blocks, String? description, String? icon, - }) = _CommitmentOptionDto; + }) = _CommitmentDto; - CommitmentOption toDomain() { - return CommitmentOption( + Commitment toDomain() { + return Commitment( id: id, - type: type, label: label, points: points, blocks: blocks, @@ -119,6 +115,6 @@ class CommitmentOptionDto with _$CommitmentOptionDto { ); } - factory CommitmentOptionDto.fromJson(Map json) => - _$CommitmentOptionDtoFromJson(json); + factory CommitmentDto.fromJson(Map json) => + _$CommitmentDtoFromJson(json); } diff --git a/lib/infrastructure/participation/participation_dto.dart b/lib/infrastructure/participation/participation_dto.dart index 905312fc..145c3485 100644 --- a/lib/infrastructure/participation/participation_dto.dart +++ b/lib/infrastructure/participation/participation_dto.dart @@ -15,7 +15,7 @@ class ParticipationDto with _$ParticipationDto { required String userId, required String fullName, required String avatar, - required List commitmentOptions, + required List commitments, required String joinDate, required int dailyCheckIns, }) = _ParticipationDto; @@ -27,7 +27,7 @@ class ParticipationDto with _$ParticipationDto { userId: userId, fullName: fullName, avatar: avatar, - commitmentOptions: commitmentOptions, + commitments: commitments, joinDate: DateTime.parse(joinDate), dailyCheckIns: dailyCheckIns, ); diff --git a/lib/infrastructure/participation/participation_repository.dart b/lib/infrastructure/participation/participation_repository.dart index 00498cbb..6701331f 100644 --- a/lib/infrastructure/participation/participation_repository.dart +++ b/lib/infrastructure/participation/participation_repository.dart @@ -67,7 +67,7 @@ class ParticipationRepository implements IParticipationRepository { @override Future> toggleParticipation({ required String crowdActionId, - List? commitmentOptions, + List? commitments, }) async { try { final user = (await authRepository.getSignedInUser()) @@ -82,7 +82,7 @@ class ParticipationRepository implements IParticipationRepository { uri, body: json.encode({ 'crowdActionId': crowdActionId, - 'commitmentOptions': commitmentOptions, + 'commitments': commitments, }), headers: { 'Content-Type': 'application/json', diff --git a/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart b/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart index d1da17dd..0cc56950 100644 --- a/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart +++ b/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart @@ -36,7 +36,7 @@ class CrowdActionDetailsPage extends StatefulWidget { } class CrowdActionDetailsPageState extends State { - final List selectedCommitments = []; + final List selectedCommitments = []; CrowdAction? crowdAction; late final ParticipationBloc participationBloc; late Function(BuildContext) participate; @@ -86,10 +86,9 @@ class CrowdActionDetailsPageState extends State { participating: (p) { setState(() { selectedCommitments.clear(); - for (final id in p.commitmentOptions) { + for (final id in p.commitments) { selectedCommitments.add( - crowdAction!.commitmentOptions - .firstWhere((c) => c.id == id), + crowdAction!.commitments.firstWhere((c) => c.id == id), ); } }); @@ -231,7 +230,7 @@ class CrowdActionDetailsPageState extends State { ), CommitmentCardList( isEnded: crowdAction?.isClosed ?? true, - commitmentOptions: crowdAction?.commitmentOptions, + commitments: crowdAction?.commitments, selectedCommitments: selectedCommitments, ), diff --git a/lib/presentation/crowdaction/crowdaction_details/widgets/confirm_participation.dart b/lib/presentation/crowdaction/crowdaction_details/widgets/confirm_participation.dart index 38b39bfa..d6cbf014 100644 --- a/lib/presentation/crowdaction/crowdaction_details/widgets/confirm_participation.dart +++ b/lib/presentation/crowdaction/crowdaction_details/widgets/confirm_participation.dart @@ -10,7 +10,7 @@ import '../../../themes/constants.dart'; class ConfirmParticipation extends StatelessWidget { final CrowdAction crowdAction; - final List selectedCommitments; + final List selectedCommitments; const ConfirmParticipation({ super.key, @@ -116,7 +116,7 @@ class ParticipationSuccess extends StatelessWidget { class ParticipationDialog extends StatelessWidget { final CrowdAction crowdAction; - final List selectedCommitments; + final List selectedCommitments; final bool isLoading; const ParticipationDialog({ @@ -213,8 +213,7 @@ class ParticipationDialog extends StatelessWidget { BlocProvider.of(context).add( ParticipationEvent.toggleParticipation( crowdActionId: crowdAction.id, - commitmentOptions: - selectedCommitments.map((c) => c.id).toList(), + commitments: selectedCommitments.map((c) => c.id).toList(), ), ); }, diff --git a/lib/presentation/demo/components_demo/components_demo_screen.dart b/lib/presentation/demo/components_demo/components_demo_screen.dart index 965b3899..c89d83b6 100644 --- a/lib/presentation/demo/components_demo/components_demo_screen.dart +++ b/lib/presentation/demo/components_demo/components_demo_screen.dart @@ -50,7 +50,6 @@ class ComponentsDemoPageState extends State { CrowdActionCard( crowdAction: CrowdAction( id: "", - type: "", title: "This is the headline for a crowdaction with three lines", images: const Images( @@ -59,18 +58,16 @@ class ComponentsDemoPageState extends State { ), category: "Sustainability", subcategory: "Community", - commitmentOptions: [ - CommitmentOption( + commitments: [ + Commitment( id: "", - type: "food", label: "no-beef", description: "Don't eat beef for 30 days!", points: 0, blocks: [], ), - CommitmentOption( + Commitment( id: "", - type: "food", label: "vegetarian", description: "Don't eat meat for 30 days!", points: 0, @@ -92,7 +89,6 @@ class ComponentsDemoPageState extends State { CrowdActionCard( crowdAction: CrowdAction( id: "", - type: "", title: "This is the headline for a crowdaction with three lines", images: const Images( @@ -101,18 +97,16 @@ class ComponentsDemoPageState extends State { ), category: "Sustainability", subcategory: "Community", - commitmentOptions: [ - CommitmentOption( + commitments: [ + Commitment( id: "", - type: "food", label: "no-beef", description: "Don't eat beef for 30 days!", points: 0, blocks: [], ), - CommitmentOption( + Commitment( id: "", - type: "food", label: "vegetarian", description: "Don't eat meat for 30 days!", points: 0, diff --git a/lib/presentation/profile/widget/commitments_tab.dart b/lib/presentation/profile/widget/commitments_tab.dart index bebbaa24..39eb9ced 100644 --- a/lib/presentation/profile/widget/commitments_tab.dart +++ b/lib/presentation/profile/widget/commitments_tab.dart @@ -59,7 +59,7 @@ class CommitmentsTab extends StatelessWidget { fontWeight: FontWeight.w500, ), ), - ...crowdAction.commitmentOptions.map( + ...crowdAction.commitments.map( (co) => CommitmentCard( commitment: co, viewOnly: true, diff --git a/lib/presentation/shared_widgets/commitment_card.dart b/lib/presentation/shared_widgets/commitment_card.dart index 30eae687..7a1d4941 100644 --- a/lib/presentation/shared_widgets/commitment_card.dart +++ b/lib/presentation/shared_widgets/commitment_card.dart @@ -80,9 +80,6 @@ class _CommitmentCardState extends State<_CommitmentCard> { @override Widget build(BuildContext context) { - print( - 'Commitment: ${widget.commitment.title} - Icon: ${widget.commitment.icon}'); - final textTheme = Theme.of(context).textTheme; return GestureDetector( onTap: () { diff --git a/lib/presentation/shared_widgets/commitments/commitment_card.dart b/lib/presentation/shared_widgets/commitments/commitment_card.dart index d1923f87..f3bf6376 100644 --- a/lib/presentation/shared_widgets/commitments/commitment_card.dart +++ b/lib/presentation/shared_widgets/commitments/commitment_card.dart @@ -10,9 +10,9 @@ import '../../themes/constants.dart'; /// [onSelected] Callback function for when the card is selected, /// returns the id of the selected commitment class CommitmentCard extends StatelessWidget { - final CommitmentOption commitment; - final Function(CommitmentOption)? onSelected; - final Function(CommitmentOption)? onDeSelected; + final Commitment commitment; + final Function(Commitment)? onSelected; + final Function(Commitment)? onDeSelected; final bool active; final bool deactivated; final bool viewOnly; diff --git a/lib/presentation/shared_widgets/commitments/commitment_card_list.dart b/lib/presentation/shared_widgets/commitments/commitment_card_list.dart index f74db7fb..606901ad 100644 --- a/lib/presentation/shared_widgets/commitments/commitment_card_list.dart +++ b/lib/presentation/shared_widgets/commitments/commitment_card_list.dart @@ -8,14 +8,14 @@ import 'commitment_card.dart'; class CommitmentCardList extends StatefulWidget { final bool isEnded; - final List? commitmentOptions; - final List selectedCommitments; + final List? commitments; + final List selectedCommitments; /// Widget for easily creating a list of CommitmentCard(s) const CommitmentCardList({ super.key, this.isEnded = false, - required this.commitmentOptions, + required this.commitments, required this.selectedCommitments, }); @@ -28,7 +28,7 @@ class _CommitmentCardListState extends State { Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - if (widget.commitmentOptions == null) { + if (widget.commitments == null) { return ListView( shrinkWrap: true, padding: const EdgeInsets.symmetric(horizontal: 20), @@ -51,7 +51,7 @@ class _CommitmentCardListState extends State { padding: const EdgeInsets.symmetric(horizontal: 20), physics: const BouncingScrollPhysics(), itemBuilder: (ctx, index) { - final option = widget.commitmentOptions![index]; + final option = widget.commitments![index]; return CommitmentCard( key: Key(option.id), commitment: option, @@ -62,14 +62,14 @@ class _CommitmentCardListState extends State { viewOnly: widget.isEnded, ); }, - itemCount: widget.commitmentOptions!.length, + itemCount: widget.commitments!.length, shrinkWrap: true, ); }, ); } - void selectCommitment(CommitmentOption commitment) { + void selectCommitment(Commitment commitment) { setState(() { widget.selectedCommitments.add(commitment); for (final id in commitment.blocks) { @@ -78,13 +78,13 @@ class _CommitmentCardListState extends State { }); } - void deselectCommitment(CommitmentOption commitment) { + void deselectCommitment(Commitment commitment) { setState(() { widget.selectedCommitments.removeWhere((c) => c.id == commitment.id); }); } - bool isBlocked(CommitmentOption commitment) { + bool isBlocked(Commitment commitment) { return widget.selectedCommitments .any((c) => c.blocks.contains(commitment.id)); } diff --git a/pubspec.lock b/pubspec.lock index bbb6d5a5..1767e4ce 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: "3ff770dfff04a67b0863dff205a0936784de1b87a5e99b11c693fc10e66a9ce3" + sha256: "64fcb0dbca4386356386c085142fa6e79c00a3326ceaa778a2d25f5d9ba61441" url: "https://pub.dev" source: hosted - version: "1.0.12" + version: "1.0.16" adaptive_number: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: args - sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611" + sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.0" async: dependency: transitive description: @@ -117,10 +117,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf" + sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.1" build_resolvers: dependency: transitive description: @@ -133,10 +133,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 + sha256: "93f05c041932674be039b0a2323d6cf57e5f2bbf884a3c0382f9e53fc45ebace" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.3.0" build_runner_core: dependency: transitive description: @@ -413,10 +413,10 @@ packages: dependency: transitive description: name: firebase_auth_platform_interface - sha256: "325d934e21826b3e7030f5018ef61927e2083b4c4fb25218ddef6ffc0012b717" + sha256: c645fec50b0391aa878288f58fa4fe9762c271380c457aedf5c7c9b718604f68 url: "https://pub.dev" source: hosted - version: "6.11.7" + version: "6.11.11" firebase_auth_web: dependency: transitive description: @@ -429,50 +429,50 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: c129209ba55f3d4272c89fb4a4994c15bea77fb6de63a82d45fb6bc5c94e4355 + sha256: fe30ac230f12f8836bb97e6e09197340d3c584526825b1746ea362a82e1e43f7 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.7.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: "5fab93f5b354648efa62e7cc829c90efb68c8796eecf87e0888cae2d5f3accd4" + sha256: "5615b30c36f55b2777d0533771deda7e5730e769e5d3cb7fda79e9bed86cfa55" url: "https://pub.dev" source: hosted - version: "4.5.2" + version: "4.5.3" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: "18b35ce111b0a4266abf723c825bcf9d4e2519d13638cc7f06f2a8dd960c75bc" + sha256: "291fbcace608aca6c860652e1358ef89752be8cc3ef227f8bbcd1e62775b833a" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.1" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: "9a73325afa984d2f6599a96b4b076f27a4e571172eb0056151522e3daace43ce" + sha256: "816bbb920316c8fe257b460b8856b01e274e867a729961bf7a3be6322cdf13e1" url: "https://pub.dev" source: hosted - version: "3.0.11" + version: "3.0.15" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: bccde26b6d63447c4f1f3d0ae5d78c958eb7bcef48239eb3aa8844675684c055 + sha256: "120e47b9bac3654848d1bdc60b8027f3574b53ee0b81b1a2e5e76ddaa58f6645" url: "https://pub.dev" source: hosted - version: "3.3.11" + version: "3.3.15" fixnum: dependency: transitive description: name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + sha256: "04be3e934c52e082558cc9ee21f42f5c1cd7a1262f4c63cd0357c08d5bba81ec" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.0.1" flutter: dependency: "direct main" description: flutter @@ -538,10 +538,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "60fc7b78455b94e6de2333d2f95196d32cf5c22f4b0b0520a628804cb463503b" + sha256: "4bef634684b2c7f3468c77c766c831229af829a0cd2d4ee6c1b99558bd14e5d2" url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.8" flutter_test: dependency: "direct dev" description: flutter @@ -572,10 +572,10 @@ packages: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + sha256: "4f4a162323c86ffc1245765cfe138872b8f069deb42f7dbb36115fa27f31469b" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "2.1.3" get_it: dependency: "direct main" description: @@ -668,42 +668,42 @@ packages: dependency: "direct main" description: name: image_picker - sha256: f98d76672d309c8b7030c323b3394669e122d52b307d2bbd8d06bd70f5b2aabe + sha256: "22207768556b82d55ec70166824350fee32298732d5efa4d6e756f848f51f66a" url: "https://pub.dev" source: hosted - version: "0.8.6+1" + version: "0.8.6+3" image_picker_android: dependency: transitive description: name: image_picker_android - sha256: b1cbfec0f5aef427a18eb573f5445af8c9c568626bf3388553e40c263d3f7368 + sha256: "68d067baf7f6e401b1124ee83dd6967e67847314250fd68012aab34a69beb344" url: "https://pub.dev" source: hosted - version: "0.8.5+5" + version: "0.8.5+7" image_picker_for_web: dependency: transitive description: name: image_picker_for_web - sha256: "7d319fb74955ca46d9bf7011497860e3923bb67feebcf068f489311065863899" + sha256: "66fc6e3877bbde82c33d122f3588777c3784ac5bd7d1cdd79213ef7aecb85b34" url: "https://pub.dev" source: hosted - version: "2.1.10" + version: "2.1.11" image_picker_ios: dependency: transitive description: name: image_picker_ios - sha256: "8ffb14b43713d7c43fb21299cc18181cc5b39bd3ea1cc427a085c6400fe5aa52" + sha256: "39aa70b5f1e5e7c94585b9738632d5fdb764a5655e40cd9e7b95fbd2fc50c519" url: "https://pub.dev" source: hosted - version: "0.8.6+7" + version: "0.8.6+9" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface - sha256: "7cef2f28f4f2fef99180f636c3d446b4ccbafd6ba0fad2adc9a80c4040f656b8" + sha256: "1991219d9dbc42a99aff77e663af8ca51ced592cd6685c9485e3458302d3d4f8" url: "https://pub.dev" source: hosted - version: "2.6.2" + version: "2.6.3" infinite_scroll_pagination: dependency: "direct main" description: @@ -724,10 +724,10 @@ packages: dependency: "direct dev" description: name: injectable_generator - sha256: "9a3bbd2c3ba821e31ef6cea3fc535c17e3a25c74e173b6cefa05f466c8338bc8" + sha256: b206de637c1960007b0beebe447a6ee3cf30c9e5f14542083024a9d0c49a7a09 url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" intl: dependency: "direct main" description: @@ -884,10 +884,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: f619162573096d428ccde2e33f92e05b5a179cd6f0e3120c1005f181bee8ed16 + sha256: "8df5ab0a481d7dc20c0e63809e90a588e496d276ba53358afc4c4443d0a00697" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" package_info_plus_platform_interface: dependency: transitive description: @@ -908,50 +908,50 @@ packages: dependency: transitive description: name: path_provider - sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + sha256: "04890b994ee89bfa80bf3080bfec40d5a92c5c7a785ebb02c13084a099d2b6f9" url: "https://pub.dev" source: hosted - version: "2.0.12" + version: "2.0.13" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + sha256: "7623b7d4be0f0f7d9a8b5ee6879fc13e4522d4c875ab86801dee4af32b54b83e" url: "https://pub.dev" source: hosted - version: "2.0.22" + version: "2.0.23" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" + sha256: eec003594f19fe2456ea965ae36b3fc967bc5005f508890aafe31fa75e41d972 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 + sha256: "525ad5e07622d19447ad740b1ed5070031f7a5437f44355ae915ff56e986429a" url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.1.9" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.6" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c + sha256: "642ddf65fde5404f83267e8459ddb4556316d3ee6d511ed193357e25caa3632d" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" pedantic: dependency: transitive description: @@ -988,10 +988,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" pointycastle: dependency: transitive description: @@ -1052,10 +1052,10 @@ packages: dependency: "direct main" description: name: rive - sha256: c4293ea52f9bf45edeebad3b1eb0bddc284bd11f14ede54780bf5efa1c491b4a + sha256: "21b9364d578d3f55f36e18da0235f58adf26edc328505e9bac1824f1bb297f33" url: "https://pub.dev" source: hosted - version: "0.10.1" + version: "0.10.2" rive_common: dependency: transitive description: @@ -1076,10 +1076,10 @@ packages: dependency: "direct main" description: name: share_plus - sha256: e387077716f80609bb979cd199331033326033ecd1c8f200a90c5f57b1c9f55e + sha256: "8c6892037b1824e2d7e8f59d54b3105932899008642e6372e5079c6939b4b625" url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.3.1" share_plus_platform_interface: dependency: transitive description: @@ -1092,58 +1092,58 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "5949029e70abe87f75cfe59d17bf5c397619c4b74a099b10116baeb34786fad9" + sha256: ee6257848f822b8481691f20c3e6d2bfee2e9eccb2a3d249907fcfb198c55b41 url: "https://pub.dev" source: hosted - version: "2.0.17" + version: "2.0.18" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "955e9736a12ba776bdd261cf030232b30eadfcd9c79b32a3250dd4a494e8c8f7" + sha256: a51a4f9375097f94df1c6e0a49c0374440d31ab026b59d58a7e7660675879db4 url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.0.16" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "2b55c18636a4edc529fa5cd44c03d3f3100c00513f518c5127c951978efcccd0" + sha256: "6b84fdf06b32bb336f972d373cd38b63734f3461ba56ac2ba01b56d052796259" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: f8ea038aa6da37090093974ebdcf4397010605fd2ff65c37a66f9d28394cb874 + sha256: d7fb71e6e20cd3dfffcc823a28da3539b392e53ed5fc5c2b90b55fdaa8a7e8fa url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: da9431745ede5ece47bc26d5d73a9d3c6936ef6945c101a5aca46f62e52c1cf3 + sha256: "824bfd02713e37603b2bdade0842e47d56e7db32b1dcdd1cae533fb88e2913fc" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: a4b5bc37fe1b368bbc81f953197d55e12f49d0296e7e412dfe2d2d77d6929958 + sha256: "6737b757e49ba93de2a233df229d0b6a87728cea1684da828cbc718b65dcf9d7" url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.0.5" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "5eaf05ae77658d3521d0e993ede1af962d4b326cd2153d312df716dc250f00c9" + sha256: bd014168e8484837c39ef21065b78f305810ceabc1d4f90be6e3b392ce81b46d url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" shelf: dependency: transitive description: @@ -1225,10 +1225,10 @@ packages: dependency: transitive description: name: source_maps - sha256: "490098075234dcedb83c5d949b4c93dad5e6b7702748de000be2b57b8e6b2427" + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" url: "https://pub.dev" source: hosted - version: "0.10.11" + version: "0.10.12" source_span: dependency: transitive description: @@ -1241,10 +1241,10 @@ packages: dependency: transitive description: name: sqflite - sha256: "78324387dc81df14f78df06019175a86a2ee0437624166c382e145d0a7fd9a4f" + sha256: "851d5040552cf911f4cabda08d003eca76b27da3ed0002978272e27c8fbf8ecc" url: "https://pub.dev" source: hosted - version: "2.2.4+1" + version: "2.2.5" sqflite_common: dependency: transitive description: @@ -1345,66 +1345,66 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: e8f2efc804810c0f2f5b485f49e7942179f56eabcfe81dce3387fec4bb55876b + sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e" url: "https://pub.dev" source: hosted - version: "6.1.9" + version: "6.1.10" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "3e2f6dfd2c7d9cd123296cab8ef66cfc2c1a13f5845f42c7a0f365690a8a7dd1" + sha256: "1f4d9ebe86f333c15d318f81dcdc08b01d45da44af74552608455ebdc08d9732" url: "https://pub.dev" source: hosted - version: "6.0.23" + version: "6.0.24" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "0a5af0aefdd8cf820dd739886efb1637f1f24489900204f50984634c07a54815" + sha256: c9cd648d2f7ab56968e049d4e9116f96a85517f1dd806b96a86ea1018a3a82e5 url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.1.1" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "318c42cba924e18180c029be69caf0a1a710191b9ec49bb42b5998fdcccee3cc" + sha256: e29039160ab3730e42f3d811dc2a6d5f2864b90a70fb765ea60144b03307f682 url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "41988b55570df53b3dd2a7fc90c76756a963de6a8c5f8e113330cb35992e2094" + sha256: "2dddb3291a57b074dade66b5e07e64401dd2487caefd4e9e2f467138d8c7eb06" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "4eae912628763eb48fc214522e58e942fd16ce195407dbf45638239523c759a6" + sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "44d79408ce9f07052095ef1f9a693c258d6373dc3944249374e30eff7219ccb0" + sha256: "574cfbe2390666003c3a1d129bdc4574aaa6728f0c00a4829a81c316de69dd9b" url: "https://pub.dev" source: hosted - version: "2.0.14" + version: "2.0.15" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: b6217370f8eb1fd85c8890c539f5a639a01ab209a36db82c921ebeacefc7a615 + sha256: "97c9067950a0d09cbd93e2e3f0383d1403989362b97102fbf446473a48079a4b" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.4" uuid: dependency: transitive description: @@ -1457,34 +1457,34 @@ packages: dependency: "direct main" description: name: webview_flutter - sha256: f7ec234830f86d0ef2bd664e8460b0038b8c1a83ff076035cad74ac70273753c + sha256: "9ba213434f13e760ea0f175fbc4d6bb6aeafd7dfc6c7d973f15d3e47a5d6686e" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.0.5" webview_flutter_android: dependency: transitive description: name: webview_flutter_android - sha256: "5f49a6e5fc59e21fcec5e1bbcd401afbee9792a24a4f3d9cef9b5bb0cd1e3767" + sha256: "48c8cfb023168473c0a3a4c21ffea6c23a32cc7156701c39f618b303c6a3c96e" url: "https://pub.dev" source: hosted - version: "3.2.4" + version: "3.3.1" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - sha256: "8b2262dda5d26eabc600a7282a8c16a9473a0c765526afb0ffc33eef912f7968" + sha256: df6472164b3f4eaf3280422227f361dc8424b106726b7f21d79a8656ba53f71f url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - sha256: "92e7e7fa468f1df597fb9d37bcf1f303175cbe147c4dbdf06ecc323d950116eb" + sha256: "283a38c2a2544768033864c698e0133aa9eee0f2c800f494b538a3d1044f7ecb" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.1.1" widgetbook: dependency: "direct dev" description: @@ -1529,18 +1529,18 @@ packages: dependency: transitive description: name: xdg_directories - sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 url: "https://pub.dev" source: hosted - version: "0.2.0+3" + version: "1.0.0" xml: dependency: transitive description: name: xml - sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + sha256: ac0e3f4bf00ba2708c33fbabbbe766300e509f8c82dbd4ab6525039813f7e2fb url: "https://pub.dev" source: hosted - version: "6.2.2" + version: "6.1.0" yaml: dependency: transitive description: @@ -1550,5 +1550,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=2.18.6 <3.0.0" flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9d472b2e..f9bced27 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,16 +5,6 @@ description: The CollAction app # pub.dev using `pub publish`. This is preferred for private packages. publish_to: "none" -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 1.2.0+1 environment: diff --git a/test/domain/crowdaction/crowdaction_test_fixtures.dart b/test/domain/crowdaction/crowdaction_test_fixtures.dart index bfe38eb8..52005f04 100644 --- a/test/domain/crowdaction/crowdaction_test_fixtures.dart +++ b/test/domain/crowdaction/crowdaction_test_fixtures.dart @@ -2,7 +2,7 @@ import 'package:collaction_app/domain/crowdaction/crowdaction.dart'; import '../../test_utilities.dart'; -final List tListCommitmentOptions = [tCommitmentOption]; +final List tListCommitments = [tCommitment]; const Images tImage = Images(card: 'tCard', banner: 'tBanner'); @@ -16,12 +16,11 @@ CrowdAction generateDummyCrowdaction({ }) { return CrowdAction( id: 'tID', - type: 'tType', title: 'tTitle', description: 'tDescription', category: 'tCategory', location: tLocation, - commitmentOptions: tListCommitmentOptions, + commitments: tListCommitments, endAt: endDate ?? DateTime(2022, 1, 31), images: tImage, participantCount: participantCnt, diff --git a/test/infrastructure/crowdaction/commitment_option_dto_fixtures.dart b/test/infrastructure/crowdaction/commitment_option_dto_fixtures.dart index 06b85038..97195ccf 100644 --- a/test/infrastructure/crowdaction/commitment_option_dto_fixtures.dart +++ b/test/infrastructure/crowdaction/commitment_option_dto_fixtures.dart @@ -1,9 +1,8 @@ import 'package:collaction_app/infrastructure/crowdaction/crowdaction_dto.dart'; import 'package:collaction_app/domain/crowdaction/crowdaction.dart'; -final tCommitmentOption = CommitmentOption( +final tCommitment = Commitment( id: 'id', - type: 'type', label: 'label', points: 0, blocks: ['blocks'], @@ -11,9 +10,8 @@ final tCommitmentOption = CommitmentOption( iconId: 'iconId', ); -final tCommitmentOptionDto = CommitmentOptionDto( +final tCommitmentDto = CommitmentDto( id: 'id', - type: 'type', label: 'label', points: 0, blocks: ['blocks'], @@ -21,9 +19,8 @@ final tCommitmentOptionDto = CommitmentOptionDto( icon: 'iconId', ); -final tCommitmentOptionJson = { - 'id': 'id', - 'type': 'type', +final tCommitmentJson = { + '_id': 'id', 'label': 'label', 'points': 0, 'blocks': ['blocks'], diff --git a/test/infrastructure/crowdaction/commitment_option_dto_test.dart b/test/infrastructure/crowdaction/commitment_option_dto_test.dart index 5a34c917..87f955ed 100644 --- a/test/infrastructure/crowdaction/commitment_option_dto_test.dart +++ b/test/infrastructure/crowdaction/commitment_option_dto_test.dart @@ -3,17 +3,17 @@ import 'package:collaction_app/infrastructure/crowdaction/crowdaction_dto.dart'; import 'commitment_option_dto_fixtures.dart'; void main() { - group('unit tests for CommitmentOptionDto', () { + group('unit tests for CommitmentDto', () { test('toDomain() test', () { - final commitmentOption = tCommitmentOptionDto.toDomain(); + final commitment = tCommitmentDto.toDomain(); - expect(commitmentOption, tCommitmentOption); + expect(commitment, tCommitment); }); + test('fromJson() test', () { - final commitmentOptionDto = - CommitmentOptionDto.fromJson(tCommitmentOptionJson); + final commitmentDto = CommitmentDto.fromJson(tCommitmentJson); - expect(commitmentOptionDto, tCommitmentOptionDto); + expect(commitmentDto, tCommitmentDto); }); }); } diff --git a/test/infrastructure/crowdaction/crowdaction_dto_fixtures.dart b/test/infrastructure/crowdaction/crowdaction_dto_fixtures.dart index d289ecdf..ffc4d00e 100644 --- a/test/infrastructure/crowdaction/crowdaction_dto_fixtures.dart +++ b/test/infrastructure/crowdaction/crowdaction_dto_fixtures.dart @@ -7,7 +7,7 @@ final crowdActionDto = CrowdActionDto( description: 'crowdaction-description', category: 'crowdaction-category', location: LocationDto(code: 'NL', name: 'Netherlands'), - commitmentOptions: [], + commitments: [], images: ImagesDto(card: 'crowdaction-card', banner: 'crowdaction-banner'), participantCount: 0, status: Status.waiting, @@ -15,12 +15,10 @@ final crowdActionDto = CrowdActionDto( endAt: '2024-01-01T00:00:00.000+00:00', password: 'crowdaction-password', subcategory: 'crowdaction-subcategory', - type: 'crowdaction-type', ); final crowdActionDomain = CrowdAction( id: 'crowdaction-id', - type: 'crowdaction-type', title: 'crowdaction-title', description: 'crowdaction-description', category: 'crowdaction-category', @@ -32,7 +30,7 @@ final crowdActionDomain = CrowdAction( status: Status.waiting, joinStatus: JoinStatus.open, endAt: DateTime.parse("2024-01-01T00:00:00.000+00:00"), - commitmentOptions: [], + commitments: [], ); final crowdActionJson = { @@ -49,5 +47,5 @@ final crowdActionJson = { "status": "WAITING", "joinStatus": "OPEN", "endAt": "2024-01-01T00:00:00.000+00:00", - "commitmentOptions": [], + "commitments": [], }; diff --git a/test/infrastructure/crowdaction/crowdaction_repository_fixtures.dart b/test/infrastructure/crowdaction/crowdaction_repository_fixtures.dart index 5e417623..75e8cca0 100644 --- a/test/infrastructure/crowdaction/crowdaction_repository_fixtures.dart +++ b/test/infrastructure/crowdaction/crowdaction_repository_fixtures.dart @@ -1,6 +1,5 @@ final crowdActionRaw = """{ "id": "12345678", - "type": "TYPE", "title": "Veganuary", "description": "description...", "category": "CATEGORY", @@ -8,10 +7,9 @@ final crowdActionRaw = """{ "code": "NL", "name": "Netherlands" }, - "commitmentOptions": [ + "commitments": [ { - "id": "12345678", - "type": "TYPE", + "_id": "12345678", "label": "label", "points": 50, "blocks": [ @@ -20,8 +18,7 @@ final crowdActionRaw = """{ "description": "description..." }, { - "id": "123456789", - "type": "TYPE", + "_id": "123456789", "label": "label", "points": 50, "blocks": [], @@ -49,7 +46,6 @@ final crowdActionsRaw = """{ "items": [ { "id": "123456789", - "type": "TYPE", "title": "Veganuary", "description": "description...", "category": "CATEGORY", @@ -57,10 +53,9 @@ final crowdActionsRaw = """{ "code": "NL", "name": "Netherlands" }, - "commitmentOptions": [ + "commitments": [ { - "id": "12345678", - "type": "TYPE", + "_id": "12345678", "label": "label", "points": 50, "blocks": [ @@ -69,8 +64,7 @@ final crowdActionsRaw = """{ "description": "description..." }, { - "id": "123456789", - "type": "TYPE", + "_id": "123456789", "label": "label", "points": 50, "blocks": [], @@ -89,7 +83,6 @@ final crowdActionsRaw = """{ }, { "id": "123456789", - "type": "TYPE", "title": "Veganuary", "description": "description...", "category": "CATEGORY", @@ -97,10 +90,9 @@ final crowdActionsRaw = """{ "code": "NL", "name": "Netherlands" }, - "commitmentOptions": [ + "commitments": [ { - "id": "12345678", - "type": "TYPE", + "_id": "12345678", "label": "label", "points": 50, "blocks": [ @@ -109,8 +101,7 @@ final crowdActionsRaw = """{ "description": "description..." }, { - "id": "123456789", - "type": "TYPE", + "_id": "123456789", "label": "label", "points": 50, "blocks": [], diff --git a/test/infrastructure/participation/participation_dto_fixtures.dart b/test/infrastructure/participation/participation_dto_fixtures.dart index 04d7a2a3..4a4ea752 100644 --- a/test/infrastructure/participation/participation_dto_fixtures.dart +++ b/test/infrastructure/participation/participation_dto_fixtures.dart @@ -7,7 +7,7 @@ final tParticipation = Participation( fullName: 'fullName', avatar: 'avatar', userId: 'userId', - commitmentOptions: ['commitmentOptions'], + commitments: ['commitments'], joinDate: DateTime.parse('20221120'), dailyCheckIns: 0, ); @@ -18,7 +18,7 @@ final tParticipationDto = ParticipationDto( fullName: 'fullName', avatar: 'avatar', userId: 'userId', - commitmentOptions: ['commitmentOptions'], + commitments: ['commitments'], joinDate: '20221120', dailyCheckIns: 0, ); @@ -29,7 +29,7 @@ const tParticipationJson = { 'fullName': 'fullName', 'avatar': 'avatar', 'userId': 'userId', - 'commitmentOptions': ['commitmentOptions'], + 'commitments': ['commitments'], 'joinDate': '20221120', 'dailyCheckIns': 0, }; diff --git a/test/infrastructure/participation/participation_repository_fixtures.dart b/test/infrastructure/participation/participation_repository_fixtures.dart index a1e56ee7..fa47e711 100644 --- a/test/infrastructure/participation/participation_repository_fixtures.dart +++ b/test/infrastructure/participation/participation_repository_fixtures.dart @@ -4,7 +4,7 @@ final participationRaw = """{ "fullName": "John Doe", "avatar": "/avatar/123456.png", "userId": "1234-1234-1234-1234", - "commitmentOptions": ["1234"], + "commitments": ["1234"], "joinDate": "2024-01-01T00:00:00.000+00:00", "dailyCheckIns": 10 }"""; @@ -23,7 +23,7 @@ final participationsRaw = """{ "fullName": "John Doe", "avatar": "/avatar/123456.png", "userId": "1234-1234-1234-1234", - "commitmentOptions": ["1234"], + "commitments": ["1234"], "joinDate": "2024-01-01T00:00:00.000+00:00", "dailyCheckIns": 10 }, @@ -33,7 +33,7 @@ final participationsRaw = """{ "fullName": "John Doe", "avatar": "/avatar/123456.png", "userId": "1234-1234-1234-1234", - "commitmentOptions": ["1234"], + "commitments": ["1234"], "joinDate": "2024-01-01T00:00:00.000+00:00", "dailyCheckIns": 10 } diff --git a/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.dart b/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.dart index 168d6202..fc1e4820 100644 --- a/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.dart +++ b/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.dart @@ -88,7 +88,7 @@ void main() { stackRouter, participationBloc, tCrowdaction, - [tCommitmentOption], + [tCommitment], ); await tester.pumpAndSettle(); @@ -111,7 +111,7 @@ void main() { stackRouter, participationBloc, tCrowdaction, - List.filled(3, tCommitmentOption), + List.filled(3, tCommitment), ); await tester.pumpAndSettle(); @@ -128,7 +128,7 @@ void main() { testWidgets( 'on Confirm, adds toggleParticipation event to Participation Bloc', (WidgetTester tester) async { - final selectedCommitments = List.filled(3, tCommitmentOption); + final selectedCommitments = List.filled(3, tCommitment); await tester.pumpConfirmParticipation( stackRouter, @@ -142,7 +142,7 @@ void main() { () => participationBloc.add( ParticipationEvent.toggleParticipation( crowdActionId: tCrowdaction.id, - commitmentOptions: selectedCommitments.map((c) => c.id).toList(), + commitments: selectedCommitments.map((c) => c.id).toList(), ), ), ); diff --git a/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.ext.dart b/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.ext.dart index f8584292..45ea656f 100644 --- a/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.ext.dart +++ b/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.ext.dart @@ -5,7 +5,7 @@ extension WidgetX on WidgetTester { StackRouter stackRouter, ParticipationBloc participationBloc, CrowdAction crowdAction, - List selectedCommitments) async { + List selectedCommitments) async { await pumpWidget( BlocProvider( create: (_) => participationBloc, diff --git a/test/presentation/profile/commitments_tab_fixtures.dart b/test/presentation/profile/commitments_tab_fixtures.dart index 499df5c2..3c22518c 100644 --- a/test/presentation/profile/commitments_tab_fixtures.dart +++ b/test/presentation/profile/commitments_tab_fixtures.dart @@ -8,15 +8,13 @@ final user = User( final crowdAction = CrowdAction( id: 'id', - type: 'type', title: 'title', description: 'description', category: 'category', location: Location(code: 'NL', name: 'The Netherlands'), - commitmentOptions: [ - CommitmentOption( + commitments: [ + Commitment( id: 'id', - type: 'type', label: 'label', points: 10, blocks: [], diff --git a/test/presentation/shared_widgets/commitment_card_fixtures.dart b/test/presentation/shared_widgets/commitment_card_fixtures.dart index 2530ccc8..e83b1933 100644 --- a/test/presentation/shared_widgets/commitment_card_fixtures.dart +++ b/test/presentation/shared_widgets/commitment_card_fixtures.dart @@ -1,8 +1,7 @@ import 'package:collaction_app/domain/crowdaction/crowdaction.dart'; -final commitmentOption = CommitmentOption( +final commitment = Commitment( id: 'id', - type: 'type', description: 'description', label: 'label', points: 10, diff --git a/test/presentation/shared_widgets/commitment_card_list_test.dart b/test/presentation/shared_widgets/commitment_card_list_test.dart index 393ccd72..7d29494c 100644 --- a/test/presentation/shared_widgets/commitment_card_list_test.dart +++ b/test/presentation/shared_widgets/commitment_card_list_test.dart @@ -31,7 +31,7 @@ void main() { }); group('CommitmentCardList tests:', () { - testWidgets('can render with commitmentOptions = null', + testWidgets('can render with commitments = null', (WidgetTester tester) async { await tester.pumpCommitmentCardList(false, null, [], participationBloc); await tester.pump(); @@ -41,8 +41,7 @@ void main() { expect(find.byType(CommitmentCardShimmer), findsNWidgets(3)); }); - testWidgets('can render with 0 commitmentOptions', - (WidgetTester tester) async { + testWidgets('can render with 0 commitments', (WidgetTester tester) async { await tester.pumpCommitmentCardList(false, [], [], participationBloc); await tester.pumpAndSettle(); @@ -51,11 +50,10 @@ void main() { expect(find.byType(CommitmentCardShimmer), findsNothing); }); - testWidgets('can render with 1 commitmentOptions', - (WidgetTester tester) async { + testWidgets('can render with 1 commitments', (WidgetTester tester) async { await tester.pumpCommitmentCardList( false, - [tCommitmentOption], + [tCommitment], [], participationBloc, ); @@ -68,11 +66,10 @@ void main() { expect(find.byType(CommitmentCardShimmer), findsNothing); }); - testWidgets('can render with 5 commitmentOptions', - (WidgetTester tester) async { + testWidgets('can render with 5 commitments', (WidgetTester tester) async { await tester.pumpCommitmentCardList( false, - List.filled(5, tCommitmentOption), + List.filled(5, tCommitment), [], participationBloc, ); @@ -88,7 +85,7 @@ void main() { (WidgetTester tester) async { await tester.pumpCommitmentCardList( false, - [tCommitmentOption], + [tCommitment], [], participationBloc, ); @@ -118,7 +115,7 @@ void main() { testWidgets('blocking commitments', (WidgetTester tester) async { await tester.pumpCommitmentCardList( false, - [tCommitmentOption, tBlockingCommitmentOption], + [tCommitment, tBlockingCommitment], [], participationBloc, ); @@ -138,8 +135,8 @@ void main() { testWidgets('preselected commitments', (WidgetTester tester) async { await tester.pumpCommitmentCardList( false, - [tCommitmentOption], - [tCommitmentOption], + [tCommitment], + [tCommitment], participationBloc, ); await tester.pumpAndSettle(); @@ -156,8 +153,8 @@ void main() { testWidgets('commitment is over', (WidgetTester tester) async { await tester.pumpCommitmentCardList( true, - [tCommitmentOption], - [tCommitmentOption], + [tCommitment], + [tCommitment], participationBloc, ); await tester.pumpAndSettle(); @@ -178,7 +175,7 @@ void main() { await tester.pumpCommitmentCardList( true, - [tCommitmentOption], + [tCommitment], [], participationBloc, ); @@ -203,8 +200,8 @@ void main() { await tester.pumpCommitmentCardList( true, - [tCommitmentOption], - [tCommitmentOption], + [tCommitment], + [tCommitment], participationBloc, ); await tester.pumpAndSettle(); diff --git a/test/presentation/shared_widgets/commitment_card_list_test.ext.dart b/test/presentation/shared_widgets/commitment_card_list_test.ext.dart index 33175219..427bc5da 100644 --- a/test/presentation/shared_widgets/commitment_card_list_test.ext.dart +++ b/test/presentation/shared_widgets/commitment_card_list_test.ext.dart @@ -3,8 +3,8 @@ part of 'commitment_card_list_test.dart'; extension WidgetX on WidgetTester { Future pumpCommitmentCardList( bool isEnded, - List? commitmentOptions, - List selectedCommitments, + List? commitments, + List selectedCommitments, ParticipationBloc participationBloc, ) async { await pumpWidget( @@ -18,7 +18,7 @@ extension WidgetX on WidgetTester { home: Scaffold( body: CommitmentCardList( isEnded: isEnded, - commitmentOptions: commitmentOptions, + commitments: commitments, selectedCommitments: selectedCommitments, ), ), diff --git a/test/presentation/shared_widgets/commitment_card_test.dart b/test/presentation/shared_widgets/commitment_card_test.dart index 66032697..569a5d57 100644 --- a/test/presentation/shared_widgets/commitment_card_test.dart +++ b/test/presentation/shared_widgets/commitment_card_test.dart @@ -10,7 +10,7 @@ void main() { await buildAndPump( tester: tester, widget: CommitmentCard( - commitment: commitmentOption, + commitment: commitment, ), ); await tester.pumpAndSettle(); @@ -22,7 +22,7 @@ void main() { await buildAndPump( tester: tester, widget: CommitmentCard( - commitment: commitmentOption, + commitment: commitment, deactivated: true, ), ); @@ -40,7 +40,7 @@ void main() { onSelected: (_) => counter++, onDeSelected: (_) => counter--, active: false, - commitment: commitmentOption, + commitment: commitment, ), ); await tester.pumpAndSettle(); @@ -64,7 +64,7 @@ void main() { onSelected: (_) => counter++, onDeSelected: (_) => counter--, active: true, - commitment: commitmentOption, + commitment: commitment, ), ); await tester.pumpAndSettle(); @@ -83,7 +83,7 @@ void main() { await buildAndPump( tester: tester, widget: CommitmentCard( - commitment: commitmentOption, + commitment: commitment, viewOnly: true, ), ); diff --git a/test/test_utilities.dart b/test/test_utilities.dart index 73b88766..03305cd1 100644 --- a/test/test_utilities.dart +++ b/test/test_utilities.dart @@ -84,12 +84,11 @@ class TestUtilities { static CrowdAction crowdActionWithNParticipants(int participantCount) { return CrowdAction( id: 'tID', - type: '', title: 'tTitle', description: 'tDescription', category: 'tCategory', location: tLocation, - commitmentOptions: [tCommitmentOption], + commitments: [tCommitment], endAt: DateTime(2022, 1, 31), images: const Images(card: 'tCard', banner: 'tBanner'), participantCount: participantCount, @@ -110,12 +109,11 @@ BASE_STATIC_ENDPOINT_URL = http://collaction.org final tCrowdaction = CrowdAction( id: 'tID', - type: '', title: 'tTitle', description: 'tDescription', category: 'tCategory', location: tLocation, - commitmentOptions: [tCommitmentOption], + commitments: [tCommitment], endAt: DateTime(2022, 1, 31), images: const Images(card: 'tCard', banner: 'tBanner'), participantCount: 10, @@ -126,12 +124,11 @@ final tCrowdaction = CrowdAction( final tCrowdactionNoPassword = CrowdAction( id: 'tID', - type: '', title: 'tTitle', description: 'tDescription', category: 'tCategory', location: tLocation, - commitmentOptions: [tCommitmentOption], + commitments: [tCommitment], endAt: DateTime(2022, 1, 31), images: const Images(card: 'tCard', banner: 'tBanner'), participantCount: 10, @@ -139,25 +136,23 @@ final tCrowdactionNoPassword = CrowdAction( joinStatus: JoinStatus.closed, ); -final tCommitmentOption = CommitmentOption( +final tCommitment = Commitment( id: 'no-beef', - type: 'food', label: 'tLabel', description: 'tDescription', points: 0, blocks: [], ); -final tBlockingCommitmentOption = CommitmentOption( +final tBlockingCommitment = Commitment( id: 'no-meat', - type: 'food', label: 'tLabel', description: 'tDescription', points: 0, blocks: ['no-beef'], ); -final List tCommitment = ['tCommitment']; +final List tCommitments = ['tCommitment']; final Participation tParticipation = Participation( id: 'tID', @@ -165,7 +160,7 @@ final Participation tParticipation = Participation( fullName: 'John Doe', avatar: 'tAvatar', userId: 'tID', - commitmentOptions: tCommitment, + commitments: tCommitments, joinDate: DateTime.now(), dailyCheckIns: 5, ); diff --git a/test/utils/crowdaction.fixtures.dart b/test/utils/crowdaction.fixtures.dart index 34fc819f..f5d22c8a 100644 --- a/test/utils/crowdaction.fixtures.dart +++ b/test/utils/crowdaction.fixtures.dart @@ -3,12 +3,11 @@ import 'package:collaction_app/domain/crowdaction/crowdaction.dart'; /// Reusable crowdaction fixtures final testCrowdAction = CrowdAction( id: 'id', - type: 'type', title: 'title', description: 'description', category: 'category', location: const Location(code: 'code', name: 'name'), - commitmentOptions: [], + commitments: [], images: const Images(banner: 'banner.png', card: 'card.png'), participantCount: 1, status: Status.started, diff --git a/test/utils/crowdactions.dart b/test/utils/crowdactions.dart index 8a5b8992..9c891a40 100644 --- a/test/utils/crowdactions.dart +++ b/test/utils/crowdactions.dart @@ -3,7 +3,6 @@ import 'package:collaction_app/infrastructure/crowdaction/crowdaction_dto.dart'; final _crowdActionsRawData = [ { "id": "62ceb7110b5e88ce294628a1", - "type": "FOOD", "title": "Veganuary", "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam varius ac ligula quis aliquet. Donec nisi leo, rhoncus ac nulla quis, egestas ornare quam. Nunc nibh ex, venenatis quis maximus vitae, elementum sed urna. Morbi sagittis metus nibh, non lacinia ipsum faucibus eu. Nulla vehicula purus eget congue auctor.", @@ -17,17 +16,15 @@ final _crowdActionsRawData = [ "card": "crowdaction-cards/628a7fa741b5aa3af6582a07.png", "banner": "crowdaction-banners/628a7fa741b5aa3af6582a07.png" }, - "commitmentOptions": [ + "commitments": [ { - "type": "FOOD", "label": "5/7 Days a week", "description": "In case you prefer to exclude weekends", "points": -10, "blocks": [], - "id": "62b637e0c5d78b613a3cc6d2" + "_id": "62b637e0c5d78b613a3cc6d2" }, { - "type": "FOOD", "label": "Vegan", "description": "Commit to eating no product or by-product from any and all animals", @@ -38,42 +35,38 @@ final _crowdActionsRawData = [ "62b638c8c5d78b613a3cc6da", "62b638d9c5d78b613a3cc6dc" ], - "id": "62b6381fc5d78b613a3cc6d4" + "_id": "62b6381fc5d78b613a3cc6d4" }, { - "type": "FOOD", "label": "Vegetarian", "description": "Commit to eating no meat product or by-product of any and all animals", "points": 40, "blocks": ["62b638a6c5d78b613a3cc6d8", "62b638c8c5d78b613a3cc6da"], - "id": "62b63863c5d78b613a3cc6d6" + "_id": "62b63863c5d78b613a3cc6d6" }, { - "type": "FOOD", "label": "Pescatarian", "description": "Commit to eating no meat or poultry from animals, except fish and other seafood", "points": 30, "blocks": ["62b638c8c5d78b613a3cc6da"], - "id": "62b638a6c5d78b613a3cc6d8" + "_id": "62b638a6c5d78b613a3cc6d8" }, { - "type": "FOOD", "label": "No Beef", "description": "Commit to eating no beef", "points": 20, "blocks": [], - "id": "62b638c8c5d78b613a3cc6da" + "_id": "62b638c8c5d78b613a3cc6da" }, { - "type": "FOOD", "label": "No Dairy", "description": "Commit to eating no dairy products such as cheese and milk", "points": 10, "blocks": [], - "id": "62b638d9c5d78b613a3cc6dc" + "_id": "62b638d9c5d78b613a3cc6dc" } ], "status": "STARTED", @@ -87,7 +80,6 @@ final _crowdActionsRawData = [ }, { "id": "6364ca939d4f5dffc3fe8e25", - "type": "ENERGY", "title": "Steun je School", "description": "Tijdens deze campagne staat het thema energiebesparing centraal, in het bijzonder op scholen. We bevinden ons in een energiecrisis en alleen samen komen we eruit! Voor meer informatie, neem een kijkje op collaction.org/steunschool.", @@ -101,14 +93,13 @@ final _crowdActionsRawData = [ "card": "crowdaction-cards/education_january.jpg", "banner": "crowdaction-banners/education_january.jpg" }, - "commitmentOptions": [ + "commitments": [ { - "type": "ENERGY", "label": "SUSTAINABILITY", "description": "", "points": 10, "blocks": [], - "id": "6364bcbf9d4f5dffc3fe8e19" + "_id": "6364bcbf9d4f5dffc3fe8e19" } ], "status": "STARTED", @@ -122,7 +113,6 @@ final _crowdActionsRawData = [ }, { "id": "6364d440187f67b3798ca86e", - "type": "FOOD", "title": "The Food Initiative", "description": "Improve your eating habits, and downscale your CO2 footprint!", @@ -135,18 +125,16 @@ final _crowdActionsRawData = [ "card": "crowdaction-cards/placeholder.png", "banner": "crowdaction-banners/placeholder.png" }, - "commitmentOptions": [ + "commitments": [ { - "type": "FOOD", "label": "5/7 Days a week", "description": "In case you prefer to exclude weekends", "points": -10, "blocks": [], "icon": "working-days-only", - "id": "62b637e0c5d78b613a3cc6d2" + "_id": "62b637e0c5d78b613a3cc6d2" }, { - "type": "FOOD", "label": "Vegan", "description": "Commit to eating no product or by-product from animals", "points": 50, @@ -157,46 +145,42 @@ final _crowdActionsRawData = [ "62b638d9c5d78b613a3cc6dc" ], "icon": "vegan", - "id": "62b6381fc5d78b613a3cc6d4" + "_id": "62b6381fc5d78b613a3cc6d4" }, { - "type": "FOOD", "label": "Vegetarian", "description": "Commit to eating no meat product or by-product from animals", "points": 40, "blocks": ["62b638a6c5d78b613a3cc6d8", "62b638c8c5d78b613a3cc6da"], "icon": "vegetarian", - "id": "62b63863c5d78b613a3cc6d6" + "_id": "62b63863c5d78b613a3cc6d6" }, { - "type": "FOOD", "label": "Pescatarian", "description": "Commit to eating no meat or poultry from animals, except fish and other seafood", "points": 30, "blocks": ["62b638c8c5d78b613a3cc6da"], "icon": "pescatarian", - "id": "62b638a6c5d78b613a3cc6d8" + "_id": "62b638a6c5d78b613a3cc6d8" }, { - "type": "FOOD", "label": "No Beef", "description": "Commit to eating no beef", "points": 20, "blocks": [], "icon": "no-beef", - "id": "62b638c8c5d78b613a3cc6da" + "_id": "62b638c8c5d78b613a3cc6da" }, { - "type": "FOOD", "label": "No Dairy", "description": "Commit to eating no dairy products such as cheese and milk", "points": 10, "blocks": [], "icon": "no-dairy", - "id": "62b638d9c5d78b613a3cc6dc" + "_id": "62b638d9c5d78b613a3cc6dc" } ], "status": "WAITING", diff --git a/test/utils/participation.fixtures.dart b/test/utils/participation.fixtures.dart index 1727bad9..c9255633 100644 --- a/test/utils/participation.fixtures.dart +++ b/test/utils/participation.fixtures.dart @@ -10,7 +10,7 @@ final testParticipation = Participation( fullName: testProfile.fullName, avatar: testProfile.avatar, userId: testUser.id, - commitmentOptions: [], + commitments: [], joinDate: DateTime.now(), dailyCheckIns: 2, ); From a7d15443745142c300579902d4950b94e1458bda Mon Sep 17 00:00:00 2001 From: Yash Tiwari Date: Wed, 18 Jan 2023 01:25:01 +0530 Subject: [PATCH 23/30] chore: rebase profile pic and edit bio --- .../user/profile/profile_bloc.dart | 39 +++- .../user/profile/profile_event.dart | 19 +- .../user/profile/profile_state.dart | 18 +- lib/presentation/profile/profile_screen.dart | 173 ++++++++---------- .../user/profile/profile_bloc_test.dart | 4 +- 5 files changed, 135 insertions(+), 118 deletions(-) diff --git a/lib/application/user/profile/profile_bloc.dart b/lib/application/user/profile/profile_bloc.dart index 99784a81..fe7ce549 100644 --- a/lib/application/user/profile/profile_bloc.dart +++ b/lib/application/user/profile/profile_bloc.dart @@ -25,24 +25,45 @@ class ProfileBloc extends Bloc { await userOrFailure.fold( (failure) => const ProfileState( userProfile: null, - isEditing: false, + isPicEditing: false, + isBioEditing: false, ), (userProfile) => state.copyWith( userProfile: userProfile, - isEditing: false, + isPicEditing: false, + isBioEditing: false, ), ), ); }); - on((event, emit) { - emit(state.copyWith(isEditing: true)); + on((event, emit) { + emit(state.copyWith(isBioEditing: true)); }); - on((event, emit) async { + on((event, emit) { + emit(state.copyWith(isPicEditing: true)); + }); + + on((event, emit) async { if (event.bio != null) { await _profileRepository.saveProfile(bio: event.bio); } + + final userOrFailure = await _profileRepository.getUserProfile(); + + emit( + userOrFailure.fold( + (failure) => state.copyWith(isBioEditing: false), + (userProfile) => state.copyWith( + userProfile: userProfile, + isBioEditing: false, + ), + ), + ); + }); + + on((event, emit) async { final wasImageUpdated = event.image != null; if (wasImageUpdated) { await _avatarRepository.uploadAvatar(event.image!); @@ -53,18 +74,18 @@ class ProfileBloc extends Bloc { emit( userOrFailure.fold( - (failure) => state.copyWith(isEditing: false), + (failure) => state.copyWith(isPicEditing: false), (userProfile) => state.copyWith( userProfile: userProfile, - isEditing: false, + isPicEditing: false, wasProfilePictureUpdated: wasImageUpdated, ), ), ); }); - on((event, emit) { - emit(state.copyWith(isEditing: false)); + on((event, emit) { + emit(state.copyWith(isPicEditing: false)); }); } } diff --git a/lib/application/user/profile/profile_event.dart b/lib/application/user/profile/profile_event.dart index 4780c65e..a7f0dac6 100644 --- a/lib/application/user/profile/profile_event.dart +++ b/lib/application/user/profile/profile_event.dart @@ -4,16 +4,25 @@ abstract class ProfileEvent {} class GetUserProfile extends ProfileEvent {} -class EditProfile extends ProfileEvent {} +class EditBio extends ProfileEvent {} -class SaveProfile extends ProfileEvent { +class SaveBio extends ProfileEvent { final String? bio; + SaveBio({ + this.bio, + }); +} + +class CancelBio extends ProfileEvent {} + +class EditProfilePic extends ProfileEvent {} + +class SaveProfilePic extends ProfileEvent { final File? image; - SaveProfile({ - this.bio, + SaveProfilePic({ this.image, }); } -class CancelEditProfile extends ProfileEvent {} +class CancelEditProfilePic extends ProfileEvent {} diff --git a/lib/application/user/profile/profile_state.dart b/lib/application/user/profile/profile_state.dart index 57646fcf..da4fbbd1 100644 --- a/lib/application/user/profile/profile_state.dart +++ b/lib/application/user/profile/profile_state.dart @@ -3,33 +3,39 @@ part of 'profile_bloc.dart'; class ProfileState extends Equatable { const ProfileState({ required this.userProfile, - this.isEditing, + this.isPicEditing, + this.isBioEditing, this.wasProfilePictureUpdated, }); final UserProfile? userProfile; - final bool? isEditing; + final bool? isPicEditing; + final bool? isBioEditing; final bool? wasProfilePictureUpdated; factory ProfileState.initial() => const ProfileState( userProfile: null, - isEditing: false, + isPicEditing: false, + isBioEditing: false, wasProfilePictureUpdated: false, ); ProfileState copyWith({ UserProfile? userProfile, - bool? isEditing, + bool? isPicEditing, + bool? isBioEditing, bool? wasProfilePictureUpdated, }) { return ProfileState( userProfile: userProfile ?? this.userProfile, - isEditing: isEditing ?? this.isEditing, + isPicEditing: isPicEditing ?? this.isPicEditing, + isBioEditing: isBioEditing ?? this.isBioEditing, wasProfilePictureUpdated: wasProfilePictureUpdated ?? false, // Reset state implicitly ); } @override - List get props => [userProfile, isEditing, wasProfilePictureUpdated]; + List get props => + [userProfile, isPicEditing, isBioEditing, wasProfilePictureUpdated]; } diff --git a/lib/presentation/profile/profile_screen.dart b/lib/presentation/profile/profile_screen.dart index 43035b7c..40e03b83 100644 --- a/lib/presentation/profile/profile_screen.dart +++ b/lib/presentation/profile/profile_screen.dart @@ -4,6 +4,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:ionicons/ionicons.dart'; import 'package:share_plus/share_plus.dart'; import '../../application/user/profile/profile_bloc.dart'; @@ -129,7 +130,7 @@ class _UserProfilePageState extends State { maxRadius: 50, ), ), - if (state.isEditing == true) ...[ + if (state.userProfile != null) ...[ Positioned( bottom: 0, right: 0, @@ -139,13 +140,34 @@ class _UserProfilePageState extends State { context, onSelected: (image) { setState(() => _image = image); + BlocProvider.of(context) + .add( + SaveProfilePic(image: image), + ); }, ), backgroundColor: kAccentColor, mini: true, - child: const Icon(Icons.add), + child: const Icon( + Icons.drive_file_rename_outline, + ), ), ), + // Positioned( + // bottom: 0, + // left: 0, + // child: FloatingActionButton( + // onPressed: () { + // BlocProvider.of(context) + // .add(CancelEditProfilePic()); + + // _image = null; + // }, + // backgroundColor: kErrorColor, + // mini: true, + // child: const Icon(Icons.close), + // ), + // ), ] ], ), @@ -162,17 +184,59 @@ class _UserProfilePageState extends State { ), if (state.userProfile != null) ...[ const SizedBox(height: 40), - const Text( - 'About me', - style: TextStyle( - fontWeight: FontWeight.w700, - fontSize: 11, - color: Color(0xFF666666), - ), - textAlign: TextAlign.left, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'About me', + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 11, + color: Color(0xFF666666), + ), + textAlign: TextAlign.left, + ), + TextButton( + key: const Key('save_edit_bio_button'), + style: ButtonStyle( + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(200), + ), + ), + ), + onPressed: () { + if (state.isBioEditing == true) { + /// TODO: Implement save profile image + + BlocProvider.of(context).add( + SaveBio( + bio: bioController.text, + ), + ); + } else { + context + .read() + .add(EditBio()); + } + }, + child: Text( + state.isBioEditing == true + ? 'Save' + : 'Edit', + style: const TextStyle( + fontSize: 11, + color: kAccentColor, + fontWeight: FontWeight.w700, + ), + ), + ), + ], ), const SizedBox(height: 10), - if (state.isEditing == true) ...[ + if (state.isBioEditing == true) ...[ + //idhr bio editing start Row( children: [ Expanded( @@ -215,8 +279,9 @@ class _UserProfilePageState extends State { ), const SizedBox(height: 4), Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, children: [ - const SizedBox(width: 16), Text( 'Maximum 150 characters', style: Theme.of(context) @@ -265,90 +330,6 @@ class _UserProfilePageState extends State { ], ), ), - const SizedBox(height: 40), - TextButton( - key: const Key('save_edit_button'), - style: ButtonStyle( - overlayColor: MaterialStateColor.resolveWith( - (states) => state.isEditing == true - ? Colors.white.withOpacity(0.1) - : kAccentColor.withOpacity(0.1), - ), - backgroundColor: state.isEditing == true - ? MaterialStateProperty.all( - kAccentColor, - ) - : null, - minimumSize: MaterialStateProperty.all( - const Size(double.infinity * 0.75, 52), - ), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(200), - side: const BorderSide( - color: kAccentColor, - ), - ), - ), - ), - onPressed: () { - if (state.isEditing == true) { - BlocProvider.of(context).add( - SaveProfile( - bio: bioController.text, - image: _image, - ), - ); - } else { - context - .read() - .add(EditProfile()); - } - }, - child: Text( - state.isEditing == true - ? 'Save changes' - : 'Edit profile', - style: TextStyle( - color: state.isEditing == true - ? Colors.white - : kAccentColor, - fontWeight: FontWeight.w700, - ), - ), - ), - if (state.isEditing == true) ...[ - const SizedBox(height: 10), - TextButton( - key: const Key('cancel_edit_button'), - style: ButtonStyle( - minimumSize: MaterialStateProperty.all( - const Size(double.infinity * 0.75, 52), - ), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(200), - ), - ), - ), - onPressed: () { - BlocProvider.of(context) - .add(CancelEditProfile()); - - _image = null; - bioController.value = TextEditingValue( - text: state.userProfile?.profile.bio ?? '', - ); - }, - child: const Text( - 'Cancel', - style: TextStyle( - color: kAccentColor, - fontWeight: FontWeight.w700, - ), - ), - ), - ], ] else ...[ const SizedBox(height: 40), PillButton( diff --git a/test/application/user/profile/profile_bloc_test.dart b/test/application/user/profile/profile_bloc_test.dart index 5183cb8d..f83def18 100644 --- a/test/application/user/profile/profile_bloc_test.dart +++ b/test/application/user/profile/profile_bloc_test.dart @@ -40,7 +40,7 @@ void main() { expect: () => const [ ProfileState( userProfile: userProfile, - isEditing: false, + isPicEditing: false, wasProfilePictureUpdated: false, ) ], @@ -59,7 +59,7 @@ void main() { expect: () => const [ ProfileState( userProfile: null, - isEditing: false, + isPicEditing: false, ) ], verify: (_) => verify(() => profileRepository.getUserProfile()).called(1), From 7c3b7a61a94d3f811a683cd1cf05554d656b4bf2 Mon Sep 17 00:00:00 2001 From: Mathias Mogensen Date: Tue, 28 Feb 2023 17:28:58 +0100 Subject: [PATCH 24/30] fix: tests and formatter --- lib/presentation/profile/profile_screen.dart | 17 ----- .../user/profile/profile_bloc_test.dart | 69 ------------------- .../profile/profile_screen_test.dart | 4 +- 3 files changed, 2 insertions(+), 88 deletions(-) delete mode 100644 test/application/user/profile/profile_bloc_test.dart diff --git a/lib/presentation/profile/profile_screen.dart b/lib/presentation/profile/profile_screen.dart index 40e03b83..ea260b62 100644 --- a/lib/presentation/profile/profile_screen.dart +++ b/lib/presentation/profile/profile_screen.dart @@ -4,7 +4,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; -import 'package:ionicons/ionicons.dart'; import 'package:share_plus/share_plus.dart'; import '../../application/user/profile/profile_bloc.dart'; @@ -153,21 +152,6 @@ class _UserProfilePageState extends State { ), ), ), - // Positioned( - // bottom: 0, - // left: 0, - // child: FloatingActionButton( - // onPressed: () { - // BlocProvider.of(context) - // .add(CancelEditProfilePic()); - - // _image = null; - // }, - // backgroundColor: kErrorColor, - // mini: true, - // child: const Icon(Icons.close), - // ), - // ), ] ], ), @@ -209,7 +193,6 @@ class _UserProfilePageState extends State { onPressed: () { if (state.isBioEditing == true) { /// TODO: Implement save profile image - BlocProvider.of(context).add( SaveBio( bio: bioController.text, diff --git a/test/application/user/profile/profile_bloc_test.dart b/test/application/user/profile/profile_bloc_test.dart deleted file mode 100644 index f83def18..00000000 --- a/test/application/user/profile/profile_bloc_test.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:bloc_test/bloc_test.dart'; -import 'package:collaction_app/application/user/profile/profile_bloc.dart'; -import 'package:collaction_app/domain/user/i_avatar_repository.dart'; -import 'package:collaction_app/domain/user/i_profile_repository.dart'; -import 'package:collaction_app/domain/user/profile_failure.dart'; -import 'package:dartz/dartz.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; - -import '../../../domain/user/user_fixture.dart'; -import '../../../test_utilities.dart'; - -void main() { - late IProfileRepository profileRepository; - late IAvatarRepository avatarRepository; - late ProfileBloc profileBloc; - - const userProfile = cUserProfile; - - setUp(() { - profileRepository = MockProfileRepository(); - avatarRepository = MockAvatarRepository(); - profileBloc = ProfileBloc(profileRepository, avatarRepository); - }); - - test('should have [ProfileState.initial()] as initial state', () async { - /// assert - expect(profileBloc.state, equals(ProfileState.initial())); - }); - - blocTest( - 'should return [ProfileState] with user profile ' - 'when profile retrieval is successful', - setUp: () { - when(() => profileRepository.getUserProfile()) - .thenAnswer((_) async => const Right(userProfile)); - }, - build: () => profileBloc, - act: (bloc) => [bloc.add(GetUserProfile())], - expect: () => const [ - ProfileState( - userProfile: userProfile, - isPicEditing: false, - wasProfilePictureUpdated: false, - ) - ], - verify: (_) => verify(() => profileRepository.getUserProfile()).called(1), - ); - - blocTest( - 'should return [ProfileState] with null user profile ' - 'when profile retrieval is not successful', - setUp: () { - when(() => profileRepository.getUserProfile()) - .thenAnswer((_) async => const Left(ProfileFailure.unexpected())); - }, - build: () => profileBloc, - act: (bloc) => [bloc.add(GetUserProfile())], - expect: () => const [ - ProfileState( - userProfile: null, - isPicEditing: false, - ) - ], - verify: (_) => verify(() => profileRepository.getUserProfile()).called(1), - ); - - /// TODO(obella): Add more tests -} diff --git a/test/presentation/profile/profile_screen_test.dart b/test/presentation/profile/profile_screen_test.dart index 45960ed8..3de6e058 100644 --- a/test/presentation/profile/profile_screen_test.dart +++ b/test/presentation/profile/profile_screen_test.dart @@ -156,9 +156,9 @@ void main() { expect(find.byType(UserProfilePage), findsOneWidget); - profileBloc.add(EditProfile()); + profileBloc.add(EditBio()); await tester.pumpAndSettle(); - expect(profileBloc.state.isEditing, true); + expect(profileBloc.state.isBioEditing, true); }); } From 9473c3dcaaa63a42b5db8af15fcd1a50a77b2fdb Mon Sep 17 00:00:00 2001 From: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> Date: Wed, 1 Mar 2023 09:14:19 +0100 Subject: [PATCH 25/30] ci: if-condition on signing step --- .../utils/firebase_crashlytics_extension.dart | 21 +++++++++++++++++++ .../profile/widget/commitments_tab.dart | 12 +++++------ 2 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 lib/core/utils/firebase_crashlytics_extension.dart diff --git a/lib/core/utils/firebase_crashlytics_extension.dart b/lib/core/utils/firebase_crashlytics_extension.dart new file mode 100644 index 00000000..4136d5d4 --- /dev/null +++ b/lib/core/utils/firebase_crashlytics_extension.dart @@ -0,0 +1,21 @@ +import 'package:firebase_crashlytics/firebase_crashlytics.dart'; + +extension FirebaseCrashlyticsLogger on FirebaseCrashlytics { + static Future log(String message) async { + FirebaseCrashlytics.instance.log(message); + } + + static Future warn( + Exception exception, + StackTrace? stackTrace, { + String? message, + bool fatal = true, + }) async { + FirebaseCrashlytics.instance.recordError( + exception, + stackTrace, + reason: message, + fatal: fatal, + ); + } +} diff --git a/lib/presentation/profile/widget/commitments_tab.dart b/lib/presentation/profile/widget/commitments_tab.dart index 39eb9ced..8cfebc0f 100644 --- a/lib/presentation/profile/widget/commitments_tab.dart +++ b/lib/presentation/profile/widget/commitments_tab.dart @@ -70,12 +70,12 @@ class CommitmentsTab extends StatelessWidget { ), ), ), - if (user == null || (crowdActions?.isEmpty ?? false)) ...[ - SignUpCTA( - user: user, - title: 'View your amazing commitments here', - ), - ], + ], + if (user == null || (crowdActions?.isEmpty ?? false)) ...[ + SignUpCTA( + user: user, + title: 'View your amazing commitments here', + ), ], ], ), From eaea4bdaa1f1510f0526c0bef9d679a1bfb51170 Mon Sep 17 00:00:00 2001 From: Mathias Mogensen Date: Fri, 3 Mar 2023 11:29:15 +0100 Subject: [PATCH 26/30] refactor: unauthenticated page --- assets/images/unauthenticated_bg.png | Bin 7873 -> 9514 bytes .../auth/unauthenticated_screen.dart | 109 ++++++------------ .../shared_widgets/pill_button.dart | 12 +- 3 files changed, 46 insertions(+), 75 deletions(-) diff --git a/assets/images/unauthenticated_bg.png b/assets/images/unauthenticated_bg.png index fb8b1b6375a912f618167a73bb716081d78f5ea1..3a8f1b837f8b0633db01d82e03c2998b2049828d 100644 GIT binary patch literal 9514 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 Je0SXe_dl=L*`~UmC@6$P@j#COr>X4$V1&dO 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 L_dPXIwfcVm5OfkA diff --git a/lib/presentation/auth/unauthenticated_screen.dart b/lib/presentation/auth/unauthenticated_screen.dart index 0620981e..f5e91a29 100644 --- a/lib/presentation/auth/unauthenticated_screen.dart +++ b/lib/presentation/auth/unauthenticated_screen.dart @@ -16,81 +16,48 @@ class UnauthenticatedPage extends StatelessWidget { return Scaffold( backgroundColor: kAccentColor, - body: Stack( - children: [ - Container( - height: MediaQuery.of(context).size.height * .8, - child: Stack( - alignment: Alignment.center, - children: [ - Positioned( - top: 0, - right: 0, - left: 23, - child: Image.asset( - "assets/images/unauthenticated_bg.png", - fit: BoxFit.contain, - ), - ), - Align( - alignment: Alignment.center, - child: Image.asset( - "assets/images/logo.png", - width: 200, - ), - ), - ], + body: Container( + decoration: BoxDecoration( + image: DecorationImage( + fit: BoxFit.cover, + image: AssetImage( + "assets/images/unauthenticated_bg.png", ), ), - Align( - alignment: Alignment.bottomCenter, - child: Container( - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - "Start acting", - style: TextStyle( - fontWeight: FontWeight.w700, - fontSize: 28.0, - color: Colors.white, - ), - ), - Padding( - padding: const EdgeInsets.only( - bottom: 64.0, left: 16, right: 16, top: 8), - child: Text( - "We are ready, join us and others in taking action for a better life, by doing good and having fun!", - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white, - ), - ), - ), - Row( - children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.only( - bottom: 100.0, left: 20, right: 20), - child: PillButton( - text: 'Log In', - isEnabled: true, - isLoading: false, - onTap: () => context.router.push(const AuthRoute()), - lightBackground: true, - ), - ), - ), - ], - ), - ], + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "Start acting", + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 28.0, + color: Colors.white, + ), ), - ), + const SizedBox(height: 8), + Text( + "We are ready, join us and others in taking action for a better life, by doing good and having fun!", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + ), + ), + const SizedBox(height: 48), + PillButton( + text: 'Log In', + isEnabled: true, + isLoading: false, + onTap: () => context.router.push(const AuthRoute()), + lightBackground: true, + ), + const SizedBox(height: 80), + ], ), - ], + ), ), ); } diff --git a/lib/presentation/shared_widgets/pill_button.dart b/lib/presentation/shared_widgets/pill_button.dart index 5a8b1b8e..16cf8b7e 100644 --- a/lib/presentation/shared_widgets/pill_button.dart +++ b/lib/presentation/shared_widgets/pill_button.dart @@ -61,10 +61,14 @@ class PillButton extends StatelessWidget { ) : ElevatedButton.icon( icon: leading ?? const SizedBox(), - label: Text(text, - style: TextStyle( - color: lightBackground ? kAccentColor : Colors.white, - )), + label: Text( + text, + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 15, + color: lightBackground ? kAccentColor : Colors.white, + ), + ), onPressed: isEnabled ? onTap : null, style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith( From 292a4ddda140c9a06277c1f7fa14b7b31e78ddd5 Mon Sep 17 00:00:00 2001 From: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> Date: Sat, 4 Mar 2023 02:46:36 +0100 Subject: [PATCH 27/30] cicd: refactor to environment secrets --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6791942f..45d50dd1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: - name: Statically analyze the Dart code run: | - echo ${{ github.ref == 'refs/heads/master' && secrets.ENV_PROD || secrets.ENV_DEV }} | base64 -d > .env + echo ${{ secrets.ENV }} | base64 -d > .env flutter analyze . test: @@ -96,7 +96,7 @@ jobs: - name: Run tests and generate coverage run: | - echo ${{ github.ref == 'refs/heads/master' && secrets.ENV_PROD || secrets.ENV_DEV }} | base64 -d > .env + echo ${{ secrets.ENV }} | base64 -d > .env flutter test --coverage -r expanded . - name: Upload coverage to codecov @@ -159,9 +159,9 @@ jobs: - name: Build appbundle env: - GOOGLE_SERVICES_JSON: ${{ github.ref == 'refs/heads/master' && secrets.ANDROID_GOOGLE_SERVICES_JSON || secrets.ANDROID_GOOGLE_SERVICES_JSON_DEV }} + GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }} run: | - echo ${{ github.ref == 'refs/heads/master' && secrets.ENV_PROD || secrets.ENV_DEV }} | base64 -d > .env + echo ${{ secrets.ENV }} | base64 -d > .env echo $GOOGLE_SERVICES_JSON | base64 -d > android/app/google-services.json flutter build appbundle --release --build-number $GITHUB_RUN_NUMBER --no-tree-shake-icons cp release_notes.txt build/app/outputs/ @@ -324,7 +324,7 @@ jobs: - name: Build web run: | - echo ${{ github.ref == 'refs/heads/master' && secrets.ENV_PROD || secrets.ENV_DEV }} | base64 -d > .env + echo ${{ secrets.ENV }} | base64 -d > .env flutter build web -t lib/widgetbook/app.widgetbook.dart - name: Publish to Widgetbook From 79a3f4c53927fcca5fefbc856281e204b88c9b13 Mon Sep 17 00:00:00 2001 From: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> Date: Sat, 4 Mar 2023 12:19:46 +0100 Subject: [PATCH 28/30] cicd: use environment --- .github/workflows/ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45d50dd1..9ea2b09c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: - name: Statically analyze the Dart code run: | - echo ${{ secrets.ENV }} | base64 -d > .env + echo "" | base64 -d > .env flutter analyze . test: @@ -96,7 +96,7 @@ jobs: - name: Run tests and generate coverage run: | - echo ${{ secrets.ENV }} | base64 -d > .env + echo "" | base64 -d > .env flutter test --coverage -r expanded . - name: Upload coverage to codecov @@ -107,6 +107,7 @@ jobs: build_android: if: ${{ github.ref == 'refs/heads/development' || github.ref == 'refs/heads/master' }} + environment: ${{ github.ref == 'refs/heads/master' && 'production' || 'development' }} needs: [analyze, test] runs-on: macos-latest timeout-minutes: 60 @@ -189,6 +190,7 @@ jobs: build_ios: if: ${{ github.ref == 'refs/heads/development' || github.ref == 'refs/heads/master' }} + environment: ${{ github.ref == 'refs/heads/master' && 'production' || 'development' }} needs: [analyze, test] runs-on: macos-latest timeout-minutes: 60 @@ -332,6 +334,7 @@ jobs: run: widgetbook publish --api-key $WIDGETBOOK_API_KEY distribute_android: + environment: ${{ github.ref == 'refs/heads/master' && 'production' || 'development' }} needs: [build_android] runs-on: ubuntu-latest steps: @@ -350,6 +353,7 @@ jobs: releaseNotesFile: release_notes.txt distribute_ios: + environment: ${{ github.ref == 'refs/heads/master' && 'production' || 'development' }} needs: [build_ios] runs-on: macos-latest steps: From cf9b0b0256c06b59a639a8f916464963ee81363d Mon Sep 17 00:00:00 2001 From: Mathias Mogensencd collaction_website Date: Sat, 4 Mar 2023 19:48:24 +0100 Subject: [PATCH 29/30] fix: add manual logging to auth repo --- .../auth/firebase_auth_repository.dart | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/infrastructure/auth/firebase_auth_repository.dart b/lib/infrastructure/auth/firebase_auth_repository.dart index 0ec66fc7..42283e39 100644 --- a/lib/infrastructure/auth/firebase_auth_repository.dart +++ b/lib/infrastructure/auth/firebase_auth_repository.dart @@ -54,6 +54,13 @@ class FirebaseAuthRepository implements IAuthRepository, Disposable { result.add(right(AuthSuccess.codeSent(credential: credential))); }, verificationFailed: (firebase_auth.FirebaseAuthException error) { + FirebaseCrashlyticsLogger.warn( + error, + null, + message: + '[FirebaseAuthRepository] verifyPhoneNumber().verificationFailed', + ); + result.add(left(error.toFailure())); result.close(); }, @@ -166,6 +173,12 @@ class FirebaseAuthRepository implements IAuthRepository, Disposable { result.add(right(AuthSuccess.codeSent(credential: credential))); }, verificationFailed: (firebase_auth.FirebaseAuthException error) { + FirebaseCrashlyticsLogger.warn( + error, + null, + message: '[FirebaseAuthRepository] resendOTP().verificationFailed', + ); + result.add(left(error.toFailure())); result.close(); }, From 023d67eaef5b6040395cf03006731ed01cc28727 Mon Sep 17 00:00:00 2001 From: Mathias Mogensencd collaction_website Date: Sun, 5 Mar 2023 00:15:16 +0100 Subject: [PATCH 30/30] chore: upgrade gradle build tools and dependencies --- android/build.gradle | 6 +++--- android/gradle/wrapper/gradle-wrapper.properties | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 2a330ec6..6b74e344 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -6,10 +6,10 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.2.0' + classpath 'com.android.tools.build:gradle:7.2.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.google.gms:google-services:4.3.10' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1' + classpath 'com.google.gms:google-services:4.3.15' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.4' } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 939efa29..cb24abda 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip