From 379ac72766b8429f01ebca7b976378f9e052c2f7 Mon Sep 17 00:00:00 2001 From: Hardik Date: Thu, 11 Jul 2024 03:05:19 +0530 Subject: [PATCH 01/20] refactor: organize asset constants --- lib/app/modules/profile/views/profile_view.dart | 3 ++- lib/app/modules/root/views/root_view.dart | 3 ++- lib/app/utils/icon_constants.dart | 5 +++++ lib/app/utils/img_constants.dart | 6 ++++++ lib/app/widgets/login_widgets.dart | 5 +++-- pubspec.yaml | 6 +++--- 6 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 lib/app/utils/icon_constants.dart create mode 100644 lib/app/utils/img_constants.dart diff --git a/lib/app/modules/profile/views/profile_view.dart b/lib/app/modules/profile/views/profile_view.dart index c26d11c1..cd9b8e28 100644 --- a/lib/app/modules/profile/views/profile_view.dart +++ b/lib/app/modules/profile/views/profile_view.dart @@ -6,6 +6,7 @@ import 'package:get/get.dart'; import '../../../../services/auth_service.dart'; import '../../../../models/screens.dart'; +import '../../../utils/img_constants.dart'; import '../../../widgets/change_password_dialog.dart'; import '../../../widgets/image_picker_button.dart'; import '../controllers/profile_controller.dart'; @@ -62,7 +63,7 @@ class ProfileView extends GetView { ) : Center( child: Image.asset( - 'assets/images/dash.png', + ImgConstants.dash, width: size, fit: BoxFit.contain, ), diff --git a/lib/app/modules/root/views/root_view.dart b/lib/app/modules/root/views/root_view.dart index 2bbf228c..e5be177b 100644 --- a/lib/app/modules/root/views/root_view.dart +++ b/lib/app/modules/root/views/root_view.dart @@ -5,6 +5,7 @@ import 'package:get/get.dart'; import 'package:get_flutter_fire/services/auth_service.dart'; import '../../../routes/app_pages.dart'; import '../../../../models/screens.dart'; +import '../../../utils/icon_constants.dart'; import '../controllers/root_controller.dart'; import 'drawer.dart'; @@ -31,7 +32,7 @@ class RootView extends GetView { ) : IconButton( icon: ImageIcon( - const AssetImage("icons/logo.png"), + const AssetImage(IconConstants.logo), color: Colors.grey.shade800, ), onPressed: () => AuthService.to.isLoggedInValue diff --git a/lib/app/utils/icon_constants.dart b/lib/app/utils/icon_constants.dart new file mode 100644 index 00000000..b3351a95 --- /dev/null +++ b/lib/app/utils/icon_constants.dart @@ -0,0 +1,5 @@ +abstract class IconConstants { + static const _assetsIcon = 'assets/icons'; + + static const logo = '$_assetsIcon/logo.png'; +} diff --git a/lib/app/utils/img_constants.dart b/lib/app/utils/img_constants.dart new file mode 100644 index 00000000..a2be2f73 --- /dev/null +++ b/lib/app/utils/img_constants.dart @@ -0,0 +1,6 @@ +abstract class ImgConstants { + static const _assetsImg = 'assets/image'; + + static const dash = '$_assetsImg/dash.png'; + static const flutterfire = '$_assetsImg/flutterfire_300x.png'; +} diff --git a/lib/app/widgets/login_widgets.dart b/lib/app/widgets/login_widgets.dart index b8f2d8c1..98d6f950 100644 --- a/lib/app/widgets/login_widgets.dart +++ b/lib/app/widgets/login_widgets.dart @@ -6,6 +6,7 @@ import 'package:get/get.dart'; import '../../services/auth_service.dart'; import '../../models/screens.dart'; import '../../services/remote_config.dart'; +import '../utils/img_constants.dart'; import 'menu_sheet_button.dart'; class LoginWidgets { @@ -14,7 +15,7 @@ class LoginWidgets { padding: const EdgeInsets.all(20), child: AspectRatio( aspectRatio: 1, - child: Image.asset('assets/images/flutterfire_300x.png'), + child: Image.asset(ImgConstants.flutterfire), ), ); } @@ -38,7 +39,7 @@ class LoginWidgets { padding: const EdgeInsets.all(20), child: AspectRatio( aspectRatio: 1, - child: Image.asset('assets/images/flutterfire_300x.png'), + child: Image.asset(ImgConstants.flutterfire), ), ); } diff --git a/pubspec.yaml b/pubspec.yaml index 2909a374..d000e5f5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,8 +36,8 @@ flutter: fonts: - asset: packages/firebase_ui_auth/fonts/SocialIcons.ttf assets: - - assets/images/flutterfire_300x.png - - assets/images/dash.png - - assets/icons/logo.png + - assets/ + - assets/images/ + - assets/icons/ uses-material-design: true From 255019f945da70574f0ecfbd0252891105c69080 Mon Sep 17 00:00:00 2001 From: Hardik Date: Thu, 11 Jul 2024 03:13:10 +0530 Subject: [PATCH 02/20] fix: correct path typo from 'assets/image' to 'assets/images' --- lib/app/utils/img_constants.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/app/utils/img_constants.dart b/lib/app/utils/img_constants.dart index a2be2f73..83a5c0c6 100644 --- a/lib/app/utils/img_constants.dart +++ b/lib/app/utils/img_constants.dart @@ -1,5 +1,5 @@ abstract class ImgConstants { - static const _assetsImg = 'assets/image'; + static const _assetsImg = 'assets/images'; static const dash = '$_assetsImg/dash.png'; static const flutterfire = '$_assetsImg/flutterfire_300x.png'; From fed66e8bc8349512fe44d15ca028246c73281a7b Mon Sep 17 00:00:00 2001 From: neminsheth Date: Fri, 12 Jul 2024 15:11:12 +0530 Subject: [PATCH 03/20] Add Firebase configuration and setup Android/iOS platforms --- .metadata | 25 +- android/app/google-services.json | 82 +++++ .../example/get_flutter_fire/MainActivity.kt | 5 + firebase.json | 1 + ios/Podfile | 44 +++ ios/Podfile.lock | 346 ++++++++++++++++++ ios/Runner/GoogleService-Info.plist | 34 ++ macos/Podfile | 43 +++ macos/Runner/GoogleService-Info.plist | 34 ++ pubspec.lock | 88 ++--- 10 files changed, 653 insertions(+), 49 deletions(-) create mode 100644 android/app/google-services.json create mode 100644 android/app/src/main/kotlin/com/example/get_flutter_fire/MainActivity.kt create mode 100644 firebase.json create mode 100644 ios/Podfile create mode 100644 ios/Podfile.lock create mode 100644 ios/Runner/GoogleService-Info.plist create mode 100644 macos/Podfile create mode 100644 macos/Runner/GoogleService-Info.plist diff --git a/.metadata b/.metadata index 784ce129..6eb54a17 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3" + revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49" channel: "stable" project_type: app @@ -13,11 +13,26 @@ project_type: app migration: platforms: - platform: root - create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 - base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: android + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: ios + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: linux + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: macos + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - platform: web - create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 - base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: windows + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 # User provided section diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 00000000..41ea6bf4 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,82 @@ +{ + "project_info": { + "project_number": "56053565487", + "project_id": "sharekhan-nemin", + "storage_bucket": "sharekhan-nemin.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:56053565487:android:f0e57a6359c84f97cb5eda", + "android_client_info": { + "package_name": "nemin.sharekhan" + } + }, + "oauth_client": [ + { + "client_id": "56053565487-rbb4oliis6kl4angm8pbu9dusnr5g4op.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyA42S1sFkyQAj1yZ1F4gUmE_pxec3h3YBw" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "56053565487-rbb4oliis6kl4angm8pbu9dusnr5g4op.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "56053565487-4hpe201r97dakjqqo6b0g3ltrabpfrmo.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.example.getFlutterFire" + } + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:56053565487:android:a10bbf5abca35d98cb5eda", + "android_client_info": { + "package_name": "sharekhan.nemin" + } + }, + "oauth_client": [ + { + "client_id": "56053565487-rbb4oliis6kl4angm8pbu9dusnr5g4op.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyA42S1sFkyQAj1yZ1F4gUmE_pxec3h3YBw" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "56053565487-rbb4oliis6kl4angm8pbu9dusnr5g4op.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "56053565487-4hpe201r97dakjqqo6b0g3ltrabpfrmo.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.example.getFlutterFire" + } + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/example/get_flutter_fire/MainActivity.kt b/android/app/src/main/kotlin/com/example/get_flutter_fire/MainActivity.kt new file mode 100644 index 00000000..018e286b --- /dev/null +++ b/android/app/src/main/kotlin/com/example/get_flutter_fire/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.get_flutter_fire + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/firebase.json b/firebase.json new file mode 100644 index 00000000..6643fe7b --- /dev/null +++ b/firebase.json @@ -0,0 +1 @@ +{"flutter":{"platforms":{"dart":{"lib/firebase_options.dart":{"projectId":"sharekhan-nemin","configurations":{"android":"1:56053565487:android:f0e57a6359c84f97cb5eda","ios":"1:56053565487:ios:3515d315addadb07cb5eda","macos":"1:56053565487:ios:3515d315addadb07cb5eda","web":"1:56053565487:web:bdde9b35d35b497ccb5eda","windows":"1:56053565487:web:5c35ec65002974a6cb5eda"}}},"ios":{"default":{"projectId":"sharekhan-nemin","appId":"1:56053565487:ios:3515d315addadb07cb5eda","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"android":{"default":{"projectId":"sharekhan-nemin","appId":"1:56053565487:android:f0e57a6359c84f97cb5eda","fileOutput":"android/app/google-services.json"}},"macos":{"default":{"projectId":"sharekhan-nemin","appId":"1:56053565487:ios:3515d315addadb07cb5eda","uploadDebugSymbols":false,"fileOutput":"macos/Runner/GoogleService-Info.plist"}}}}} \ No newline at end of file diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 00000000..d97f17e2 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 00000000..6afd14af --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,346 @@ +PODS: + - AppAuth (1.7.5): + - AppAuth/Core (= 1.7.5) + - AppAuth/ExternalUserAgent (= 1.7.5) + - AppAuth/Core (1.7.5) + - AppAuth/ExternalUserAgent (1.7.5): + - AppAuth/Core + - desktop_webview_auth (0.0.1): + - Flutter + - DKImagePickerController/Core (4.3.9): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.9) + - DKImagePickerController/PhotoGallery (4.3.9): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.9) + - DKPhotoGallery (0.0.19): + - DKPhotoGallery/Core (= 0.0.19) + - DKPhotoGallery/Model (= 0.0.19) + - DKPhotoGallery/Preview (= 0.0.19) + - DKPhotoGallery/Resource (= 0.0.19) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.19): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.19): + - SDWebImage + - SwiftyGif + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter + - Firebase/Analytics (10.25.0): + - Firebase/Core + - Firebase/Auth (10.25.0): + - Firebase/CoreOnly + - FirebaseAuth (~> 10.25.0) + - Firebase/Core (10.25.0): + - Firebase/CoreOnly + - FirebaseAnalytics (~> 10.25.0) + - Firebase/CoreOnly (10.25.0): + - FirebaseCore (= 10.25.0) + - Firebase/DynamicLinks (10.25.0): + - Firebase/CoreOnly + - FirebaseDynamicLinks (~> 10.25.0) + - Firebase/RemoteConfig (10.25.0): + - Firebase/CoreOnly + - FirebaseRemoteConfig (~> 10.25.0) + - Firebase/Storage (10.25.0): + - Firebase/CoreOnly + - FirebaseStorage (~> 10.25.0) + - firebase_analytics (10.10.7): + - Firebase/Analytics (= 10.25.0) + - firebase_core + - Flutter + - firebase_auth (4.20.0): + - Firebase/Auth (= 10.25.0) + - firebase_core + - Flutter + - firebase_core (2.32.0): + - Firebase/CoreOnly (= 10.25.0) + - Flutter + - firebase_dynamic_links (5.5.7): + - Firebase/DynamicLinks (= 10.25.0) + - firebase_core + - Flutter + - firebase_remote_config (4.4.7): + - Firebase/RemoteConfig (= 10.25.0) + - firebase_core + - Flutter + - firebase_storage (11.7.7): + - Firebase/Storage (= 10.25.0) + - firebase_core + - Flutter + - FirebaseABTesting (10.29.0): + - FirebaseCore (~> 10.0) + - FirebaseAnalytics (10.25.0): + - FirebaseAnalytics/AdIdSupport (= 10.25.0) + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - FirebaseAnalytics/AdIdSupport (10.25.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleAppMeasurement (= 10.25.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - FirebaseAppCheckInterop (10.29.0) + - FirebaseAuth (10.25.0): + - FirebaseAppCheckInterop (~> 10.17) + - FirebaseCore (~> 10.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/Environment (~> 7.8) + - GTMSessionFetcher/Core (< 4.0, >= 2.1) + - RecaptchaInterop (~> 100.0) + - FirebaseAuthInterop (10.29.0) + - FirebaseCore (10.25.0): + - FirebaseCoreInternal (~> 10.0) + - GoogleUtilities/Environment (~> 7.12) + - GoogleUtilities/Logger (~> 7.12) + - FirebaseCoreExtension (10.29.0): + - FirebaseCore (~> 10.0) + - FirebaseCoreInternal (10.29.0): + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseDynamicLinks (10.25.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (10.29.0): + - FirebaseCore (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/UserDefaults (~> 7.8) + - PromisesObjC (~> 2.1) + - FirebaseRemoteConfig (10.25.0): + - FirebaseABTesting (~> 10.0) + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - FirebaseRemoteConfigInterop (~> 10.23) + - FirebaseSharedSwift (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseRemoteConfigInterop (10.29.0) + - FirebaseSharedSwift (10.29.0) + - FirebaseStorage (10.25.0): + - FirebaseAppCheckInterop (~> 10.0) + - FirebaseAuthInterop (~> 10.25) + - FirebaseCore (~> 10.0) + - FirebaseCoreExtension (~> 10.0) + - GoogleUtilities/Environment (~> 7.12) + - GTMSessionFetcher/Core (< 4.0, >= 2.1) + - Flutter (1.0.0) + - google_sign_in_ios (0.0.1): + - AppAuth (>= 1.7.4) + - Flutter + - FlutterMacOS + - GoogleSignIn (~> 7.1) + - GTMSessionFetcher (>= 3.4.0) + - GoogleAppMeasurement (10.25.0): + - GoogleAppMeasurement/AdIdSupport (= 10.25.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - GoogleAppMeasurement/AdIdSupport (10.25.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 10.25.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - GoogleAppMeasurement/WithoutAdIdSupport (10.25.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - GoogleSignIn (7.1.0): + - AppAuth (< 2.0, >= 1.7.3) + - GTMAppAuth (< 5.0, >= 4.1.1) + - GTMSessionFetcher/Core (~> 3.3) + - GoogleUtilities/AppDelegateSwizzler (7.13.3): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Privacy + - GoogleUtilities/Environment (7.13.3): + - GoogleUtilities/Privacy + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.13.3): + - GoogleUtilities/Environment + - GoogleUtilities/Privacy + - GoogleUtilities/MethodSwizzler (7.13.3): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/Network (7.13.3): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Privacy + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.13.3)": + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (7.13.3) + - GoogleUtilities/Reachability (7.13.3): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/UserDefaults (7.13.3): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GTMAppAuth (4.1.1): + - AppAuth/Core (~> 1.7) + - GTMSessionFetcher/Core (< 4.0, >= 3.3) + - GTMSessionFetcher (3.5.0): + - GTMSessionFetcher/Full (= 3.5.0) + - GTMSessionFetcher/Core (3.5.0) + - GTMSessionFetcher/Full (3.5.0): + - GTMSessionFetcher/Core + - image_picker_ios (0.0.1): + - Flutter + - nanopb (2.30910.0): + - nanopb/decode (= 2.30910.0) + - nanopb/encode (= 2.30910.0) + - nanopb/decode (2.30910.0) + - nanopb/encode (2.30910.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - PromisesObjC (2.4.0) + - RecaptchaInterop (100.0.0) + - SDWebImage (5.19.4): + - SDWebImage/Core (= 5.19.4) + - SDWebImage/Core (5.19.4) + - SwiftyGif (5.4.5) + +DEPENDENCIES: + - desktop_webview_auth (from `.symlinks/plugins/desktop_webview_auth/ios`) + - file_picker (from `.symlinks/plugins/file_picker/ios`) + - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) + - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_dynamic_links (from `.symlinks/plugins/firebase_dynamic_links/ios`) + - firebase_remote_config (from `.symlinks/plugins/firebase_remote_config/ios`) + - firebase_storage (from `.symlinks/plugins/firebase_storage/ios`) + - Flutter (from `Flutter`) + - google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + +SPEC REPOS: + trunk: + - AppAuth + - DKImagePickerController + - DKPhotoGallery + - Firebase + - FirebaseABTesting + - FirebaseAnalytics + - FirebaseAppCheckInterop + - FirebaseAuth + - FirebaseAuthInterop + - FirebaseCore + - FirebaseCoreExtension + - FirebaseCoreInternal + - FirebaseDynamicLinks + - FirebaseInstallations + - FirebaseRemoteConfig + - FirebaseRemoteConfigInterop + - FirebaseSharedSwift + - FirebaseStorage + - GoogleAppMeasurement + - GoogleSignIn + - GoogleUtilities + - GTMAppAuth + - GTMSessionFetcher + - nanopb + - PromisesObjC + - RecaptchaInterop + - SDWebImage + - SwiftyGif + +EXTERNAL SOURCES: + desktop_webview_auth: + :path: ".symlinks/plugins/desktop_webview_auth/ios" + file_picker: + :path: ".symlinks/plugins/file_picker/ios" + firebase_analytics: + :path: ".symlinks/plugins/firebase_analytics/ios" + firebase_auth: + :path: ".symlinks/plugins/firebase_auth/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + firebase_dynamic_links: + :path: ".symlinks/plugins/firebase_dynamic_links/ios" + firebase_remote_config: + :path: ".symlinks/plugins/firebase_remote_config/ios" + firebase_storage: + :path: ".symlinks/plugins/firebase_storage/ios" + Flutter: + :path: Flutter + google_sign_in_ios: + :path: ".symlinks/plugins/google_sign_in_ios/darwin" + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" + +SPEC CHECKSUMS: + AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa + desktop_webview_auth: d645139460ef203d50bd0cdb33356785dd939cce + DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c + DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 + file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 + Firebase: 0312a2352584f782ea56f66d91606891d4607f06 + firebase_analytics: cc06e24d6a2343c44f845b3112143db72d10ef78 + firebase_auth: 5719ddc9f654b813405899480e84971bd8e61235 + firebase_core: a626d00494efa398e7c54f25f1454a64c8abf197 + firebase_dynamic_links: 525e9c1b702d2ed2d9b0dbd342eee1e15a75e62d + firebase_remote_config: 7b05c80210ab558c80f7a756681022b4ee98eea0 + firebase_storage: 5c0f552d6b27d621429d7fd16ebab4be94a3c954 + FirebaseABTesting: d87f56707159bae64e269757a6e963d490f2eebe + FirebaseAnalytics: ec00fe8b93b41dc6fe4a28784b8e51da0647a248 + FirebaseAppCheckInterop: 6a1757cfd4067d8e00fccd14fcc1b8fd78cfac07 + FirebaseAuth: c0f93dcc570c9da2bffb576969d793e95c344fbb + FirebaseAuthInterop: 17db81e9b198afb0f95ce48c133825727eed55d3 + FirebaseCore: 7ec4d0484817f12c3373955bc87762d96842d483 + FirebaseCoreExtension: 705ca5b14bf71d2564a0ddc677df1fc86ffa600f + FirebaseCoreInternal: df84dd300b561c27d5571684f389bf60b0a5c934 + FirebaseDynamicLinks: 12c9f5b643943e0565ed28080373f89cbcb914a3 + FirebaseInstallations: 913cf60d0400ebd5d6b63a28b290372ab44590dd + FirebaseRemoteConfig: 9f3935cefecd85d5b312192117f444957de24a75 + FirebaseRemoteConfigInterop: 6efda51fb5e2f15b16585197e26eaa09574e8a4d + FirebaseSharedSwift: 20530f495084b8d840f78a100d8c5ee613375f6e + FirebaseStorage: 44f4e25073f6fa0d4d8c09f5bec299ee9e4eb985 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + google_sign_in_ios: 07375bfbf2620bc93a602c0e27160d6afc6ead38 + GoogleAppMeasurement: 9abf64b682732fed36da827aa2a68f0221fd2356 + GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db + GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 + GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de + GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 + image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 + nanopb: 438bc412db1928dac798aa6fd75726007be04262 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 + RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21 + SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d + SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 + +PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 + +COCOAPODS: 1.14.2 diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist new file mode 100644 index 00000000..78e3a245 --- /dev/null +++ b/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,34 @@ + + + + + CLIENT_ID + 56053565487-4hpe201r97dakjqqo6b0g3ltrabpfrmo.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.56053565487-4hpe201r97dakjqqo6b0g3ltrabpfrmo + API_KEY + AIzaSyD12e9qHOj8ulb6cxP0DIWHW2tIeYzgKtc + GCM_SENDER_ID + 56053565487 + PLIST_VERSION + 1 + BUNDLE_ID + com.example.getFlutterFire + PROJECT_ID + sharekhan-nemin + STORAGE_BUCKET + sharekhan-nemin.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:56053565487:ios:3515d315addadb07cb5eda + + \ No newline at end of file diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 00000000..c795730d --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,43 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/macos/Runner/GoogleService-Info.plist b/macos/Runner/GoogleService-Info.plist new file mode 100644 index 00000000..78e3a245 --- /dev/null +++ b/macos/Runner/GoogleService-Info.plist @@ -0,0 +1,34 @@ + + + + + CLIENT_ID + 56053565487-4hpe201r97dakjqqo6b0g3ltrabpfrmo.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.56053565487-4hpe201r97dakjqqo6b0g3ltrabpfrmo + API_KEY + AIzaSyD12e9qHOj8ulb6cxP0DIWHW2tIeYzgKtc + GCM_SENDER_ID + 56053565487 + PLIST_VERSION + 1 + BUNDLE_ID + com.example.getFlutterFire + PROJECT_ID + sharekhan-nemin + STORAGE_BUCKET + sharekhan-nemin.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:56053565487:ios:3515d315addadb07cb5eda + + \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 877fc75e..d0fdd9cb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -117,10 +117,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: "29c90806ac5f5fb896547720b73b17ee9aed9bba540dc5d91fe29f8c5745b10a" + sha256: "824f5b9f389bfc4dddac3dea76cd70c51092d9dff0b2ece7ef4f53db8547d258" url: "https://pub.dev" source: hosted - version: "8.0.3" + version: "8.0.6" file_selector_linux: dependency: transitive description: @@ -181,26 +181,26 @@ packages: dependency: "direct main" description: name: firebase_auth - sha256: f0a75f61992d036e4c46ad0e9febd364d98aa2c092690a5475cb1421a8243cfe + sha256: cfc2d970829202eca09e2896f0a5aa7c87302817ecc0bdfa954f026046bf10ba url: "https://pub.dev" source: hosted - version: "4.19.5" + version: "4.20.0" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: feb77258404309ffc7761c78e1c0ad2ed5e4fdc378e035619e2cc13be4397b62 + sha256: a0270e1db3b2098a14cb2a2342b3cd2e7e458e0c391b1f64f6f78b14296ec093 url: "https://pub.dev" source: hosted - version: "7.2.6" + version: "7.3.0" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: "6d527f357da2bf93a67a42b423aa92943104a0c290d1d72ad9a42c779d501cd2" + sha256: "64e067e763c6378b7e774e872f0f59f6812885e43020e25cde08f42e9459837b" url: "https://pub.dev" source: hosted - version: "5.11.5" + version: "5.12.0" firebase_core: dependency: "direct main" description: @@ -213,34 +213,34 @@ packages: dependency: transitive description: name: firebase_core_platform_interface - sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 + sha256: "1003a5a03a61fc9a22ef49f37cbcb9e46c86313a7b2e7029b9390cf8c6fc32cb" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.1.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: "43d9e951ac52b87ae9cc38ecdcca1e8fa7b52a1dd26a96085ba41ce5108db8e9" + sha256: "23509cb3cddfb3c910c143279ac3f07f06d3120f7d835e4a5d4b42558e978712" url: "https://pub.dev" source: hosted - version: "2.17.0" + version: "2.17.3" firebase_dynamic_links: dependency: transitive description: name: firebase_dynamic_links - sha256: f704859abc17d99e74b47eaf47455b45a88ab7e2973f03e6130ff666b45fe11f + sha256: "47b8c8a8546d8a7f9000edb90848549f20b137d814ee7e0407b3d43b8445e282" url: "https://pub.dev" source: hosted - version: "5.5.5" + version: "5.5.7" firebase_dynamic_links_platform_interface: dependency: transitive description: name: firebase_dynamic_links_platform_interface - sha256: f86992605b50e2f0ce6c24993430affc98021da8d8a74d5596b7a2c84196c110 + sha256: "72e7810635f908ce060c5803c7acb29116c5b6befc73e90446c52722bc9506a2" url: "https://pub.dev" source: hosted - version: "0.2.6+33" + version: "0.2.6+35" firebase_remote_config: dependency: "direct main" description: @@ -269,26 +269,26 @@ packages: dependency: "direct main" description: name: firebase_storage - sha256: da76ca9c11d795c4bae1bd13b31d54bb9eb9ccbee7eb5f6b86b8294370e9d488 + sha256: "2ae478ceec9f458c1bcbf0ee3e0100e4e909708979e83f16d5d9fba35a5b42c1" url: "https://pub.dev" source: hosted - version: "11.7.5" + version: "11.7.7" firebase_storage_platform_interface: dependency: transitive description: name: firebase_storage_platform_interface - sha256: be17bfa9110a6429b40dd3760c755034079fd734aa1dd2476d5638ab780cc508 + sha256: "4e18662e6a66e2e0e181c06f94707de06d5097d70cfe2b5141bf64660c5b5da9" url: "https://pub.dev" source: hosted - version: "5.1.20" + version: "5.1.22" firebase_storage_web: dependency: transitive description: name: firebase_storage_web - sha256: "5219c20c0768a8e2ffedf0a116b7bc80ab32fcc6e2cbd50cbde14f8c4575c3f4" + sha256: "3a44aacd38a372efb159f6fe36bb4a7d79823949383816457fd43d3d47602a53" url: "https://pub.dev" source: hosted - version: "3.9.5" + version: "3.9.7" firebase_ui_auth: dependency: "direct main" description: @@ -351,10 +351,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" + sha256: c6b0b4c05c458e1c01ad9bcc14041dd7b1f6783d487be4386f793f47a8a4d03e url: "https://pub.dev" source: hosted - version: "2.0.19" + version: "2.0.20" flutter_svg: dependency: transitive description: @@ -417,10 +417,10 @@ packages: dependency: transitive description: name: google_sign_in_android - sha256: "7647893c65e6720973f0e579051c8f84b877b486614d9f70a404259c41a4632e" + sha256: d30fb34b659679ea74397e9748b4ab5d720720d57dcc79538f1b3c4a68654cb3 url: "https://pub.dev" source: hosted - version: "6.1.23" + version: "6.1.27" google_sign_in_ios: dependency: transitive description: @@ -441,10 +441,10 @@ packages: dependency: transitive description: name: google_sign_in_web - sha256: fc0f14ed45ea616a6cfb4d1c7534c2221b7092cc4f29a709f0c3053cc3e821bd + sha256: d606264c7a1a526a3aa79d938b85a601d8589731a478bd4a3dcbdeb14a572228 url: "https://pub.dev" source: hosted - version: "0.12.4" + version: "0.12.4+1" http: dependency: transitive description: @@ -465,18 +465,18 @@ packages: dependency: "direct main" description: name: image_picker - sha256: "33974eca2e87e8b4e3727f1b94fa3abcb25afe80b6bc2c4d449a0e150aedf720" + sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" image_picker_android: dependency: transitive description: name: image_picker_android - sha256: "79455f6cff4cbef583b2b524bbf0d4ec424e5959f4d464e36ef5323715b98370" + sha256: ff39a10ab4f48f4ac70776d0494a97bf073cd2570892cd46bc8a5cac162c25db url: "https://pub.dev" source: hosted - version: "0.8.12" + version: "0.8.12+4" image_picker_for_web: dependency: transitive description: @@ -489,10 +489,10 @@ packages: dependency: transitive description: name: image_picker_ios - sha256: cb0db0ec0d3e2cd49674f2e6053be25ccdb959832607c1cbd215dd6cf10fb0dd + sha256: "6703696ad49f5c3c8356d576d7ace84d1faf459afb07accbb0fae780753ff447" url: "https://pub.dev" source: hosted - version: "0.8.11" + version: "0.8.12" image_picker_linux: dependency: transitive description: @@ -625,10 +625,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d + sha256: "30c5aa827a6ae95ce2853cdc5fe3971daaac00f6f081c419c013f7f57bff2f5e" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.2.7" path_provider_foundation: dependency: transitive description: @@ -657,10 +657,10 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.0" petitparser: dependency: transitive description: @@ -673,10 +673,10 @@ packages: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" plugin_platform_interface: dependency: transitive description: @@ -798,10 +798,10 @@ packages: dependency: transitive description: name: win32 - sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 url: "https://pub.dev" source: hosted - version: "5.5.0" + version: "5.5.1" xdg_directories: dependency: transitive description: @@ -819,5 +819,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.3.4 <4.0.0" - flutter: ">=3.19.2" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" From c11c7942b2b5d736e96067db3c571dc789de75d5 Mon Sep 17 00:00:00 2001 From: neminsheth Date: Fri, 12 Jul 2024 20:40:42 +0530 Subject: [PATCH 04/20] Working: TODO: Ensure Reset Password has Email verification --- .../modules/profile/views/profile_view.dart | 192 +++++++++++------- 1 file changed, 116 insertions(+), 76 deletions(-) diff --git a/lib/app/modules/profile/views/profile_view.dart b/lib/app/modules/profile/views/profile_view.dart index c26d11c1..e5be965f 100644 --- a/lib/app/modules/profile/views/profile_view.dart +++ b/lib/app/modules/profile/views/profile_view.dart @@ -1,5 +1,6 @@ // ignore_for_file: inference_failure_on_function_invocation +import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_ui_auth/firebase_ui_auth.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -17,11 +18,11 @@ class ProfileView extends GetView { Color get placeholderColor => Colors.grey; Widget _imageFrameBuilder( - BuildContext context, - Widget? child, - int? frame, - bool? _, - ) { + BuildContext context, + Widget? child, + int? frame, + bool? _, + ) { if (frame == null) { return Container(color: placeholderColor); } @@ -31,84 +32,96 @@ class ProfileView extends GetView { @override Widget build(BuildContext context) { - return Obx(() => profileScreen()); + return Obx(() => profileScreen(context)); } - Widget profileScreen() { + Widget profileScreen(BuildContext context) { return AuthService.to.isLoggedInValue ? ProfileScreen( - // We are using the Flutter Fire Profile Screen now but will change in subsequent steps. - // The issues are highlighted in comments here + // We are using the Flutter Fire Profile Screen now but will change in subsequent steps. + // The issues are highlighted in comments here - // appBar: AppBar( - // title: const Text('User Profile'), - // ), - avatar: SizedBox( - //null will give the profile image component but it does not refresh the pic when changed - height: size, + // appBar: AppBar( + // title: const Text('User Profile'), + // ), + avatar: SizedBox( + //null will give the profile image component but it does not refresh the pic when changed + height: size, + width: size, + child: ClipPath( + clipper: ShapeBorderClipper(shape: shape), + clipBehavior: Clip.hardEdge, + child: controller.photoURL != null + ? Image.network( + controller.photoURL!, + width: size, + height: size, + cacheWidth: size.toInt(), + cacheHeight: size.toInt(), + fit: BoxFit.contain, + frameBuilder: _imageFrameBuilder, + ) + : Center( + child: Image.asset( + 'assets/images/dash.png', width: size, - child: ClipPath( - clipper: ShapeBorderClipper(shape: shape), - clipBehavior: Clip.hardEdge, - child: controller.photoURL != null - ? Image.network( - controller.photoURL!, - width: size, - height: size, - cacheWidth: size.toInt(), - cacheHeight: size.toInt(), - fit: BoxFit.contain, - frameBuilder: _imageFrameBuilder, - ) - : Center( - child: Image.asset( - 'assets/images/dash.png', - width: size, - fit: BoxFit.contain, - ), - ), - ), + fit: BoxFit.contain, ), - // showDeleteConfirmationDialog: true, //this does not work properly. Possibly a bug in FlutterFire - actions: [ - SignedOutAction((context) { - Get.back(); - controller.logout(); - Get.rootDelegate.toNamed(Screen.PROFILE.route); - // Navigator.of(context).pop(); - }), - AccountDeletedAction((context, user) { - //If we don't include this the button is still shown but no action gets done. Ideally the button should also not be shown. Its a bug in FlutterFire - Get.defaultDialog( - //this is only called after the delete is done and not useful for confirmation of the delete action - title: 'Deleted Account of ${user.displayName}', - barrierDismissible: true, - navigatorKey: Get.nestedKey(Screen.HOME.route), - ); - }) - ], - children: [ - //This is to show that we can add custom content here - const Divider(), - controller.currentUser?.email != null - ? TextButton.icon( - onPressed: callChangePwdDialog, - label: const Text('Change Password'), - icon: const Icon(Icons.password_rounded), - ) - : const SizedBox.shrink(), - ImagePickerButton(callback: (String? path) async { - if (path != null) { - //Upload to Store - String? dest = await controller.uploadFile(path); - //attach it to User imageUrl - if (dest != null) { - await controller.updatePhotoURL(dest); - } - } - }) - ], - ) + ), + ), + ), + // showDeleteConfirmationDialog: true, //this does not work properly. Possibly a bug in FlutterFire + actions: [ + SignedOutAction((context) { + Get.back(); + controller.logout(); + Get.rootDelegate.toNamed(Screen.PROFILE.route); + // Navigator.of(context).pop(); + }), + AccountDeletedAction((context, user) { + //If we don't include this the button is still shown but no action gets done. Ideally the button should also not be shown. Its a bug in FlutterFire + Get.defaultDialog( + //this is only called after the delete is done and not useful for confirmation of the delete action + title: 'Deleted Account of ${user.displayName}', + barrierDismissible: true, + navigatorKey: Get.nestedKey(Screen.HOME.route), + ); + }) + ], + children: [ + //This is to show that we can add custom content here + const Divider(), + controller.currentUser?.email != null + ? TextButton.icon( + onPressed: callChangePwdDialog, + label: const Text('Change Password'), + icon: const Icon(Icons.password_rounded), + ) + : const SizedBox.shrink(), + TextButton.icon( + onPressed: () => _resetPasswordEmailVerification(context), + label: const Text('Reset Password'), + icon: const Icon(Icons.email_rounded), + ), + ImagePickerButton(callback: (String? path) async { + if (path != null) { + //Upload to Store + String? dest = await controller.uploadFile(path); + //attach it to User imageUrl + if (dest != null) { + await controller.updatePhotoURL(dest); + } + else { + Get.snackbar( + 'Error', + 'Failed to pick image.', + snackPosition: SnackPosition.BOTTOM, + ); + } + } + }) + ], + ) : const Scaffold(); } @@ -121,4 +134,31 @@ class ProfileView extends GetView { textCancel: "Cancel", onConfirm: dlg.onSubmit); } + + Future _resetPasswordEmailVerification(BuildContext context) async { + final email = controller.currentUser?.email; + if (email != null) { + try { + await FirebaseAuth.instance.sendPasswordResetEmail(email: email); + controller.logout(); + Get.snackbar( + 'Success', + 'Password reset email sent. Please check your inbox.', + snackPosition: SnackPosition.BOTTOM, + ); + } catch (e) { + Get.snackbar( + 'Error', + 'Failed to send password reset email: $e', + snackPosition: SnackPosition.BOTTOM, + ); + } + } else { + Get.snackbar( + 'Error', + 'No email associated with this account.', + snackPosition: SnackPosition.BOTTOM, + ); + } + } } From 7f2c1667b5269469f69a30c10d2da6e9348511ee Mon Sep 17 00:00:00 2001 From: neminsheth Date: Fri, 12 Jul 2024 22:51:13 +0530 Subject: [PATCH 05/20] Working: TODO: Ensure Reset Password has Email verification --- .../modules/profile/views/profile_view.dart | 74 ++++++++++--------- lib/app/widgets/image_picker_button.dart | 35 +++++---- 2 files changed, 61 insertions(+), 48 deletions(-) diff --git a/lib/app/modules/profile/views/profile_view.dart b/lib/app/modules/profile/views/profile_view.dart index e5be965f..327a6193 100644 --- a/lib/app/modules/profile/views/profile_view.dart +++ b/lib/app/modules/profile/views/profile_view.dart @@ -92,48 +92,54 @@ class ProfileView extends GetView { //This is to show that we can add custom content here const Divider(), controller.currentUser?.email != null - ? TextButton.icon( - onPressed: callChangePwdDialog, - label: const Text('Change Password'), - icon: const Icon(Icons.password_rounded), - ) - : const SizedBox.shrink(), - TextButton.icon( + // ? TextButton.icon( + // onPressed: callChangePwdDialog, + // label: const Text('Change Password'), + // icon: const Icon(Icons.password_rounded), + // ) + // : const SizedBox.shrink(), + ? TextButton.icon( onPressed: () => _resetPasswordEmailVerification(context), label: const Text('Reset Password'), icon: const Icon(Icons.email_rounded), - ), - ImagePickerButton(callback: (String? path) async { - if (path != null) { - //Upload to Store - String? dest = await controller.uploadFile(path); - //attach it to User imageUrl - if (dest != null) { - await controller.updatePhotoURL(dest); - } - else { - Get.snackbar( - 'Error', - 'Failed to pick image.', - snackPosition: SnackPosition.BOTTOM, - ); - } - } - }) + ) + : const SizedBox.shrink(), + // ImagePickerButton( + // callback: (String? path) async { + // if (path != null) { + // String? dest = await controller.uploadFile(path); + // if (dest != null) { + // await controller.updatePhotoURL(dest); + // } else { + // Get.snackbar( + // 'Error', + // 'Failed to upload image.', + // snackPosition: SnackPosition.BOTTOM, + // ); + // } + // } else { + // Get.snackbar( + // 'Error', + // 'Failed to pick image.', + // snackPosition: SnackPosition.BOTTOM, + // ); + // } + // }, + // ), ], ) : const Scaffold(); } - void callChangePwdDialog() { - var dlg = ChangePasswordDialog(controller.currentUser!); - Get.defaultDialog( - title: "Change Password", - content: dlg, - textConfirm: "Submit", - textCancel: "Cancel", - onConfirm: dlg.onSubmit); - } + // void callChangePwdDialog() { + // var dlg = ChangePasswordDialog(controller.currentUser!); + // Get.defaultDialog( + // title: "Change Password", + // content: dlg, + // textConfirm: "Submit", + // textCancel: "Cancel", + // onConfirm: dlg.onSubmit); + // } Future _resetPasswordEmailVerification(BuildContext context) async { final email = controller.currentUser?.email; diff --git a/lib/app/widgets/image_picker_button.dart b/lib/app/widgets/image_picker_button.dart index d6e87ff4..9082249e 100644 --- a/lib/app/widgets/image_picker_button.dart +++ b/lib/app/widgets/image_picker_button.dart @@ -24,14 +24,14 @@ enum ImageSources implements ActionEnum { case ImageSources.file: return await getFile(); default: + return null; } - return null; } @override - final IconData? icon; + final IconData icon; @override - final String? label; + final String label; static Future getImage(ImageSource imageSource) async { final pickedFile = await ImagePicker().pickImage(source: imageSource); @@ -53,7 +53,6 @@ enum ImageSources implements ActionEnum { GetStorage().write(fileName, fileBytes); return fileName; - //result.files.single.path;//is causing issues for Web, see https://github.com/miguelpruivo/flutter_file_picker/wiki/FAQ } else { Get.snackbar('Error', 'Image Not Selected'); return null; @@ -64,28 +63,36 @@ enum ImageSources implements ActionEnum { class ImagePickerButton extends MenuSheetButton { final ValueSetter? callback; - const ImagePickerButton( - {super.key, - super.icon = const Icon(Icons.image), - super.label = 'Pick an Image', - this.callback}); + const ImagePickerButton({ + super.key, + super.icon = const Icon(Icons.image), + super.label = 'Pick an Image', + this.callback, + }); @override Iterable get values => ImageSources.values; @override void callbackFunc(act) { - if (callback != null) callback!(act); + if (callback != null && act != null) { + callback!(act); + } } @override Widget build(BuildContext context) { return !(GetPlatform.isAndroid || GetPlatform.isIOS) ? TextButton.icon( - onPressed: () async => callbackFunc(await ImageSources.getFile()), - icon: icon, - label: const Text('Pick an Image'), - ) + onPressed: () async { + var result = await ImageSources.getFile(); + if (result != null) { + callbackFunc(result); + } + }, + icon: icon, + label: const Text('Pick an Image'), + ) : builder(context); } } From 80a4999736033826ac14739ae509a802ccd95922 Mon Sep 17 00:00:00 2001 From: Anshuman Das Date: Sat, 13 Jul 2024 21:08:23 +0530 Subject: [PATCH 06/20] interim commit for merging PRs --- lib/app/modules/root/views/drawer.dart | 38 +++++----- lib/app/modules/root/views/root_view.dart | 12 +--- .../search/bindings/search_binding.dart | 12 ++++ .../search/controllers/search_controller.dart | 23 ++++++ lib/app/modules/search/views/search_view.dart | 24 +++++++ lib/app/routes/app_pages.dart | 9 ++- lib/app/routes/app_routes.dart | 2 + lib/app/widgets/login_widgets.dart | 30 ++++---- lib/app/widgets/menu_sheet_button.dart | 10 +-- lib/app/widgets/remotely_config_obx.dart | 71 +++++++++++++++++++ lib/app/widgets/screen_widget.dart | 30 ++++++++ lib/app/widgets/search_bar_button.dart | 3 + lib/models/access_level.dart | 2 +- lib/models/screens.dart | 59 ++++++++++----- lib/services/auth_service.dart | 9 +++ lib/services/remote_config.dart | 8 --- 16 files changed, 270 insertions(+), 72 deletions(-) create mode 100644 lib/app/modules/search/bindings/search_binding.dart create mode 100644 lib/app/modules/search/controllers/search_controller.dart create mode 100644 lib/app/modules/search/views/search_view.dart create mode 100644 lib/app/widgets/remotely_config_obx.dart create mode 100644 lib/app/widgets/search_bar_button.dart diff --git a/lib/app/modules/root/views/drawer.dart b/lib/app/modules/root/views/drawer.dart index 908d0223..ed5eb929 100644 --- a/lib/app/modules/root/views/drawer.dart +++ b/lib/app/modules/root/views/drawer.dart @@ -7,7 +7,8 @@ import '../../../../models/role.dart'; import '../../../../services/auth_service.dart'; import '../../../../models/screens.dart'; -import '../controllers/my_drawer_controller.dart'; +import '../../../../services/remote_config.dart'; +import '../../../widgets/remotely_config_obx.dart'; class DrawerWidget extends StatelessWidget { const DrawerWidget({ @@ -16,23 +17,26 @@ class DrawerWidget extends StatelessWidget { @override Widget build(BuildContext context) { - MyDrawerController controller = Get.put(MyDrawerController([]), - permanent: true); //must make true else gives error - Screen.drawer().then((v) => {controller.values.value = v}); - return Obx(() => Drawer( - //changing the shape of the drawer - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topRight: Radius.circular(0), bottomRight: Radius.circular(20)), - ), - width: 200, - child: Column( - children: drawerItems(context, controller.values), - ), - )); + return RemotelyConfigObxVal.noparam( + (data) => Drawer( + //changing the shape of the drawer + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topRight: Radius.circular(0), bottomRight: Radius.circular(20)), + ), + width: 200, + child: Column( + children: drawerItems(context, data), + ), + ), + List.empty().obs, + "useBottomSheetForProfileOptions", + Typer.boolean, + func: Screen.drawer, + ); } - List drawerItems(BuildContext context, Rx> values) { + List drawerItems(BuildContext context, Iterable values) { List list = [ Container( height: 100, @@ -67,7 +71,7 @@ class DrawerWidget extends StatelessWidget { } } - for (Screen screen in values.value) { + for (Screen screen in values) { list.add(ListTile( title: Text(screen.label ?? ''), onTap: () { diff --git a/lib/app/modules/root/views/root_view.dart b/lib/app/modules/root/views/root_view.dart index 2bbf228c..2173aa8d 100644 --- a/lib/app/modules/root/views/root_view.dart +++ b/lib/app/modules/root/views/root_view.dart @@ -5,6 +5,7 @@ import 'package:get/get.dart'; import 'package:get_flutter_fire/services/auth_service.dart'; import '../../../routes/app_pages.dart'; import '../../../../models/screens.dart'; +import '../../../widgets/screen_widget.dart'; import '../controllers/root_controller.dart'; import 'drawer.dart'; @@ -38,7 +39,7 @@ class RootView extends GetView { ? controller.openDrawer() : {Screen.HOME.doAction()}, ), - actions: topRightMenuButtons(current), + actions: ScreenWidgetExtension.topRightMenuButtons(current), // automaticallyImplyLeading: false, //removes drawer icon ), body: GetRouterOutlet( @@ -52,13 +53,4 @@ class RootView extends GetView { }, ); } - -//This could be used to add icon buttons in expanded web view instead of the context menu - List topRightMenuButtons(GetNavConfig current) { - return [ - Container( - margin: const EdgeInsets.only(right: 15), - child: Screen.LOGIN.widget(current)) - ]; //TODO add seach button - } } diff --git a/lib/app/modules/search/bindings/search_binding.dart b/lib/app/modules/search/bindings/search_binding.dart new file mode 100644 index 00000000..64cfa36f --- /dev/null +++ b/lib/app/modules/search/bindings/search_binding.dart @@ -0,0 +1,12 @@ +import 'package:get/get.dart'; + +import '../controllers/search_controller.dart'; + +class SearchBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => SearchController(), + ); + } +} diff --git a/lib/app/modules/search/controllers/search_controller.dart b/lib/app/modules/search/controllers/search_controller.dart new file mode 100644 index 00000000..f282fabb --- /dev/null +++ b/lib/app/modules/search/controllers/search_controller.dart @@ -0,0 +1,23 @@ +import 'package:get/get.dart'; + +class SearchController extends GetxController { + //TODO: Implement SearchController + + final count = 0.obs; + @override + void onInit() { + super.onInit(); + } + + @override + void onReady() { + super.onReady(); + } + + @override + void onClose() { + super.onClose(); + } + + void increment() => count.value++; +} diff --git a/lib/app/modules/search/views/search_view.dart b/lib/app/modules/search/views/search_view.dart new file mode 100644 index 00000000..f0c3c16c --- /dev/null +++ b/lib/app/modules/search/views/search_view.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart' hide SearchController; + +import 'package:get/get.dart'; + +import '../controllers/search_controller.dart'; + +class SearchView extends GetView { + const SearchView({Key? key}) : super(key: key); + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('SearchView'), + centerTitle: true, + ), + body: const Center( + child: Text( + 'SearchView is working', + style: TextStyle(fontSize: 20), + ), + ), + ); + } +} diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index 7269755d..068e4cb4 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; + import 'package:get/get.dart'; import '../../models/access_level.dart'; import '../../models/role.dart'; +import '../../models/screens.dart'; import '../middleware/auth_middleware.dart'; import '../modules/cart/bindings/cart_binding.dart'; import '../modules/cart/views/cart_view.dart'; @@ -28,6 +30,8 @@ import '../modules/register/bindings/register_binding.dart'; import '../modules/register/views/register_view.dart'; import '../modules/root/bindings/root_binding.dart'; import '../modules/root/views/root_view.dart'; +import '../modules/search/bindings/search_binding.dart'; +import '../modules/search/views/search_view.dart'; import '../modules/settings/bindings/settings_binding.dart'; import '../modules/settings/views/settings_view.dart'; import '../modules/task_details/bindings/task_details_binding.dart'; @@ -36,7 +40,6 @@ import '../modules/tasks/bindings/tasks_binding.dart'; import '../modules/tasks/views/tasks_view.dart'; import '../modules/users/bindings/users_binding.dart'; import '../modules/users/views/users_view.dart'; -import '../../models/screens.dart'; part 'app_routes.dart'; part 'screen_extension.dart'; @@ -150,5 +153,9 @@ class AppPages { ) ], ), + Screen.SEARCH.getPage( + page: () => const SearchView(), + binding: SearchBinding(), + ), ]; } diff --git a/lib/app/routes/app_routes.dart b/lib/app/routes/app_routes.dart index f3129d21..7391dcc0 100644 --- a/lib/app/routes/app_routes.dart +++ b/lib/app/routes/app_routes.dart @@ -30,6 +30,7 @@ abstract class Routes { '${Screen.LOGIN.route}?then=${Uri.encodeQueryComponent(afterSuccessfulLogin)}'; static String REGISTER_THEN(String afterSuccessfulLogin) => '${Screen.REGISTER.route}?then=${Uri.encodeQueryComponent(afterSuccessfulLogin)}'; + // static const SEARCH = _Paths.SEARCH; } // Keeping this as Get_Cli will require it. Any addition can later be added to Screen @@ -51,4 +52,5 @@ abstract class _Paths { // static const USERS = '/users'; // static const USER_PROFILE = '/:uId'; // static const MY_PRODUCTS = '/my-products'; + // static const SEARCH = '/search'; } diff --git a/lib/app/widgets/login_widgets.dart b/lib/app/widgets/login_widgets.dart index b8f2d8c1..35e7d153 100644 --- a/lib/app/widgets/login_widgets.dart +++ b/lib/app/widgets/login_widgets.dart @@ -7,6 +7,7 @@ import '../../services/auth_service.dart'; import '../../models/screens.dart'; import '../../services/remote_config.dart'; import 'menu_sheet_button.dart'; +import 'remotely_config_obx.dart'; class LoginWidgets { static Widget headerBuilder(context, constraints, shrinkOffset) { @@ -56,31 +57,36 @@ class LoginBottomSheetToggle extends MenuSheetButton { @override Icon? get icon => (AuthService.to.isLoggedInValue) - ? values.length == 1 + ? values.length <= 1 ? const Icon(Icons.logout) : const Icon(Icons.menu) : const Icon(Icons.login); @override String? get label => (AuthService.to.isLoggedInValue) - ? values.length == 1 + ? values.length <= 1 ? 'Logout' : 'Click for Options' : 'Login'; + @override + void buttonPressed(Iterable values) async => values.isEmpty + ? callbackFunc(await Screen.LOGOUT.doAction()) + : super.buttonPressed(values); + @override Widget build(BuildContext context) { - MenuItemsController controller = Get.put( - MenuItemsController([]), - permanent: true); //must make true else gives error - Screen.sheet(null).then((val) { - controller.values.value = val; - }); - RemoteConfig.instance.then((ins) => - ins.addUseBottomSheetForProfileOptionsListener((val) async => - {controller.values.value = await Screen.sheet(null)})); + MenuItemsController controller = + MenuItemsController(const Iterable.empty()); return Obx(() => (AuthService.to.isLoggedInValue) - ? builder(context, vals: controller.values.value) + ? RemotelyConfigObx( + () => builder(context, vals: controller.values.value), + controller, + Screen.sheet, + Screen.NONE, + "useBottomSheetForProfileOptions", + Typer.boolean, + ) : !(current.currentPage!.name == Screen.LOGIN.path) ? IconButton( onPressed: () async { diff --git a/lib/app/widgets/menu_sheet_button.dart b/lib/app/widgets/menu_sheet_button.dart index abd3873e..31649e4d 100644 --- a/lib/app/widgets/menu_sheet_button.dart +++ b/lib/app/widgets/menu_sheet_button.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../models/action_enum.dart'; +import 'remotely_config_obx.dart'; -class MenuItemsController extends GetxController { - MenuItemsController(Iterable iter) : values = Rx>(iter); - - final Rx> values; +class MenuItemsController + extends RemoteConfigController> { + MenuItemsController(super.iter); } class MenuSheetButton extends StatelessWidget { @@ -65,7 +65,7 @@ class MenuSheetButton extends StatelessWidget { //This should be a modal bottom sheet if on Mobile (See https://mercyjemosop.medium.com/select-and-upload-images-to-firebase-storage-flutter-6fac855970a9) Widget builder(BuildContext context, {Iterable? vals}) { Iterable values = vals ?? values_!; - return values.length == 1 || + return values.length <= 1 || Get.mediaQuery.orientation == Orientation.portrait // : Get.context!.isPortrait ? (icon != null diff --git a/lib/app/widgets/remotely_config_obx.dart b/lib/app/widgets/remotely_config_obx.dart new file mode 100644 index 00000000..a1ea63db --- /dev/null +++ b/lib/app/widgets/remotely_config_obx.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../services/remote_config.dart'; + +class RemoteConfigController extends GetxController { + RemoteConfigController(L iter) : values = Rx(iter); + final Rx values; +} + +/// final _name = "GetX".obs; +/// Obx(() => Text( _name.value )),... ; +class RemotelyConfigObx>> + extends ObxWidget { + final Widget Function() builder; + final X param; + final C controller; + final String config; + final Typer configType; + final Future Function(X) func; + + RemotelyConfigObx(this.builder, this.controller, this.func, this.param, + this.config, this.configType, + {super.key}) { + Get.put(controller, permanent: true); //must make true else gives error + func(param).then((v) { + controller.values.value = v; + }); + RemoteConfig.instance.then((ins) => ins.addListener(config, configType, + (val) async => {controller.values.value = await func(param)})); + } + + @override + Widget build() => builder(); +} + +/// Similar to Obx, but manages a local state. +/// Pass the initial data in constructor. +/// Useful for simple local states, like toggles, visibility, themes, +/// button states, etc. +/// Sample: +/// ObxValue((data) => Switch( +/// value: data.value, +/// onChanged: (flag) => data.value = flag, +/// ), +/// false.obs, +/// ), +class RemotelyConfigObxVal extends ObxWidget { + final Widget Function(T) builder; + final T data; + final String config; + final Typer configType; + + RemotelyConfigObxVal(this.builder, this.data, this.config, this.configType, + {super.key, required Future Function(X) func, required X param}) { + func(param).then((v) => {data.value = v}); + RemoteConfig.instance.then((ins) => ins.addListener( + config, configType, (val) async => {data.value = await func(param)})); + } + + RemotelyConfigObxVal.noparam( + this.builder, this.data, this.config, this.configType, + {super.key, required Future Function() func}) { + func().then((v) => {data.value = v}); + RemoteConfig.instance.then((ins) => ins.addListener( + config, configType, (val) async => {data.value = await func()})); + } + + @override + Widget build() => builder(data); +} diff --git a/lib/app/widgets/screen_widget.dart b/lib/app/widgets/screen_widget.dart index d80c9275..edb4ead1 100644 --- a/lib/app/widgets/screen_widget.dart +++ b/lib/app/widgets/screen_widget.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import '../routes/app_pages.dart'; import '../../models/role.dart'; import '../../models/screens.dart'; +import 'login_widgets.dart'; class ScreenWidget extends StatelessWidget { final Widget body; @@ -73,3 +74,32 @@ class ScreenWidget extends StatelessWidget { return null; //TODO multi fab button on press } } + +extension ScreenWidgetExtension on Screen { + Widget? widget(GetNavConfig current) { + //those with accessor == widget must be handled here + switch (this) { + case Screen.SEARCH: + return IconButton(onPressed: () => {}, icon: Icon(icon)); + case Screen.LOGIN: + return LoginBottomSheetToggle(current); + case Screen.LOGOUT: + return LoginBottomSheetToggle(current); + default: + } + return null; + } + +//This could be used to add icon buttons in expanded web view instead of the context menu + static List topRightMenuButtons(GetNavConfig current) { + List widgets = []; + Screen.topRightMenu().then((v) { + for (var screen in v) { + widgets.add(Container( + margin: const EdgeInsets.only(right: 15), + child: screen.widget(current))); + } + }); + return widgets; //This will return empty. We need a Obx + } +} diff --git a/lib/app/widgets/search_bar_button.dart b/lib/app/widgets/search_bar_button.dart new file mode 100644 index 00000000..14f60d37 --- /dev/null +++ b/lib/app/widgets/search_bar_button.dart @@ -0,0 +1,3 @@ +// This uses Remote Config to know where to locate the search button +// If on top, then it expands to title area on press in mobiles and is already expanded in web +// If on Nav Bar, the it initiates a SearchAnchor as a bottomsheet in mobile and like a drawer/nav column in web \ No newline at end of file diff --git a/lib/models/access_level.dart b/lib/models/access_level.dart index a7b89742..20b79252 100644 --- a/lib/models/access_level.dart +++ b/lib/models/access_level.dart @@ -1,7 +1,7 @@ enum AccessLevel { + notAuthed, // used for login screens public, //available without any login guest, //available with guest login - notAuthed, // used for login screens authenticated, //available on login roleBased, //available on login and with allowed roles masked, //available in a partly masked manner based on role diff --git a/lib/models/screens.dart b/lib/models/screens.dart index 24dee39f..eb89b5a5 100644 --- a/lib/models/screens.dart +++ b/lib/models/screens.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import '../app/widgets/login_widgets.dart'; import '../services/remote_config.dart'; import 'action_enum.dart'; import 'access_level.dart'; @@ -8,9 +7,13 @@ import '../../services/auth_service.dart'; enum AccessedVia { auto, - widget, //example: top right button - navigator, //bottom nav. can be linked to drawer items //handled in ScreenWidget - drawer, //creates nav tree //handled in RootView + widget, + topRight, + topCenter, + topLeft, + topBar, //bar below the main top menu bar + navigator, //bottom nav. can be linked to drawer items. left strip in expanded web //handled in ScreenWidget + drawer, //creates nav tree. persistant in expanded web and linked with nav icons //handled in RootView bottomSheet, //context menu for web handled via the Button that calls the sheet fab, //handled in ScreenWidget singleTap, //when an item of a list is clicked @@ -18,6 +21,7 @@ enum AccessedVia { } enum Screen implements ActionEnum { + NONE.none(), //null HOME('/home', icon: Icons.home, label: "Home", @@ -37,22 +41,18 @@ enum Screen implements ActionEnum { parent: HOME), PRODUCT_DETAILS('/:productId', accessLevel: AccessLevel.public, parent: PRODUCTS), - LOGIN('/login', - icon: Icons.login, - accessor_: AccessedVia.widget, - accessLevel: AccessLevel.notAuthed), PROFILE('/profile', icon: Icons.account_box_rounded, label: "Profile", accessor_: AccessedVia.drawer, accessLevel: AccessLevel.authenticated, - remoteConfig: true), + remoteConfig: "useBottomSheetForProfileOptions"), SETTINGS('/settings', icon: Icons.settings, label: "Settings", accessor_: AccessedVia.drawer, accessLevel: AccessLevel.authenticated, - remoteConfig: true), + remoteConfig: "useBottomSheetForProfileOptions"), CART('/cart', icon: Icons.trolley, label: "Cart", @@ -96,20 +96,40 @@ enum Screen implements ActionEnum { accessLevel: AccessLevel.roleBased), MY_PRODUCT_DETAILS('/:productId', parent: MY_PRODUCTS, accessLevel: AccessLevel.roleBased), + SEARCH('/search', + icon: Icons.search, + label: "Search", + accessor_: AccessedVia.topRight, + remoteConfig: "showSearchBarOnTop", + accessLevel: AccessLevel.public), LOGOUT('/login', icon: Icons.logout, label: "Logout", - accessor_: AccessedVia.bottomSheet, + accessor_: AccessedVia.topRight, + remoteConfig: "useBottomSheetForProfileOptions", accessLevel: AccessLevel.authenticated), + LOGIN('/login', + icon: Icons.login, + accessor_: AccessedVia.topRight, + accessLevel: AccessLevel.notAuthed), ; const Screen(this.path, {this.icon, this.label, - this.parent, + this.parent = Screen.NONE, this.accessor_ = AccessedVia.singleTap, this.accessLevel = AccessLevel.authenticated, - this.remoteConfig = false}); + this.remoteConfig}); + + const Screen.none() + : path = '', + icon = null, + label = null, + parent = null, + accessor_ = AccessedVia.singleTap, + accessLevel = AccessLevel.authenticated, + remoteConfig = null; @override final IconData? icon; @@ -121,10 +141,10 @@ enum Screen implements ActionEnum { final Screen? parent; final AccessLevel accessLevel; //if false it is role based. true means allowed for all - final bool remoteConfig; + final String? remoteConfig; Future get accessor async { - if (remoteConfig && + if (remoteConfig == "useBottomSheetForProfileOptions" && (await RemoteConfig.instance).useBottomSheetForProfileOptions()) { return AccessedVia.bottomSheet; } @@ -164,6 +184,12 @@ enum Screen implements ActionEnum { return list; } + static Future> topRightMenu() async { + return Screen.values.where((Screen screen) => + screen.accessor_ == AccessedVia.topRight && + AuthService.to.accessLevel.index >= screen.accessLevel.index); + } + @override Future doAction() async { if (this == LOGOUT) { @@ -171,7 +197,4 @@ enum Screen implements ActionEnum { } Get.rootDelegate.toNamed(route); } - - Widget? widget(GetNavConfig current) => - (this == LOGIN) ? LoginBottomSheetToggle(current) : null; } diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart index 8bf72aaa..a6bef783 100644 --- a/lib/services/auth_service.dart +++ b/lib/services/auth_service.dart @@ -5,6 +5,7 @@ import 'package:firebase_ui_auth/firebase_ui_auth.dart' as fbui; import 'package:firebase_ui_localizations/firebase_ui_localizations.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:get_flutter_fire/models/access_level.dart'; import '../models/screens.dart'; import '../constants.dart'; @@ -37,6 +38,14 @@ class AuthService extends GetxService { }); } + AccessLevel get accessLevel => user != null + ? user!.isAnonymous + ? _userRole.value.index > Role.buyer.index + ? AccessLevel.roleBased + : AccessLevel.authenticated + : AccessLevel.guest + : AccessLevel.public; + bool get isEmailVerified => user != null && (user!.email == null || user!.emailVerified); diff --git a/lib/services/remote_config.dart b/lib/services/remote_config.dart index 5d1145a5..cb2a8928 100644 --- a/lib/services/remote_config.dart +++ b/lib/services/remote_config.dart @@ -13,7 +13,6 @@ class RemoteConfig { } final FirebaseRemoteConfig _remoteConfig = FirebaseRemoteConfig.instance; - final List listeners = []; Future init() async { await _remoteConfig.setConfigSettings(RemoteConfigSettings( @@ -68,11 +67,4 @@ class RemoteConfig { bool showSearchBarOnTop() { return _remoteConfig.getBool("showSearchBarOnTop"); } - - void addUseBottomSheetForProfileOptionsListener(listener) { - addListener("useBottomSheetForProfileOptions", Typer.boolean, listener); - if (!listeners.contains(listener)) { - listeners.add(listener); - } - } } From 038adb5b345087423022a0e358fab8b9dd24010f Mon Sep 17 00:00:00 2001 From: neminsheth Date: Sun, 14 Jul 2024 01:02:39 +0530 Subject: [PATCH 07/20] Fix null check error in ImagePickerButton and improve null handling in ProfileView and ProfileController --- .../modules/profile/views/profile_view.dart | 48 ++++------------- lib/app/widgets/image_picker_button.dart | 54 ++++++++++++------- 2 files changed, 45 insertions(+), 57 deletions(-) diff --git a/lib/app/modules/profile/views/profile_view.dart b/lib/app/modules/profile/views/profile_view.dart index 327a6193..764954b8 100644 --- a/lib/app/modules/profile/views/profile_view.dart +++ b/lib/app/modules/profile/views/profile_view.dart @@ -92,54 +92,28 @@ class ProfileView extends GetView { //This is to show that we can add custom content here const Divider(), controller.currentUser?.email != null - // ? TextButton.icon( - // onPressed: callChangePwdDialog, - // label: const Text('Change Password'), - // icon: const Icon(Icons.password_rounded), - // ) - // : const SizedBox.shrink(), ? TextButton.icon( onPressed: () => _resetPasswordEmailVerification(context), label: const Text('Reset Password'), icon: const Icon(Icons.email_rounded), ) : const SizedBox.shrink(), - // ImagePickerButton( - // callback: (String? path) async { - // if (path != null) { - // String? dest = await controller.uploadFile(path); - // if (dest != null) { - // await controller.updatePhotoURL(dest); - // } else { - // Get.snackbar( - // 'Error', - // 'Failed to upload image.', - // snackPosition: SnackPosition.BOTTOM, - // ); - // } - // } else { - // Get.snackbar( - // 'Error', - // 'Failed to pick image.', - // snackPosition: SnackPosition.BOTTOM, - // ); - // } - // }, - // ), + ImagePickerButton(callback: (String? path) async { + if (path != null) { + //Upload to Store + String? dest = await controller.uploadFile(path); + //attach it to User imageUrl + if (dest != null) { + await controller.updatePhotoURL(dest); + } + } + }) ], ) : const Scaffold(); } - // void callChangePwdDialog() { - // var dlg = ChangePasswordDialog(controller.currentUser!); - // Get.defaultDialog( - // title: "Change Password", - // content: dlg, - // textConfirm: "Submit", - // textCancel: "Cancel", - // onConfirm: dlg.onSubmit); - // } + Future _resetPasswordEmailVerification(BuildContext context) async { final email = controller.currentUser?.email; diff --git a/lib/app/widgets/image_picker_button.dart b/lib/app/widgets/image_picker_button.dart index 9082249e..dbd504ef 100644 --- a/lib/app/widgets/image_picker_button.dart +++ b/lib/app/widgets/image_picker_button.dart @@ -24,14 +24,14 @@ enum ImageSources implements ActionEnum { case ImageSources.file: return await getFile(); default: - return null; } + return null; } @override - final IconData icon; + final IconData? icon; @override - final String label; + final String? label; static Future getImage(ImageSource imageSource) async { final pickedFile = await ImagePicker().pickImage(source: imageSource); @@ -44,14 +44,37 @@ enum ImageSources implements ActionEnum { } static Future getFile() async { - FilePickerResult? result = await FilePicker.platform - .pickFiles(type: FileType.image, allowMultiple: false); + if (GetPlatform.isWeb) { + // Web-specific file picking logic + return await getWebFile(); + } else { + FilePickerResult? result = await FilePicker.platform.pickFiles( + type: FileType.image, + allowMultiple: false, + ); + + if (result != null && result.files.isNotEmpty) { + final fileBytes = result.files.first.bytes; + final fileName = result.files.first.name; + GetStorage().write(fileName, fileBytes); + return fileName; + } else { + Get.snackbar('Error', 'Image Not Selected'); + return null; + } + } + } + + static Future getWebFile() async { + FilePickerResult? result = await FilePicker.platform.pickFiles( + type: FileType.image, + allowMultiple: false, + ); if (result != null && result.files.isNotEmpty) { final fileBytes = result.files.first.bytes; final fileName = result.files.first.name; GetStorage().write(fileName, fileBytes); - return fileName; } else { Get.snackbar('Error', 'Image Not Selected'); @@ -75,24 +98,15 @@ class ImagePickerButton extends MenuSheetButton { @override void callbackFunc(act) { - if (callback != null && act != null) { - callback!(act); - } + if (callback != null) callback!(act); } @override Widget build(BuildContext context) { - return !(GetPlatform.isAndroid || GetPlatform.isIOS) - ? TextButton.icon( - onPressed: () async { - var result = await ImageSources.getFile(); - if (result != null) { - callbackFunc(result); - } - }, + return TextButton.icon( + onPressed: () async => callbackFunc(await ImageSources.getFile()), icon: icon, label: const Text('Pick an Image'), - ) - : builder(context); + ); } -} +} \ No newline at end of file From 50e9bdb32eab0603f1d4fbaf29e376aa88d8030b Mon Sep 17 00:00:00 2001 From: Hardik Date: Sun, 14 Jul 2024 22:19:54 +0530 Subject: [PATCH 08/20] fix: bottom navigation current index --- lib/app/routes/screen_extension.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/app/routes/screen_extension.dart b/lib/app/routes/screen_extension.dart index aaf138b0..8b4cad98 100644 --- a/lib/app/routes/screen_extension.dart +++ b/lib/app/routes/screen_extension.dart @@ -110,13 +110,13 @@ extension ScreenExtension on Screen { extension RoleExtension on Role { int getCurrentIndexFromRoute(GetNavConfig? currentRoute) { - final String? currentLocation = currentRoute?.location; + final String? currentLocation = currentRoute?.uri.toString(); int currentIndex = 0; if (currentLocation != null) { currentIndex = - tabs.indexWhere((tab) => currentLocation.startsWith(tab.path)); + tabs.indexWhere((tab) => currentLocation.endsWith(tab.path)); } - return (currentIndex > 0) ? currentIndex : 0; + return (currentIndex >= 0) ? currentIndex : 0; } void routeTo(int value, GetDelegate delegate) { From c1fedc493808390135d2a9826cc9f532e30afa35 Mon Sep 17 00:00:00 2001 From: Hardik Date: Mon, 15 Jul 2024 02:32:33 +0530 Subject: [PATCH 09/20] fix: handled bottom nav index for path & query parameters --- lib/app/routes/screen_extension.dart | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/app/routes/screen_extension.dart b/lib/app/routes/screen_extension.dart index 8b4cad98..e334c4e9 100644 --- a/lib/app/routes/screen_extension.dart +++ b/lib/app/routes/screen_extension.dart @@ -113,10 +113,14 @@ extension RoleExtension on Role { final String? currentLocation = currentRoute?.uri.toString(); int currentIndex = 0; if (currentLocation != null) { - currentIndex = - tabs.indexWhere((tab) => currentLocation.endsWith(tab.path)); + // removinng '/home' from the start of the location + final filteredLocation = + currentLocation.replaceFirst(RegExp(r'^/home'), ''); + currentIndex = tabs.indexWhere((tab) { + return filteredLocation.startsWith(tab.path); + }); } - return (currentIndex >= 0) ? currentIndex : 0; + return (currentIndex > 0) ? currentIndex : 0; } void routeTo(int value, GetDelegate delegate) { From ad6278cf8bf256c1ac7923572f676d80a485b94d Mon Sep 17 00:00:00 2001 From: Aastha Gadhvi Date: Mon, 15 Jul 2024 15:55:43 +0530 Subject: [PATCH 10/20] added image picker --- ios/Flutter/Generated.xcconfig | 14 +++ ios/Flutter/flutter_export_environment.sh | 13 +++ ios/Runner/GeneratedPluginRegistrant.h | 19 ++++ ios/Runner/GeneratedPluginRegistrant.m | 91 +++++++++++++++++++ .../ephemeral/Flutter-Generated.xcconfig | 11 +++ .../ephemeral/flutter_export_environment.sh | 12 +++ 6 files changed, 160 insertions(+) create mode 100644 ios/Flutter/Generated.xcconfig create mode 100644 ios/Flutter/flutter_export_environment.sh create mode 100644 ios/Runner/GeneratedPluginRegistrant.h create mode 100644 ios/Runner/GeneratedPluginRegistrant.m create mode 100644 macos/Flutter/ephemeral/Flutter-Generated.xcconfig create mode 100644 macos/Flutter/ephemeral/flutter_export_environment.sh diff --git a/ios/Flutter/Generated.xcconfig b/ios/Flutter/Generated.xcconfig new file mode 100644 index 00000000..ebe7b0ca --- /dev/null +++ b/ios/Flutter/Generated.xcconfig @@ -0,0 +1,14 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=C:\Users\Aastha Gadhvi\Downloads\flutter_windows_3.10.5-stable\flutter +FLUTTER_APPLICATION_PATH=C:\Users\Aastha Gadhvi\get-flutter-fire +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_TARGET=lib\main.dart +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=1.0.0 +FLUTTER_BUILD_NUMBER=1 +EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386 +EXCLUDED_ARCHS[sdk=iphoneos*]=armv7 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=true +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/ios/Flutter/flutter_export_environment.sh b/ios/Flutter/flutter_export_environment.sh new file mode 100644 index 00000000..e9e43111 --- /dev/null +++ b/ios/Flutter/flutter_export_environment.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=C:\Users\Aastha Gadhvi\Downloads\flutter_windows_3.10.5-stable\flutter" +export "FLUTTER_APPLICATION_PATH=C:\Users\Aastha Gadhvi\get-flutter-fire" +export "COCOAPODS_PARALLEL_CODE_SIGN=true" +export "FLUTTER_TARGET=lib\main.dart" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=true" +export "TREE_SHAKE_ICONS=false" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/ios/Runner/GeneratedPluginRegistrant.h b/ios/Runner/GeneratedPluginRegistrant.h new file mode 100644 index 00000000..7a890927 --- /dev/null +++ b/ios/Runner/GeneratedPluginRegistrant.h @@ -0,0 +1,19 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GeneratedPluginRegistrant_h +#define GeneratedPluginRegistrant_h + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GeneratedPluginRegistrant : NSObject ++ (void)registerWithRegistry:(NSObject*)registry; +@end + +NS_ASSUME_NONNULL_END +#endif /* GeneratedPluginRegistrant_h */ diff --git a/ios/Runner/GeneratedPluginRegistrant.m b/ios/Runner/GeneratedPluginRegistrant.m new file mode 100644 index 00000000..11a184c0 --- /dev/null +++ b/ios/Runner/GeneratedPluginRegistrant.m @@ -0,0 +1,91 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#import "GeneratedPluginRegistrant.h" + +#if __has_include() +#import +#else +@import desktop_webview_auth; +#endif + +#if __has_include() +#import +#else +@import file_picker; +#endif + +#if __has_include() +#import +#else +@import firebase_analytics; +#endif + +#if __has_include() +#import +#else +@import firebase_auth; +#endif + +#if __has_include() +#import +#else +@import firebase_core; +#endif + +#if __has_include() +#import +#else +@import firebase_dynamic_links; +#endif + +#if __has_include() +#import +#else +@import firebase_remote_config; +#endif + +#if __has_include() +#import +#else +@import firebase_storage; +#endif + +#if __has_include() +#import +#else +@import google_sign_in_ios; +#endif + +#if __has_include() +#import +#else +@import image_picker_ios; +#endif + +#if __has_include() +#import +#else +@import path_provider_foundation; +#endif + +@implementation GeneratedPluginRegistrant + ++ (void)registerWithRegistry:(NSObject*)registry { + [DesktopWebviewAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"DesktopWebviewAuthPlugin"]]; + [FilePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FilePickerPlugin"]]; + [FLTFirebaseAnalyticsPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAnalyticsPlugin"]]; + [FLTFirebaseAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAuthPlugin"]]; + [FLTFirebaseCorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseCorePlugin"]]; + [FLTFirebaseDynamicLinksPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseDynamicLinksPlugin"]]; + [FLTFirebaseRemoteConfigPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseRemoteConfigPlugin"]]; + [FLTFirebaseStoragePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseStoragePlugin"]]; + [FLTGoogleSignInPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleSignInPlugin"]]; + [FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]]; + [PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]]; +} + +@end diff --git a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig new file mode 100644 index 00000000..75656d0a --- /dev/null +++ b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig @@ -0,0 +1,11 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=C:\Users\Aastha Gadhvi\Downloads\flutter_windows_3.10.5-stable\flutter +FLUTTER_APPLICATION_PATH=C:\Users\Aastha Gadhvi\get-flutter-fire +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=1.0.0 +FLUTTER_BUILD_NUMBER=1 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=true +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/macos/Flutter/ephemeral/flutter_export_environment.sh b/macos/Flutter/ephemeral/flutter_export_environment.sh new file mode 100644 index 00000000..accf248b --- /dev/null +++ b/macos/Flutter/ephemeral/flutter_export_environment.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=C:\Users\Aastha Gadhvi\Downloads\flutter_windows_3.10.5-stable\flutter" +export "FLUTTER_APPLICATION_PATH=C:\Users\Aastha Gadhvi\get-flutter-fire" +export "COCOAPODS_PARALLEL_CODE_SIGN=true" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=true" +export "TREE_SHAKE_ICONS=false" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" From 4125c29af001a127c664c9a62b732d39128627d3 Mon Sep 17 00:00:00 2001 From: Aastha Gadhvi Date: Mon, 15 Jul 2024 16:22:29 +0530 Subject: [PATCH 11/20] change readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index be46f067..7d403519 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Step 5: Add Guest User/Anonymous login with a Cart and Checkout use case [https: * delete unlinked anonymous user post logout -Step 6: Add ImagePicker and Firebase Storage for profile image +Step 6: Add ImagePicker and Firebase Storage for profile image (Done) * Create PopupMenu button for web [https://api.flutter.dev/flutter/material/PopupMenuButton-class.html] * BottomSheet for phones and single file button for desktops From 0d780efd1735326ca43c07e2d4b138bc4e4ffaec Mon Sep 17 00:00:00 2001 From: Hardik Date: Mon, 15 Jul 2024 21:54:19 +0530 Subject: [PATCH 12/20] added check if the path mentioned is for bottom navigation --- lib/app/routes/screen_extension.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/app/routes/screen_extension.dart b/lib/app/routes/screen_extension.dart index e334c4e9..ca90f303 100644 --- a/lib/app/routes/screen_extension.dart +++ b/lib/app/routes/screen_extension.dart @@ -112,10 +112,9 @@ extension RoleExtension on Role { int getCurrentIndexFromRoute(GetNavConfig? currentRoute) { final String? currentLocation = currentRoute?.uri.toString(); int currentIndex = 0; - if (currentLocation != null) { + if (currentLocation != null && currentLocation.startsWith('/home')) { // removinng '/home' from the start of the location - final filteredLocation = - currentLocation.replaceFirst(RegExp(r'^/home'), ''); + final filteredLocation = currentLocation.replaceFirst('/home', ''); currentIndex = tabs.indexWhere((tab) { return filteredLocation.startsWith(tab.path); }); From 1180864430117bf79d40a77fd92c441308c37639 Mon Sep 17 00:00:00 2001 From: Hardik Date: Wed, 17 Jul 2024 11:50:36 +0530 Subject: [PATCH 13/20] using full path for bottom navigation --- lib/app/routes/screen_extension.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/app/routes/screen_extension.dart b/lib/app/routes/screen_extension.dart index ca90f303..23877c7d 100644 --- a/lib/app/routes/screen_extension.dart +++ b/lib/app/routes/screen_extension.dart @@ -110,13 +110,13 @@ extension ScreenExtension on Screen { extension RoleExtension on Role { int getCurrentIndexFromRoute(GetNavConfig? currentRoute) { - final String? currentLocation = currentRoute?.uri.toString(); + final String? currentLocation = currentRoute?.uri.path; int currentIndex = 0; - if (currentLocation != null && currentLocation.startsWith('/home')) { - // removinng '/home' from the start of the location - final filteredLocation = currentLocation.replaceFirst('/home', ''); + if (currentLocation != null) { currentIndex = tabs.indexWhere((tab) { - return filteredLocation.startsWith(tab.path); + String parentPath = tab.parent?.path ?? ''; + String fullPath = '$parentPath${tab.path}'; + return currentLocation.startsWith(fullPath); }); } return (currentIndex > 0) ? currentIndex : 0; From 77a4d2cdb6ea044e0bd6c3ee4943a4d53b80e510 Mon Sep 17 00:00:00 2001 From: ITDhruv <120175094+ITDhruv@users.noreply.github.com> Date: Thu, 18 Jul 2024 22:14:22 +0530 Subject: [PATCH 14/20] implemented-googleOauth-and-todo11-crud-implemented-for-various-modules --- .metadata | 12 +-- android/app/google-services.json | 47 +++++++++ .../example/sharekhan_dhruv/MainActivity.kt | 5 + ...icial_Logo_of_Sharekhan_by_BNP_Paribas.png | Bin 0 -> 37950 bytes firebase.json | 16 +++ ios/Flutter/Generated.xcconfig | 14 +++ ios/Flutter/flutter_export_environment.sh | 13 +++ ios/Runner/GeneratedPluginRegistrant.h | 19 ++++ ios/Runner/GeneratedPluginRegistrant.m | 98 ++++++++++++++++++ lib/app/middleware/auth_middleware.dart | 10 +- .../cart/controllers/cart_controller.dart | 67 ++++++++++-- lib/app/modules/cart/views/cart_view.dart | 71 +++++++++++-- .../bindings/categories_binding.dart | 5 +- .../controllers/categories_controller.dart | 25 +++-- .../categories/views/categories_view.dart | 35 ++++--- lib/app/modules/login/views/login_view.dart | 4 +- .../bindings/role_requests_binding.dart | 11 ++ .../role_requests_controllers.dart | 33 ++++++ .../views/role_requests_view.dart | 36 +++++++ lib/app/routes/app_pages.dart | 10 ++ lib/app/routes/app_routes.dart | 1 + lib/app/routes/screen_extension.dart | 2 +- lib/app/widgets/login_widgets.dart | 4 +- lib/models/cart_item.dart | 29 ++++++ lib/models/category.dart | 26 +++++ lib/models/role_requests.dart | 23 ++++ lib/models/screens.dart | 5 + .../ephemeral/Flutter-Generated.xcconfig | 11 ++ .../ephemeral/flutter_export_environment.sh | 12 +++ pubspec.lock | 24 +++++ pubspec.yaml | 37 +++---- 31 files changed, 624 insertions(+), 81 deletions(-) create mode 100644 android/app/google-services.json create mode 100644 android/app/src/main/kotlin/com/example/sharekhan_dhruv/MainActivity.kt create mode 100644 assets/icons/Official_Logo_of_Sharekhan_by_BNP_Paribas.png create mode 100644 firebase.json create mode 100644 ios/Flutter/Generated.xcconfig create mode 100644 ios/Flutter/flutter_export_environment.sh create mode 100644 ios/Runner/GeneratedPluginRegistrant.h create mode 100644 ios/Runner/GeneratedPluginRegistrant.m create mode 100644 lib/app/modules/role_requests/bindings/role_requests_binding.dart create mode 100644 lib/app/modules/role_requests/controllers/role_requests_controllers.dart create mode 100644 lib/app/modules/role_requests/views/role_requests_view.dart create mode 100644 lib/models/cart_item.dart create mode 100644 lib/models/category.dart create mode 100644 lib/models/role_requests.dart create mode 100644 macos/Flutter/ephemeral/Flutter-Generated.xcconfig create mode 100644 macos/Flutter/ephemeral/flutter_export_environment.sh diff --git a/.metadata b/.metadata index 784ce129..8973d6ca 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3" + revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49" channel: "stable" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 - base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 - - platform: web - create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 - base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: android + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 # User provided section diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 00000000..29c901db --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,47 @@ +{ + "project_info": { + "project_number": "64647061409", + "project_id": "sharekhan-dhruv", + "storage_bucket": "sharekhan-dhruv.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:64647061409:android:a52c96120dd0ace789f5e0", + "android_client_info": { + "package_name": "com.sharekhan.app" + } + }, + "oauth_client": [ + { + "client_id": "64647061409-aoongerjfjrn2pmgtoukvknpnpl12lgo.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.sharekhan.app", + "certificate_hash": "198480b6d8094d0a7c9fb03e1874e270745c4c2f" + } + }, + { + "client_id": "64647061409-rtduueba7plkbr9uaqid0vuusjn3a7fk.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCSA4qFzD3_qsxw5WnbX0rJPwmtGWmfZ0k" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "64647061409-rtduueba7plkbr9uaqid0vuusjn3a7fk.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/example/sharekhan_dhruv/MainActivity.kt b/android/app/src/main/kotlin/com/example/sharekhan_dhruv/MainActivity.kt new file mode 100644 index 00000000..a235e9f2 --- /dev/null +++ b/android/app/src/main/kotlin/com/example/sharekhan_dhruv/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.sharekhan_dhruv + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/assets/icons/Official_Logo_of_Sharekhan_by_BNP_Paribas.png b/assets/icons/Official_Logo_of_Sharekhan_by_BNP_Paribas.png new file mode 100644 index 0000000000000000000000000000000000000000..5e50fd3e934a7967dd6fb9ee2baa675f5f6f761c GIT binary patch literal 37950 zcmXt8by!qi6W#!pM(OU5?vf6rLqMciSh~Ao0RfSe?vO^hn^js;K$dRl?hyF={qa5b zxpn3~_sqQa%$$2>-Y89V1sqIrOaK6YqogRW1pt7O{$01Bqy0O2bT@wdcLKU=DaZn< z$0_&z-JqGPD98hz|J(ArO80_-_XSGP6nlH9|a;)ZRcgAXxZdZbufa zP5^)wpd>G&(i8BWPWYToQ47dZ9uZVetfouv~OX00)o^P-Q&FSc|$vJShc`0yt&e zd5XcB9q}fU;E|cD8F*T}n@sRHFhvLj+?3pZbbF0K?&#hAY!6r)*ufL)N07T~LHgP0%S+A@+@uL*l30c7A6 z@V#rkLFh2PYAUoIC5bu%D}dbRU4)?D%TtfFpE)m7sWAeGEU8d-a$)AdY984Jnq;Z|huq5$z%t4D>EspB$DZqml8h>E@wPoy^Gr}~{R;DOM2 zs406%OF1Klk7TZcNc{c>ICJ(%7HYbddTdh9EcxM6B|v}0!QFUu6`k#%Mjx}D#6{nA zQ>#9)I2r0zx=zWL*(C!lf5_>Em6bRfTI`s8P4D<;m6lZ<2Y^`EuypTC4IkiAZ?PR> z@xk#|qRVKHU$r(cqI96$a8pZ}l_?!t36bF?6ePVy#uyO#seZ=R3g8M^;|GmaCRfR^eXLa;0^zh)B z`mkz|l~M<1fHh=qvl!>0fVhz*l|}xa!hCNcI2FYyMaT*M2E1T4vF>J#FC;R|{N8}* zI}0E{|F&RSfq3PD=R&ZpxIw>Winu}$L;ceR-)#h+TNxeaZiFrNCdonT5wb#++0%?0%y zZ%h^bfeNi>R^fyP@G%>@D9!EK_WAVfzgM5=b|}03_k($b^#U7AdBAizZ*;@jO z#CwUo6>Wr7+?YD;*>`VUO#TyNsZyhLf-hM`z}bOJF0xei=zzD12LXvwdmw2qa3J%k zGwlhmJJ~8?L9{)}QGj*D^`D7+9RSM~Mk~Xt8U_EAwnN>PGCkcELTHl8V0SXUs?bkX zS{l^l!3i{cfZJG*n*kQ1_}UDK|1PMcb)^M#Tl=G!cE@wVhbZt+2Q>X}RO_?)J@!*- z=gD;!hU{^`#je_m*L>OjIh|@Z?rpxB*Y^pud*Y-!<1oa;j_AEK?61w>vn1*qkq8EX z!u+3DW+Ak$)~J+!0P*9MF@}M2Q8kZ-)=UU?O2p5(ij%)0iU2hb$+s%bNIeG60zp0T z{ud>F8P%JI3CQDQurvvDU&qT#jY~X4F1kh)=`$h!ye{h)R>Sl=o0nsX>2Pc#?Gw--7_WTW3lD#(~arz4?MU-URH) zAFbP#Modtb5m?zjgP6Dzh}>(vt>8sbo1_P{I{&^{Bba$7q#I%p&jZ^9MRzljePUU@ z1(Y4!kdBc393R;au{t}{o(mQ4TEma|Z>WNJeMP@bi>=;fA3KFC(D$&!*J;HYJ1uFy zZY!N4PSg4+z*8O%@(myru(Cvz6Z};Vm`sp4B|CHCcEd2TF5}R$ZT;74G9fg@JUALH zWw~A|2i~Dqo(Cn>d5bXkC%XJLOY$4EmXR*G_!6Ajz6L`mE$THEn0M9P2pVUV1Ul;` zr1=s4uDZAT7jS-#0(!({Qkds5sBqRi%xSb`8$Op+zf)!vV@-FseMDD{cl;5>Szz#G zSbq^S1_VPeDiZCNHG?iQsFz*?u-mI(*qUgYJS@`Rmai)0l$58otyBNuBqnV0{=7W% zSGDoM8ztMtgZd7AxQ}hL3Tr{H-q5+u?o>^Jk$qz6mQaTgB-CPzHpX}2 zO#{_GW0Y@X)L!PD+}pr~|{ zv$bY7eiYaJ3#jur_vg2ibXC5+c+PD{;Qx0rz5SvHa+a z#%>vb*%*8d2*$#G|A~TX8GVV3|1kSG&Z(b>6AhAFV1Ol{`a~lBcnq4*uO=x-{QT#c zb>A(*U$gIhV;~bWFqD>3IX=R0PYIPdzJ%F-ACt%2V`5d-X#F>%Nxe8E46s3P6*2xj zQcPsh=k3$9I)ZD54rwVz!IkzPUc)OT-)vn?Ftbga$P2EMx}C7a>espdgB7f^0njKQ zD4_=$2_HG69;ii8sfnWfC!&XHu_OM@_6z1%UQo?rB5)8?U5+2mh4kwDulnT&=2n{r zs#hiN4`Jx(VP-j~>3uVoOL3z6&H?4dy)S-PVy{%o>ztD`+Yu#5Ft{KsM zX29g$5k-kQUQ=mHdNxKD{nZ(nxU-u|h58Na$F}%&gfa zirO46X`OxTFKP|Vdd}$^pE}rU5l2<~O`+LS8_L1qP;O1TteQd6em_HY+Jhg_n%>f| zlztzX^rle`tdM@h3gSDW8xfRVuNci^9J4L-YNbff$S_1LeM<2I7zs!N3!5u5IUb!w z>Dy4{g{Ob4kO=vmj#5vA(0aJ>o&AK@7^+mh@V$nt<_IC=|Li?q*M_RiwB`quU=Dyy z<66xeMxk^7Ao`EC9mFleYSr@|5tGcGeeuEjxfbpld-9BE{p|GY+z75ZsVrLZUR{gu_ z4}#7M=Lql63^@z>7A6>3QWCsaX{--1vc5XTTw}9$7rMNK;^-TSIUpBTu6L-fcli8I zaO`U_u=P=i4n}P^Gb>|N#)wy~j>d9untdD~x_zRv&75^!Rll068ikzaV!o+El2S#c zUP7aroSR%bhZkBY4lgK%c_WK@=J9P&D1GBKnN%b(C~q|5l@8J2c4PySz7y8DWZYOINXoipKOB{tC1V>WJ&X3 ze&?-KdE#+w`QSZtO=L}SU^nvfvGCq+tw{-ZAv{Tyg;kWwhr~sLB<8RiQGf`GksKd$5h&pYS_w;L}EOG4*Aqt`5pw=FJ1&#)3Y;U^j z`*z9(=#v~mqhZnZgO}H+uf}Ifnp(t(yu0%uGSguXd?#rrxcm~Dp!?-?==@Yf1;}TnJ zM9uA`GK2d2M~Ubdep)<~e}8h2-8Mu`6@j~F7M2M!j+UCf%}SQOrGD72)|W?Qun&OU z-yd=Ei8{+3#3DmT023&pLXKe{eI9=%dx5Ng1xpHJ7TNH8}r5Hyr)t5DL7GdZ}AfxwcHTDMMGNL+?n z12xbjaEof@Z3Z0PvuP8dydp~2l!+JK9O4eCnIHS6ruj;A@fAsa#;>n`Zu*m>`~zb3 zUE$0V%;j)ggPk4uY3Nt&9m(PWXOGS&DxdE8OG}xHn=ly0NpX4V&J>8 zvTdguI@@+D4g=7MU?M)ZqxGS_4$vHN^S7L-Lps5|hSEKbF_D)|<56hh>B+?Dl9~s_ z(?|dfWE_F=QeM&=d*S=k4LVnDWqG{x)q!_On?ml{M~cQ8D?!~D=kLIb z#`gu(VMV}A{>NWmVI4azM-PB!$oae#Z3YNM_J9tB+I-NJ(>_4(lnu*E{czk*5mYZV&aAyHFfgeWW~pMNjn2CowE662n2Row6Xucs>psZsGB!x9RU!nU6bGCj~_Y z9Sb~bCw}zVdf7WccHEiyknS?WvDp|nJGCj#y%HW6GH_5!q<=}Jf~et%cnu~ z<@(J=$fs}3w3ih$tvyuV2T-AR;@!TfsD6J4G1AZ$CB%8vdO_I@>Le9u<#+aBo>x6O z4H^9QS-k>^FM;Mr-y6y6$`7o0{cT+lV+DumnGfOv9D0UsAlrGHJM_AgQKERQhjz}C zKc2;q4KusAz)_d;Y_2yt*dS(c%Xv;7$`WyMqrgNI+L?{?(Sy?k8q3%t?HI^v^_YvO zlF}O87qYV80W;cXtj=YpR@CY!Ql12CccV`%PjA->(N0_8AwJc}wzy~Pdv#S=^|&qh zA=k=yWYOavR78sm-c3#3q(M~pVo|Mz357>vTeHE2`udbJ!b1%3J8gy?!|M%5Ua8Fe zi@E-mFhFX+Vt%6N63pF%bo;B*sy?(GLAk*3o}BRuqwBsAY7+FCV6BZeOlz_71k^Sj z7{gg!xj>h`w#3vw|81UjW4^2td(vvsmznyFsk}t52^B}SpSbKQDaP3?4~7Uf=g%vj zH4jyQvoJ9fCAkCO9^*T-UmhvM=n4!IrLBQ~!%;f?YN`YQegPjp`N;GhXyK1^M21IZp1m3`tLHBqq+LR+QFnn&FfJau! zuVO2(bAk-*4vpjUkYge4e%i*|B^@GOBcm+^cHGA6!s|PJt={>Tl8wg_@1Nd~^uvpc1W`1SJ(7}3C9uf%COms6hk0BK zTCGeyM+4(9g{)AwIkyMZ?rJX>HuvI! zYCLRphm$42c{al1-$NlOB*w+T-^Jndt>S)TtoaC14 z5%=hJ(MSApbBYFMu&QV%79V;|!{`!pU!v%V1nE4MpVl^_T`7^CH4x_`9K_>^V){w= zT2h1OID1fOQd^Cqa{R{m5a0D@!y!fvPgN!GiGsjtc01j|x6dy79^eoAY-{%(6nm&o z2N!?zbfZ1R$u{E;{|Pt+CV1avENFgGv->uYP?hOdBLZ|7Ju8<&Mq*I-c*bNb$*Opn zv%OjUVIaQhWCtOR74XB-RYoYb-+Lfa9z_o3BS~d`Fl7Z~Rrz5@j&xj-NtJ#N;N2!| zQrRS*?nn+N?Fc?24N{tU&EoHoh)S9g3bExnl}U(f)gUp~U*z5H`!M(dVxLH^v4sBi zvL_aD0*lq!AlFuDESiN~gv`=r-Px^C`CiBc@f=L6*%AmOh{Rv5(pY^TH5c|L20=L| zY6cnq=H%3jDVnt%^qhpSIyWgC_%JeY%g=s6LzFwK*?yE>d!=eKlN~y*H24@%-y{fn zYizHJf8;cJ=N(aB2^5{yY1~afDyAVry8C1C1VFn2&}Sp~1f%kF^z4N2-LnzMR@H)Yez0k-i!D*^TYv)ZIR7?vV#L7Z--Bazf~DRXAU| z3hl#0Hda1%BDQQMHNbM~$c`tpvly*m8wOcV!$9eFRQb;7`YBQ8UDlkT*G*9wLSqbY zZZkVKe6DQjhn$y_!sg};ZeFq&qwk}aNfoVW!yR0M``{eww~jafWeHRfrJ-xn<}qF7 z#~b3GSf7|U!e#jxq3-4^nCkfJJciwsHajckw1K8`Q7J>OuSHFGW7c4H7?mQelM{S@zTJH{Y`x^5Sg4GnT2E?n2wF11lu)#0Qvbh8EqeLP~lz zbOS(qm4c6R#S$SOCh-^3_tKdevzDDF0T1M#C8C%P6cGn1^DbdBQzwIzN$8c1d zq0_4Lk&O!Kx;ZD*OzE)FTEe{R-7wFo*S_81H6=V0{8HsEM?xL^(Gs%jiZ zp->Rep=y1;3v>MVPRFeX`ZI!x7%h?&_DbH)p80i1V%dmf!hRn_1C%>+Tm58f`Y-*l zx~NwpZ+)=Y`@>i1>mLigft zTN(piymEnws9c~?n#iic;>Id|{Ic2}B7FJOg58B_=r=+zzlD59PhphTSYk1#o0gr> z7^a6J(dWeFULq;35^ukgE%kl0?QhY=ResdC8am33Io^(WCW*Lsr8PP5_zIb4n?x|+ z-b~eJe{3~|fzEd>c>i^wwnj}M1IL~@-0|~r zT5q)rPo70eJ~A_QCf_1~{78<|FqyQ+lfO(o6bN-)Oe(=6^bFc?P38=5#GTiV4eue^dOiSc7@YF|N=NAUZ1J#OFe@ z40+Z`Kg=zZ9dJyiw^Rz)!;?7to9R0WC*3~QE$E0%-fa2g-~mNPr9N=MUYbv|AstYR zQ2kQK{JW4&AiIM?PBdM8aL>@w5#`!JkXM1yJ=_|moYgq&BCxmlI>9}pLHZfRO`9Ta zpFV*hWH3x}ygEblY{Bb$4<}k~q-~E#ZVe6=>Pia%dnRL8y2E(sAl16&UyDH12|lJq z4{?c-7OKeie18oUN65W6P`QGz_jZ_{#LI>OOv825P2gBO)eg*0~tx~n%MpSO$s!Dx#6iH9dR1N%Br%XZ7!I#)p#4!X+>J zwCt1ar>UlG$`K0b5}!`ML&hwx-Cs#!-{T6~%%H=F55f$eJ5@N)?=WgbSH3mqEA>g1 zX50sq{FP|H2AcwWy$m8e*|6p>_0z(#asFan%C612sI;X%Py$5JFIVA%fI)KDY<0_w zM^{q51~YQ)E?G31Dn4SaRrMwSWr)JzWUAm&a$-Doe*#g+J1Cw*UW2*Y0@wa~qGZ*J z4E>9J%e8CD(U2vgD{F99+`dF|$udJAY~I}KPeTzx6B0n(ia0{QlL@;^)6OFflf-hz z4%WMG_lz4xTg&ibIPR;v+evoM+xR5ZwM~Y2_9}BGQZ@heFcuwtt!}W7^oj!(yZK8- zC9Anrcsk%8Rx&SaFjIBmT!Ioz7av*|1_(=PADdQh8prox@JK;xQu|a_kE&lanV;F( z_@*%WzMueFnU@r%sJr{>Yiep?ivAZI0B;Jf#MQ0VcT}i<7cQ~mn=aHctgD;j?oVQ4 zlMbfbsWL6PnW(5S?-dgYvFBpK0k)ryo=b9)9CIs~A4!K!da)RZ(}+~i)?5}J&OEG& z@@-bgnpj=Y>oOBK>jXYzFj)9vmlPx8u@go)Hq#dzOU?Y!x)HS^^4M}(+a5GSh4Zd| z=%-l>Osu~H*BR<)hnDetq}7p46hjzQuTw<}HUdiem9PX+O{aU@t*9t1BsT(e1_Y8zfe4-9LVZZLm?oUgKMEQrh!urFMi{U<(YGx3u99;QD+u_Fw%$ zCY#J*Sef&Tr+d=P;Pa`psYHa}dT> z?Dj3&EZ*|sML^pbru#5;XOHOXix5nF>9A~P{nl0DL1y7$zTP4sXE=Ekbn^0VasCA2 za)ndNNeKH>Cz6?#5l&?Nb_>RXp+^;0{1#wYR^zJRb;c;iB|!Z!Xt(Fb?_KlUepmf} zF2JCGb!&;9A+$6@i({{AWkj&}wcAoOV3=X1 zMr)|6QLK~xB|Uv_Yzpl_zdEL`&NcYyO@X2e4vg8Jq7+0=YuU<;l*m!S@P!%CMVIO( z$AjvVuMJ)_Yp0FXuhk((#FJ_BIIR>~sFLOe2X_Wp;Rd{H&883UavXJ0ga$M@90vcu zsqOR^nAXoZy2-26kO3@1Dka>-`l;=cI_Ro`OdVQ zR*D+w3Z26(&TkTiiiOVHmttiE!u#eSNj@2o{0(SZyVCCXKa86R6MA6kQRBxy08T(j z3p=x)ty#RWOjEx-cTk`vl|DQ3%ulk%rxX($xoTyTtfU{=OAQTf*0^3-jN_5usP?(r zy{o7JI2X=s4rDsCz|Vg)E___U-u6Yv98#XYfg1a0Ut1yc2bH#WpmQgi*6%+B#OWT9 zKUk9tR?+zA48S(tk^vtrfTNl})z2j-S4+bAM1-R@Rj=Uc+lKggg}oCFGu-1Sx*kvS z?fPp?h!a2b82NNlK=qHyn4YgHBv)!{?oJ45?;hJ4d&iox7L0R$sEgL?olJGGgwOsv zAM1#B);UBNj1|4ia{jkcQ$H^G-pUWS-5P92l46^*_lA9Qo%RI!Az75x{&lqDzCNDM zeUl0Jyrb0>0qIMdhfonV&=#&+!|ConeMY(5Xnb(9^2UKz&wYvc0N`+{LonAC+za`jYUi50 zizI12Afz3`L_<}r8X5WQS#4k;xLYtkSJz%0Yk1=8VKv(T`b(%yBm9Ne!3`U?nc{;w zhXLE-7yOZB`VMHEQM~eWEYxS3@sw`#oi7Bo8l@H`%EsBQ>%ZZSps_~mE~!aSNJKX* z6ybFMFTC)*&fWG|-gHZhECTYSSa?Ly-4r2eB*%d6S>vo(#$d37Fg9H7v6qa{Of8i< zidX*kwy&DG(!VRFDE-4$FAD-!7J-?v=2j>=`ilUttioCkMA}uxt zjK?v`=5b+61G@uiti%pnS;=$ z@+Lk0`xJ!9w|zZjOvSbj>qhrj9jB})`Usk6Vhr91!1_vk45_TES5sR((_aJg?*LE? zvo|#2wy4+G2e=lju^h{1`GlpKV_u$}5?Ny(@eG~Dd1w*3P|a>sec%Lw-jL@!sOMx_ z=KF^q8Pt~dS$YU>4Keiy_Wv>NQ|x?S#|E)*^a+BL#32vVeB(5yA0|t(r$!nu?#6*8 zj`JaDrL5Az;?NJz&?tv(br=usUx2n)O>32ApQMY#lWJGAda^6x9gpuj=ZxGvDec!P zj3WcAkw4ScHXfINUo%cv2UPC=nlMUgYIh8bT&!w-JrNUM2HKy6a(`%L!i_fie(eM= zeW!pB0T>O^KShHblX4?ngjK{LEE3U%0m@kU-*IDSSoB*>dhNk>>k$ccggvSFf z$iIQ5r67Oth6>w;s@vLC+ z6(d#85cp$`$QwIyqu=LnDwZ=g0xXc+baq9oH(y>Q@Tn{PiNUq)8kj554nBFajFmPy zIb@2pw8te?NX`6JGUDMr=hMu}Qumgmsh6+Y9xHGK`@RBUwcdtnEp=!LuAwXMP<(ei z7ZBlxjP%R32HocDxo9U##F;#&yWTE6L=GTrq^PLeTc(LycWaqWS~XNt?6I0IE_3Uz z{vc+n;|@=7J7z_Y;ku4W%g@dNF#$FjUh_rwfi-0wE}qXQzF#~_&%HHm^4G%Xht>=UC?sxH&+&QYp*5~rQUtIEzlD1Mjy z^FFZZ*dZ4EENGewo+t2O0vK5O>~GY5Xn=?5LcOapIxthtfE;Gnim}_xt>zz$V_oY` zy2Fwjka0#w`rzikm5SYs4+MY7*OI85I?m@YK>`DM=v}k9uXqF|L5q5QRjCpUjMv|TxBk_;FVEU7(Wcuw( zg%`sse;I2fA1HsP*a{J|A3(9?9s}fFe;J{?A!B%@ulKl@UzSx5FF{F87D~J7d9OCH zdCcPcTiS9n*ebRa?kr&9^?~2nSEPc@a2ngIB(gzdD}**rfC%&nJ~&zO6>enic&3KR z?;SO2f!RCD=ltOsu`OvU}pF2E@LE!;sn^^1_PQf7#I^6INNIyuFnbSK|*<(@$ch4w!C@ zyG-D(V=}@WqY&$A4dfwXf7o=cDN~+?WpePM&fC8kZg@SYwzh4xW=WgYcB$FXZPGya z;6%PkQL-&^Qk1eap_Aaa9(w7cH(O0WC$V)HN$GQQKV(Zo<;I_nlK$)7GbYsO=nLkn ztc8|GzLvzzaGj7~-~>X)wP&anGtq`I(7~c|D*^PH#0OL&^yh#_KZ-wplC{wzh}}P6iE@E!z=O+<^y` z%x6zGKjpTHbq2qhUwIm*yLHPx=4!Pm-Q*>m{mp-{ciYe|+qe!wJ^+LFs;(Av-`!@N zYZ!eIaz+q6rzIY?7cQDd&6$ahr%mX3{oU2T3%2vERyKTG#>&{lh)mjTdrB4E{YK~_ z^Ip|-j{qJYy0R}Mn>ln`#3uut!+T`hMM~oyfU+XRx@r4J8vz??wBFvhY_C^N8IT!M zSFl00WQy({j);G?#LEHTo6fE3kf$5a&x8R$zub`w6m!p0+~` z{NRPGd?Up647Iab)H7(~pm{MS$1R5&!(3fwGWV=~Sox6L)PuzzM0cv1 zU=64N#KnVU)lm4V)6?#)E5GZcNRbju%!e0&PnteTpOaE~U%%S$VVGT`nxMS?h|T-k z)=IKOb%65QNlyD(65`9YP4Ay#C6J=s^slkXFfNH?%1e%hOARJ$H5wLVx*(Mj#wpL_ z8+jErBCaF^aQ*B$ys4?-Lv$yIX$s8vq6C4l##O$-|3GmQ~G|jIx!xwjxDTsl9jjJX#)+`RSvWTb* z8M-s|A(7b`vD#c$(ds#LV>B&s2|nS$P19XsYMt3kZwGvF&JG-?-@8*P{7~n4M0ub@ zAeW2G#J)0RITxk|aNx1vysi1;o_<|(8FzH?loOETYiH^PTg~etfN@e4VW58X;0~=4{GlV9RP@Uf$?P=en3C-#zPTGo%62gue}VFFYm%@d zvb}Q#=m+E$_4vb$4=`0Fe+(n&nhoT7q-bObg456_2(rE zc|Ea4W{-?AG{w`&MXxR6C^~e9a5dIu#H1P-c*JV9rTS#^EHIEM@Gt1DIj+L}cUJ0uu3I zSi-QlVfQ(xk)^YdrDo?^;M_@Bhvc`tu75eQ51s0Sw((*>)8)=m9o>5Sm_O91F}}P* zn44Rrm7=TnNpSaMgEx#@P~}iH4y{!q*qXl@_nr}E`HY|H>S|6b_p2Ka&47hzPcM%~ zJ&(lo2n4+|cL__T8bv*N0^Tf&!C24iTc0$A3fndO6kc1|0j>f2>a%~sCc_2Yz+@%AtH7b#w``IcsVXGf+(BI<^ zf~NL&-&~a?mKdsUpEFOsvkJHQ`1>GZb3>@w6hB0Ti!VlW-k-ZJ)ims}Gj`3p{YQ^M;+VG#~2Wr^xxKn9aR=+_R+~dAOpT^U4gL1ef$n zOkl`3=GFE*yC~WP?Pi2%Ch8n$8`$nr9F3>l-R^1VsBplD$g#n5*zg}m(5MF zDHe)X=hIcBssA&xkpPcW(0l^r5ruQUgf&{o%YzKo%vgB#8MuGFktx3sVIt_|KsY^; z0cF;XPla&=4@EJ4r~6EGR8ond27QqgbHwR`uRG;yw`xVkW`5kre}Gf_ySo-wEaR&j zj}l1_R|F0#rb{>i8Iw0|*;rSQ=-0N=TY%V+p`mPXE+6(}ZQ_-y{3buAKUV9c?O4#r z`$2Z0u2Z0~<0zacS*xyJA}-~a`n9x_2357~q5D?+8G;O2)fg>va; zm8}&A8GlIkW%I<=@>Tondr?xSaj5{(OLyZzM(g0DGor$mh|ds3e?#^a^LyZ6wuyD8 z3F?*vt=!$~8ed7H1F=+>VhNjQ!5^3*Jv%-Rc)X_WJi8yT6Ouo1#)K^I+4C{IH9$|h zO~gJB_1I4(;U{Z&e(GY4e?DJ+mZNjn@Py*Ww^@%GcD(Q*J9U_}H(+6JtowLe{M3## z|6INer3}F0ARL^E7HByc;+xXM5k6?VWqnvYY)tv;y&PP(i zz!#^vD=4O!YpwP_TL1ihQ>m@V@p9)a!&-4wQzzyvq095FLFN?R8Q*7lY5J&Uey@!e zSZ}@vQGF7ZMN2l1Z@s|Ai)xA?+Cjh38ZFXj2Cbq{!-6+hHPsEqno9az1WE|xaS2k~ zMhU+@`S-(%8_}ZsD=baW{lPS-sSzP|uh!{_8CbvMG4fcWbPA~nIK3epX5u66B7O^; zkNZkj6@$|k?NXYJU37hh<9nzkv@VQj%5g@iJ5#kiz(5Eh-kT|i_=(>ds46sMXUBCq zrlHJL&Sj3-%Z+z%T6``{#bWaXpO!gb_>(IpM$h*oUw0LFrfa<&vQ*I8VT&FQ2UsaS z`}NaB6X`gw5Hy(`vgbIW8>bwEkfihRHi;`Hindx*B2v@_ob!}g=q zKQ+9CCX$H7*)HF5s2cX85Wha)ZE3E%*4n4P#^)#FE8=t)cUp+6xRpt^KT-kc41z|r zx4(-%y|*tF0(~$*T%;M{yJ^ys>)OCYR7r2onES+(LMl6JH9p+qXMtbeC?JeeUvYwPUxe) z&^L9`JbECr#Y7CjYSIMSG7~PsXdcN43uG!me`=#82{o~XTOPY%pAsgBy29S;L}t!x zQaOSdM+FcL?JY`c4JCR)foEq*<->EfQ{g0)WvGc1m0TtnC(JMGBNB$&3?bQQZU%a$ zU+cwoccgPMjed&>aWE~FDf(yJwC`euxCc)XOna~{Ob6!JcCZEm9w}ThTu(Kk8=PQIT0`&Il5*FVHb$w??ky8a0rvjR&!;Kr zzqm}~UN%bViZzr9 z+GU~6FVVnhHGMNB0o*RW-UA8*xnVACW$GTB#YS3{mG>cYp13O7S0pzXz%Lsq2b+s5#+vaNpc6Ba^(<#!Gj zdS+bk z3AlB<5e}lq#UeaVRLZK+wt^;6@|$LB>5=-i3c|BSAY26_WU1tW>3T(kqa5BP%wZBgW(yf))e$opPv4fdY(A2 zOws9!0-Z0?W@MznJ#HQos_|?^gt~_Wm>x^GXj<$=5ju-lkbd5;p-SP@Cd(?Q=3F@; zwTdJ-g5vb+WsFj6d#xGM{$pP9$1Q|Nt}Ul~wel+p6wRh&;aQ?1L-W*L#7Lf1d| ztIWFZEDI`@&ne>_x@F1)-_3ey@>{l=lYOOnc*7nNzK(;iS0r+8?6TdSV^~G2!SKuR zK5Fwqen3)Z!5v+Rk-h=Cym1O=%#9BRl{tmuFElTiM3`L3T0*;DwsPbwUo>$fx2126 z2j|V#sg-9Oe53rujz@nW&w$`oI{D})~sv(X%ifAviM z%JeQ!g=a73AZ}*HM4BX^!dU!+l$q5MLK%XY?@#*cnGa1V!vwX}-Nha=VCvD_?*@Mb zx9#;9@Qb&hU%p2e^NKOaEan4LREKGgk%3-(Of#=^j*F0SZQ9uwYK_L~WFRlklazvS zv#XQNvfuiQ3i*Xa{Vwg-JowjJov+u5JZ@a;2D@ys7^UOdZVvSU%t_*{Y$YE*4N{yj zB?vgyPwDG~u1Stx7fK{S(ec>C-2O2tWl4OaX3aO%)JU+0K zmm(Y5pSOOLTk!Lcck27F5j0-+l%yk$e5NiS*#x{s?_vOB(O)?Ys!kgGcddv&bu zm*O+Z=vm}}MM0sH;;FA*Wz-qNdkErzTydd9er#|^@Z-)i`v zzf^{OSjql7FZYGE4fzd_W7u`i?lNdU9x%I;;@C`X0@>e-{p0pgc{bXJoC2?%pYW0Jes2-cFpKxWNN6s zp|6K7>Z^c7X^4WO(lrAjN~WaV_=Q|ZZ}*Q#iN;q{R1Lp60yUGe7rvaW_=bI3Y<4ZV z+jN2^&V=jWrE?Z?oJ`8olWTjsdn&Njvji}uU#t4#9N(BHxCMEB!)G8_DyV~OMy4xa z%nj>Gyz`U-HS7JTG_KsXNli*Nl9>|!SNbGuRR?A9e76Xa9z%ux-fWdV83?kcZ=33D zwxUb-Ag_l^&9I0jzdyL#We)L=7PfeE70M}_Q9rP0X+lV_{(v`2eF)i}SOZiEkdVIK zIQ>WU(Z0!zWTCVO_fzWOx(>^G^{N6cNu~Fe^d>Ec`#XNA9VnR@sU%yOjd=SX}M8GCG$vDK;P#6YDbTNzi*0Ud9UP? zOItE|lD55-0+ji|QR>%I422HEi#a><_Sdl)F-`mUcn?Sw-lV$2Uz4p?e2Io7D~DLG zZm12fe@!KIQK+ln%(*}V2yXV*g|ffz>r1S2+$2~#FEo2z*0V-(5}WR$^eKdNTkW7F zRJ)*x+Y5{_f1Fnm0y_%|qGG(gUzBxRznCZK= zZQHhO+qm0qZ5!L&Zf$dGyIX5(+qP|e_dYMaH-EHO?(5_vXC|5aW+pQkh<^lePDtJv z)i|`oAJ$opdFiQysXanKnzgxuAzYFRl1Wf}vDzr9kL~mtXT+_U%AF8ofsZ~P6OECx zg2_${DDmGvPY*k#emqsprUGfJT)H#}I*a1YQtSGC@DdQW*S0iTc~ZCD9OG(NkWdha zKhjyKN`)0r3}C!w7XF4XAh+=K0T-t(XUWDn$?iU&{Bbn>do6e=$Ov!oK?RY|8c-($ zCD7@gxq6B7vWt9@{1E>q_Zu0{y#g$%5-JU)mX zluYsJa5rbUTt$SVX@&29T!M`1OmwWMv~Udm;j>pvY4V0X{tOU79gVRxj*|xI9-o?| zIO#PaQN9i35ZF^AuJILF#5TX92oiKlSuR$w2wOa&2gb`i1;wTnG`$-4)I`^%OqrM& zUP?Q<=chq;VC*ZHXbRvn?OTI589jPfR_}omqrhXnUrJi4U0ZEhoAd$_*2cvq@y-pO z{vtv7i{9Xi&N&Y-&Fc>o+38L0rL65q(`U0_Z(Dv@8cdb!sTxH9$P>;bPx_jI6WWe?{>YY@S<@o409wby@V_ru?PxB9E$4kB+jc z2_ujsw3{{8(-pCD84y=L+*nYRS44Vns)x;EsVX124DWYI$&q%M-f zlN_9~E9fN{iW(<;u!sADZG^<&G=B-i;+z-O%TKnR=$RLa3B}gFoB0eIU@U)f6;XM2`m=s1?yUU zI~-SsMxGUN0l6;rLnL*Tr@n#}cRY3ILJ}*8qHsVC%f;@^Umq?9`EyOLQ5>(X0L zj%gp(eKC;vm86`VeJBHVPS^8{?jN7@y{J?w9!IP_wyfE)ZKP9R-c=(8Pnu4!?wOzp z{^OZ<4W3b1gNQ)JpNJ!6Au5~fsssD)y(g__q`^~0xJe6OLQ7#s6V=#gUMY`4 z1lA2_srLPsj2rl`-M2ZzALtacz==U~_ALD~hUDzJHHXAHpzn7%4)d~kW4-6R2b7zu z8Zt$?EqVYc6r44Q9+j}X+ZH`rERZeCwOq7QLcOjGdw-Y@DXapwK{Pk;0on>D1-x@^ z&!RnK+maA_^{a}-a`sD9-cx5*k^oQ5&GY8=gzcWCs{@?rIT0fWAJ6sH6s{}s!7yyo z*vQ&`*0^*%mMvQBASJU?E~?rB#=uPQTcWosGSDKB8mLX%GkNq&b)wa&KM-!dtdsHx z(}#*=*1c(k!Fq19-99dp7|G`4Auii} z+aee`pygFP)J~`tWO)GZ$}rfpg5$1Fiev%(8|dT~2;qbySUn4LXQ=}12B(>{;|Ndy z2(IWfVD$&G41bx$2nkDfZQx*mPYn`W5#lh18HdIhWw&pJP}^I+5Dssh zk3e58T9z%*Hht+@s^o@`;26Uj*k-R~vW~c)RWaiACs~#Idl9pIv!52T_4R0Yv`YVI z#dC{qQoS3W!f>LF1rdbXf6A^|!-2=qYiJ!U)NN>+t&T>BX+XyioQXs40A0?AjlLV$ zwxqS4w@pxEFk(MejWmFFVJ)^1GwBF{<-H3=21N$@Bz97Y0cQz{NDSmWhQ;cTVi75A z`?YCHIK_0;ZDg`0Zo*b9f7$)8MiD+-5Lp+*x8#;L6DQg*kb$3DNvS^11zGiHrk!HC zN-pcs(fg85GypHRc-~x^xHQV0{)Ey(?t&=J{mx}`Pcjc1OxnuVcdnBeTEDH;H+v7Cx)+=H0y`(CnMPqNON_`v7+?|`H;b4ByX zLMDin2E}5U;G%?|pR$u4y`RN=U>!sNNUC!ha3^P>Co+ZIBWPJVvh0PC(Os0M42hMd zAS6INYi1BJr?%8g*p0m|ekd2jMSPEOP%ik9d7lcGKEJ@p%e7pnV2*8e>jxu`KgTd> z%D5cBfwM<#G&zlDJLFL@7hFZ7FBPNIleUBe7|1q@!Hp$d!pc>#`$e?X_mkDX4v~F6 z6+K8flc8B4+Mc0bTOIoe!M7Jp7loo0Np%I`R&)CI$7>1dbUC7NH9dp=t=*W&h8gI= zMoQi4pvGdrgvrEEm^uTfp+isM{g8Ql$APWiU294q!hR)gm&BqtYEOari}3b{M?Wmc zUk0LjFE_DQ(YHO13iQFzqi-O`yYEXy30fw9reC`7?+>to2u`oQXE!=(us}{YLMt&t z#aW!}FUaHjGB63wds8NA1UD$4RWn58HcCEyCmkDKd;WG=d)JQTDp#9ah8`j`CV*tB z6q*ur87>RJzg=S|&1tIY68(Ub`PWci|FswscbvMXX%tT?cp$Fx`a0zz$h*>r1H24d{_od+Tqnsg(<~eH~C`Dnb z4Q?cjrfA3%Fp3PLH{WUaTf=>bta=prxz;XOE8;q(6L1XBh~ds+&l z=FzE(pn+ePD8Ct>`N_lRK8IdO+DF-Vj6IRBcg(QbYXZH8!7pL`-@u2Z-!oOdMa!5l z7`WovxJrpYQ`B=A901HD&;4@~>jOAgwD6qA;WL-IL%J~jEal1^H9#^#KK>99cEm$- zW3w!KOBm~Vvf-E9qRpvccGHhV-Ih<0M5rW}b^B%>O)%d;1iO5TLB+DFc?O@yjbXJX z=ED@jJ8CODtiNO?(z$Y}VJ8sp3O28=4O9Q%oCIS|60b=5DA*-=L=Xn65ZxwRNGn+a zcR=^JdUnHy210QWyu)I0#Gfa88|p|&G?&>I@)H{4EvOzM3k?=zEafQx;}1tCrom#f zHmQh)0yM#b!Tlr0_PrA$7qLmq0}XW_T&b0kI4K?JA~y$f_96v>Max?ZDSugJn(ic+ zcZu}~Hkey}hS!6wA=0Y_J~1}P?=pO|;r!E5JV_)ZF@F13KN7k4;SXV1-+FEV>jo1;PG>OHZFkaQHk|MPO~T9qA$6U-cBz)1+ET zCt$Wd~@Yh;%-MmG4wNyMbcqV6R zo5WzLq)r=V9bJ%q#c7f?!O;nSSu<5dc%eEV@9{rdt%s}^AV9FpsVG5sWI-5#regjU z*18F40Tc2XtuWbJ$!X=r9G1U$(tkio2oU^IOs0+)JgBs6=>8Toui*d2Wf}ejct{|tyG~99XEOWg6QdhNWX@2 zQcn}3>VXM^<3_(hFpny#0GEfmI~E*GgYYIUcI(wyHK7vbPyqe0tgY!{eUKJOU1^$! zv0mCJ4(@??t@b;%?ov#mCyN3M0_@y5iWZ*L@C8)8qK3V~j^N&c!?7PR+EK@_s9>c2 ziX)%MsDwsX7fcF6S@iI=rWw&Yi3*7`A0Jy5#sz|_O=esj^UHbDMoFtfyiI44Hl&M5 zvkYxmUJ?0MxD&eZRMw}+7k6%*_;WOe$DP>E7c;fmXjw<`1l2nR+q^xR)}TGQaJ)ky z%j#qD18!}fUAbm(T3gB4_EAF3Ee;v^%j?Ct=#Z7c0w4#ri2Q`7O-!gkC}wBx<~DVn)ptQe5dUDCW z#X4_k)s2y_?ylm=O=K59()h4>DrFGw8^jS>T)F+$ zE5)`aVPbSZ>sWNU9-->oYuyRa6)gX>VX`|#b^_nizAxP(xVO-8Ao$WydcTHpHb?>@ zq;-iG5ot~HXX_W3Kl`;8+mpj2K!C~~RAV+)A+wsDdbQKsgazrghK1#U)ki~=c|^60 zxdm!leBQUDEe1=S%}TwEPqrsh!6FVqA^#p*-{Wu-4Etk&MF0H>7eT*ODa@U0Q2z2> zHRyeGzBmzChehhqt7l#%8e@@M-YOr<$&YV%fe#0_s>l8|R_k{sW?Wx+_QjjxLK(GF80-y2>+#Cy6R{}``79fGIM zBP=qDetyG3|DE>wEwbPGy)I&M)|?0HHwBSLo-C|1(9Rh<;#B^^2IOXqe_o*J%EuySFN(lObe(YGO#0a7_7(1xu zKCV404aS#Hfc>!aPPpISbL0xJ&t-KFPAH$VGPB{D{A4*ReSa}HiVK9LoE4){0{ zJInegd-;B9?(ziY9YoE{kE_r@%Z(n9Q%`y8-b5KAlvuA=*BRgJ;}KQ~~> zIy~B#W*QMkpFgS1EtR@^7D=sRhLcLf>)zCs4ooPh#V)I z2E}yCGq*Cz-7;CYc9G{;6+E&^eA(BC^9ox0uqG_BXv&Ad09jTzGoG)owfrV#GPg#5 zPloz}THO0S#LD>4TnM^pux~VmMJU?2< zA?=5xJ?9x4nq$2rUG3~fCTW50(&7yBQU!UwX73pzX=;w=BI55Z;}08NXEq)J>mDyJ z5{Q%qb?clEyO6N>)YJnp9G|wi{hT;|y7WvQ=NV*|W!ml%?2f=?rnhqewTfLma#sIx z>kkxG^~F>{^bA3QG_3m;^v71g%5w0}X?4;52>WVrz#JZtSCJvvXUbr?b<~!;6IJt{ z-IzRU;Uo(%dw&LojL?Q8*A$4`cbS;~SfZzr^&$tj0^dd<6YbTs>@+S&9 zhL3ULIshxiC$CAuwEQJi!8ggn@~>Ib7snou6c*Me;lntr8G$Y=N~1YmwV<_ z56*{h_6{|iB0rD$I+(I$7n|XamRPZLeOQa#amAZ(S6u(Km zcM+9m1P)tv!o5?RISZj1l8u9I+Nv|cbzluE3I{HWyx3zU=2VoO^+|I}v8?NKcM9w_l48&;6yfKk*ks~$7l{5sl1&YaIk2qgZ$A-+E zXcBE)IUPsH6^OPjXK~l1&NI5SN`HrLP!o5_a>F5tW10t^f@lq9$CiSY!P{D@d;!8S1R)J^tmk!yjrsg$%)O1Ee zrUY)87P+>9-yPXlvwS)?8oAW2o;IUX{1nd|k&aD5f9v@MFKp?(Yt=MbyH8pSu<3z6 zKAqTh!?BriGer+cKzIA)G>ssdf|W0>;8}MJ5&81C6<+M zp(!{{BSIuULWux`UxJE1Ly?qS-ONquY@X5>d-SF=5ls$9X$g8di`&ymb0v=d5y;rM z|8wj4yJm&!Z|5OTsa}9R!K=t27Y{R{ZKA5^V54crDrjQ0>!&zZcBlscHl2oP!nR8(7emFto|fe> z{~A>{3u+Prn#cgI3lRA}35knxtgTVGxiGpPg`+}(ADvo*UlmNmjoiKYfRaXGAny7D zJaKM|`4B@Ax)Pj!$-zmUfu0la(6>r*(vWA{S07lJl1MKJ5@wzpnT@}$daa@Gsg}`mB|p|?qOr=Oj0jc#!s1YJ zER=Ob1AU}1CGsZnZoTm}DYk`Y24J!_7lX*(3Jd2Qsl^CyY9110N#g$!VFFwI=MOO1 z>;JFN_5%J-g);45^Z$7kBJuxJlGH-}Wib8UUs!biPvyT}NhpSqrkgV4-#z^2 z<+J@FYC0^uqnQ2ls(A&JdH>bLfA>okXTZ}MO&_j(D@-4AP%;){;^5vvkoKRRfXQqB z{r?+8!H`CA=vK6~CQg_?q3mu<7!g{DRH1_hJ7yoBm`}B9^ zdj^~xAvIM3IdyWvu5KaTiRSMXGhKdGFEa?*(qp0-1G6QXbTqkZcMt)!?*R;1IQaz& zE+)YKFlvAqXn_P{-ybPsGdf|v3D>|aRZ_zgjnC;k6wTgl^KN%JEZ-|rGp*$dhJ#^g z;p>HrTvP$gX?qlF)`s$VHIic`6OpV?kogXpy}w2uvy;-=UfUYT)H0#lSJYqOJ>9u< zJM7D3-Z(o|c)m^`0SQ^u<)P1|na%L;X{|?TFmr=aJ@+t}svT z+98vv`4`F)0s{Li_i+79Rh0bKJ?;cgal$>Gwe4r&1h0d?u|EM+IIsWHk2)X)KlCj8 zzgW6gADlGk5ENrr1I?1W`G`PQ6P!O9yCWtmVq8Bu40+8TLQgWGTy5o*&&@@sTP?rw z?#)h3@6((?Vb$gbmVW;6bs}{w-($#Go(%i3VLg>16Yfrl`?V24wBLzUM7~ylgVkzG z-D+{kR*hpca`Y$=+N+*M0uG`A$wH-o|iwT{I-mXxVG-m#3U%dm`VI z+qHMyR1#CPD!)gG_S0vhVBo7>e7lkX`$hEWi%B#>hLjQhtM769!rkZLQY1`y221Pd zOLYg8C^>qeq1L4m2}BNSt`ITAka}?bDNg3=k@MjxwClyR%b~_l`m|7rg2UeTmXC(=5~Hf)t{@61lEJR4gtAj2+A?_9!{Xfs~@E6{49U7P9Zp(Fq7)K zK)JL(1AUJOO=LBX-ll3!izgG+r{aEOiL!zJ3__JaK~-J+aolFc+u5D?-<=a zn$aVQ;VvnHg7ud{6crN|O*-5ZCed8EB(pr=D%SP#qU~u>aYL|QVd}+(b))4>EBpN| zXY1uV$5B;aSnCUD43S^Fh=5e5#&@W~CYO=0p?27>#DkM+Zv){k4T(l7h&UO0WmvgM z`Glbw+NaXg{r##jUN0|1Xylq={$aLbE4?iF=)s)%m)3=@S*#`Uj41yVrMo(#K)KMj zy|=rF%K$Fwwmm#T1|R$}1U@U(wtTR*&JDbs zfk=&A08c{B#Zpwy9S) zWO-~RQ`hp$m<$y@v5>e)lBu~3_-EHAF*u_H`v@c?A|pjrfnU3^;^Hh_ti>??y)?@1 zjN5B>%A^>`!h@*f<>^sfYue_}8S|qR^y77O?AcpS7xJ6(wOk8BqU5Μh3)CFxmq z6NO~=n9|+_?Ya1exx?iCzO#no@f4NwJTeE}3sdsnhW9!nj}fJG1=@1#yeBVlfX^3l z8A^$KZhbXP}G4Yx1-}62c-&hBdvmz>M z4W5a@Ifcut3|VO^!u0iO09zSoe?Uf2WXiRRC6u6Ovl*!p8ryjZV*a=Tz#|~8<479n zbe<%U&@}$x#HjkrxNlH{+t3{%VdfTA7E$I~kNU<3kX51Be3l8bK_5~}iFXO5!+%B! zP5Qbt5<5;Sn1^q5p2!^10@)y^nAfJ8q^R)8^GUN!gE6xkyqnFL&Sw4XlssIYr^mc# z^1Z^)mW2#>_$IQb zT<_LfR(YPL+Yqo27+SD<n_<%+?7}z!m|IXJcnty;tD_y9YJthzqUdmFcP1X&y&b*EFNU_^o-l% zo_pMT@_&ni-3{%u`k-($d_wtXt%~x2rE(eHz}@$5vlZeV7Y;7K59hl2pR)i*iAdn3 ze3LPgBR)gxGeXS2q=SYa&xm&mtEO1@OV$jq*Z2=7!yp%_egAl0)*(=`RVqI0>Nliy zN^!Gt3-?IcehruA+Bj!B_>EuWWBfhJ_@o39rCTqa6mqJBeRwW+L$0n2yCX?4WV7ke z6Jwxd%Xw)1GVLvyB=LgKm7ncoc;ACGkx0%6&7l35gNwq~tVz$J_dDzJRHQN=gUi6F zZ+h;U6p5G)H^ynTD3!lh^9ydZt{Y!yKzI@q=az#Mm3&skPwDNKdkiV>t#aRE+QVxN zRaK0dop)3*iR7NU)@C59)aSLlsgswNZ#6eBgfr`oMyZ=CpBk zHYx(eMg9CV{Saa32=JSc(<`H^zFnb`NpyYiZlLaa7g`}ZWIUCJThoZ6T4G4c{D5RK zb{CFdp%67Wk~jmfVPffS^8RpSnSQ+5jON;PE+f30e^G!xfTtfl$&L{*WR@>e;r={BWC#tZd*hE@Q zG~RTUf)9gb&0S@X%*1$B?rfFY1ud-zjK zwv_FZlIGWt=bN&byNjM=?mT%SW9+LeS)xTX6-sH)Pufg6T11>j^{loRW`>pesK%zD zao^9v#F0Y0zGli5-2)y|h!+#G{)dl}?H_tG(#f%EYt?9TP;w?sJ`-T)ILxMTD!OrN zqX!k2)J3F_woubi3dMb7Q|^<8nea-3zxM?w6Q!s$6>(jSLL!$wgFmDe)iGifM;siE zh0`^3D!n|^z}0`$4ma+8D0`9#E&LbFD~)tlhW z%*m@^>1*7DArF9)ybhWq8Ty%f=zAA?G$j~$%yejjvpFe4HAZx)iHwL6{?6g3|AZ1* zuYWw{&>ZGs22ewt)uTX#>X>FtRE%)w;{FPwBO`6LlS`;g05nNbtvTvr)Ek9-NH5;B zWpq87>FJ!nL^7OZ4YgopX{g7JP$U-Rg5SW5wFxrXid5tZo|fn&ZH&L15f!)dj8^u= z)gelH)3PUOo>;$|Y&yF^^7`SE7~tn5Eu|3d!7{ zbr*}@blRL?!6Ra*z^vZ^Y7-Ik!AEr~SG(gA$_-v_j6V>nirI%*(~kGkCtC$J8{&i? zE+rk~B+X$qt0kxuv&CHkG74Z)s{^KqYF+`IZVKOMGf)C$Y~pjwa!T5c>UdWs>|J93 z8ftFg-|<8>Ha__i6n-rz*vq=P45nD|m;e}|k*i42v2dFoe+|}^h)Aa$)#XU~`F_ei z<1K#EX@O3+Yb%hrM3h`kINp5@KH~9Mlar#9r0JV*HP+lrvN;jlqe!Ggc*FJir8~KH z4buQ8PQcQU(kn5@cNS$&#n&$o8iCYRx2>U7R5E8unB>95hHD=vI^rYyYc~-t(%{c! zelvv}O<(+)5t+m(1*+43%~Bu)5CzOKd{V{i|AR%m>>J@z-xU7r`2@4UH2nwwnIshOb@%OXo;u`UMCHK{b2S&&O1F zFdKX+SD=rnEimWWIv4&vQRcuG>L-7}V#KD4#ZVsQLkpBgR*pU)UAr`yYJ%@BgTZdl zylkfVmHx{MBNQ}bQqPcHS#>YCSo_T`eLZkcXt=ir*J9Dw?#LE;rfHaj4c_(bxxx?U zBawvcX<9fDQDxab6r{3C>y3V)3XkY!=2%*q{?v9flG?nx;RNU|2>>C{ewTG5?Jwfr zn1Kf)fHERpX4y%398Ug0)-p>wr8tF*iyyU|D+68RXS`;|l1RjU>4No{D;eh`5_ZqI z97_+mqwwikoz2GHY`TUSJ8eoTH!uRR4O~J4t!Km*6pPLy|5p8fPy=-Qh=vY;C*Ja; zPu-hWF!U0u--?4gQ8hpDDREyW4{QjrvIpBDz$2)EOK@aS!b%eb!$QwF>431|Wg#jQ zb-M|l7zikmdnKSv#V5#s>nZ0fYr?UZkqRAaw6C;W1$aG$Qb=fD`qaZW@S~$}k zrebzZCpq1S`+e$>B+)#0%5+&7m5f5deB@k%G||S_mt>}Q{hjL(Uv~-?e9HNH+NPPR z{o6j*JmLOI%COT?Wxr19DNqS`xyO>0wQ+Mh&jPUNwI#p~sD?7veO>cm9dO-o+M3O3 z6IAW3H@L=)9PR;bhXK|e*c83Z65--Mv{?Ct|7LOt$r(E*A&wBGJm&UArZ4t?o!bFi zI&f~(8CGtwCyk$2#5pr9{)p*c-NUzWivW} zl%S<+C@9^iw=f;OHwBu}#K+Vrdj9W9L;}o)WdvuX?ypLg)UhrovLUSc_VbJYA`~EY z<6*#O_4fOkkZ+DIzY8p};v-v#mk$9LNIr``=ZYFkFZ-i;J)ca%*8AM~b*$!U~>psN4F&&K-#V51|MfFi?zUirt#kqzL+4ODM= z&F|Pdtow6me0N7Vrtg$- zA>v)s+KSf>rJla7^yXvUL+A$Z-kFGa?S)ILbnuym5)ce-4;PAOccw?Qryz)5rq>DM zCknp%E%v%Wd0S#ass5c|frf_uLHp26VYRjkVu8pw-cgqyoeyv>#v zg|wqocI3mNaRj2%jYttcOAM*e9UX#yrMOz*gwe9qb1*o_>GM^(+$D^khlh=KfJ*Ql zL%P-elC@IyDP|0n3P~1GXNEO1SOVHm6e<6hBOVD@+T-m`;9S;){aN#29QEH&`U7Sy zkzAad04}2)7q=NuxXgEA$B_l7kU;IBMb9)D6sSwim9mJ$hck4>HbI%;q{-JgUFE< zCHVfYuhj334PNV>7Vc49GXm%r^vhBJIpFaK zyH2?OGLuE#YBd-P1k*7xeE(W?n8|#XbjWq35ER8CJ^;>qJ6Sh%6z5rM?$DzQ6K&E2 zUWZIqj3>pd+-i~|B=rAq%19MvTAQGVMyO?yH#~6b&dz93p`?cAba>wZ3TW?uq_WkH z+mm*U-Q}LcN`#H9R{c*a54^%YGN1^sFuYsII;SGU@>)8D1}aZ51C(t*P_Tn3N&7wp zgffSpCy^r%l2+U2|DnWO@6tc>2N14<5c*lfvY*h$+~%ZSkrQIUqNMKm(%oS$LGWMw zRj`i#%JGMIn&dGlAB-OdnKN|!M->HBwR|}-@!Q=7s*NG;H)RX+`X8#ovQ}|Ju5j^X zBf8ZBBy?kY9*7SfXnUv%}tdr3s#d zjh^m^YvCZyp3!hd`uU41%S^)+(Dt1XAIS^X!uzjZ-O^VTPnT2{av(c^B*VV~@`LQg zX2ou{?rW%cNM$dA)gSmv;#8~68zRUod@B#XYUDqre!E*2#qN4ZwQo>ONc`YnTJzAm z@(orZX(t@g2TOxxTXXose=ahneCX5AX%$;D3M9sq6=C1cT&g0_&H)BN(U^`@oRSWQ z`}W3{4Z;!7jAVDK)7C?R%GH5ULf5M>8Jpp70JP_nKvep7(!MflM;DB~ADW_J1bu?e z+$Zy!f2o2ZgUDF}wD2&0HruNt28jiMEW>rc2tW%!?GM$db;yQsm)3(l^Rci5^PQKB zN(Z5aeJ$-aAW8heDbml=SsBpNtNoj>A!@>TEpSay>kR!c!4Nj4a=7NPq-LxUPpC-= zU)h9TU=@T-Rfa$fN{n`_`pi7esd^8J9pRKmCn++N(cuw`gb{4tGZxRvjbv64DIx?{ z7*t{B%h^xbqmSBVNBkGbk`SN1D8LKUt1rd7-kHk@G7h2(>fJ3Nw;P8Ckq2oRJdLTe zWEiCnO2ZiB1^pU$goj^HKoht_d39KkA}g!`L6hJ}$2thLy7zBxeR+>47bs9$u-T3OTIV z^IjKGL0(8b7t&q*eEg--I>Kg8Zce_bVFtXdZ#oxb3FJ(VqGQ5g8GIj}jKJnHiCiTH zl1s6#K^=@(#4EL&NSF1JLQh%UTGJcnxems-A+stQ?^hgLEvPkA!?W z2wIB9C1sxxO-Y#x;&ot|kjEUK7+!Iv6&(pNhAHlKv~xm^H5=8KRg;KATach!pPIS1 zL6xOe7;pJF>JGxTz_w#Bbn{vc(qoAnx~`}hoshZ)B&tvFo(8Xy#H9LxvBdKwkL>H2 z=EJ^>?>LkYh+pH)Qw9@`uT2x3&cu>gV|6?qJQ)KRR)iDPHQ)ACaO!Rsx_Whbyzx=< zG{goCJoz0dGs@ru{8*iUgqqgaYN!(#9rdqkJn6C;S^cMES(U=~B;&+eL018eK zjLGG91n`KcyB80@tSOswe@Nmn%x(LE5TjDu1r*Kl@4+NGVX6DQ@@Fzs!=>fh^70I@ z6KFxueZul{`aU;SF@KR{88Fx(6p}biPwu6+P}}lk0>Z)3it`M+?PPwEnjfV` zJmr|^q}EM^(xon4Fh5MrpO~~7yuIiL?+d(r2fqxENz=hjW-sYR@;p&fFQfaIioPSA#8>9L!ortUq$eg24ev6{wSa1kADRQHZaFVHV9B znLB!#G0>lgJgE8j2XqyfzJ<}+xrL*=&Es1jh*h0CT|N9k%8Gqn;O6?zCXvRii1)7L zx7EciUYTLocqvqTcJnff9g zHMZIFpuruncR)0~@oO7JZMl6e>+-G|S}asQ;hst7Ir+Vb{-}qk&qq~}GssR}{W{C3 zAq{ak1NzI7(V=o`_R{uh8~VRPty=#~9&x#=f}{o-&T-C0%KSB9yitD}EpP0`$?6WT z?p39n5`$DoKEqu9Ar$rnqy7tfb4o8plcUWE5DOy@n))@}uj8qK66B*@h{VXQjM`77 zDce$7ga_62G0YM4cuf@DTuAHi zSF`i}=ufA*1qn=%Ze%~%WYG!%e&VQ3Tk-;(E4q&jE@^%>{@TKiIh@2_J(n1h{XEeM zKgYT0Y9!mD>n$apbLg~xLp<_2+B`-9#;Cx9~b@?b?n%ZnUF zu9z@#7Iv0>xL`d)UPHZI%3Dw5TOU!7mIL|(xTgeTfT%>K^)CU?ZW%=ki>SN88d;gT zEQlE2Bo&_k7KYQvMh4T>rJvgI8-7Ng$mzBm?KW|8&(-}1s=KP zy>1NLhD--g{9yeR$&R+n^RjI47@+7zKT_?x-(~Tf;ERerF#-njM$)=3pfbs_>V?&S zw19Y;$2dAde^5!`w**Eiw&VO6ynKElmR`#Gbu;=g&J$rLKUNC!Gp0$tNZPaW3Z^`) zypk9_obK_L#S!Z<>dX(7PPH&o+lB|&ii>aJYenu?5tITRe%iLD8K|W3Xt)PQWWalj z+&~7)N}>tnBU2Cj7DgwqWG8-ujoArbMDjY=OXc~Ub*@LMq3l4D)i0n;)sSLTqA%*> zM-L5->Xsq!7CEazWHg%^X2e68pf&9@-&dLrqZnij6KM;#zw8n3zOMuCyvVjC?J*_kP@%2ynI8ljI(Anx525aK5VMFWXw?<02{W!BDvFpX(1SqskZ0 zJfQno5#LNS&l+Q8qWphLWfv6a)v0wC>aXzFgvuDB-F3Sx7p2u{^HR~Bp+}GkBU74R z5vp0Z|86Q(YK=wPiB4l&WmfsH_r3#gwZ^VG`!tv57%1x#^e3ZI(vOO)kQ(xq=M*=l zywfwW=Z6Ak80Oo1HOUarIi{O>+CS&?RQ8s}viyrTyiI9)!v>-!G|w1g2fh527WEQh zM3>e%jLul=z&PXER1SEvuF*Bqc)UU6t5^)$Jx9A&ci8aiqGolKF`D|LR?PkRR=b9G zB%yR>@DJN7TQ>wckgkt|yB@`q!*R&2sJec;QX^n$)*;lTRe8F8@WHtHRb4gFwhuIF zOmsEcWh1@4KW&j0d#5O(nyIC`D|_;gu%}IiBL+BL*u!r6JDIgN@E2RIWPTf5ig>_=&Dx2$berwH-0!--`QpBNo zfirI!iZ4{)pV~FlhCPc3@N8I3MLfa^2_WK;PNuHB*vw=lbrb@BZ$NF#svf=2(oaC$ zNrWM`y$+#SU-iOpo3pl{w3=ENnmr_AmDU>J<-xThoRaWT z+%sXiZ6)!s7nFXbjWdx&{k%p^64q5d+Y_xWn3*A580xlPy|UJSw3z88%wy?K7-LKe zI$U#~oBJiKtSM*-_O^26h-qq`=rak;)KUx@`+FVrch7q#-P5XPClOCG5pInq=O#g8 zl+iZA+a1Vo1A6g=`>6hGRCALl%ib%5z&1NtJKur!ST>P7Efnrr{x ziYP&z;t?cV22oEMz+lF`8dB6&O}opJ#dU$z8KqTljyw6{kp)xPA;}))AY`!opYH_E z!i_jYg}p5LR^kZujg-Fp<;1qiiA#8*Wi_pKLeLUC+Hzc>qr~}5=YolVxivbaeuyII z`#PIGd~qdaP5_w5B?TH3wab*0!Xa)GD-tv0MO1@eRiGkIc*PIcx`y1358Ul89_Shm zF1TpUM6f~qww3qefxo8jp7=zi#$lwILJHZ}>WW~uUz~&9H9^j9_z^2CU4`XawDu%X zt{4IMheCn9VF|02XS~FFtY6RyZG+sI6I|)Tw0#(qJiptGcp(2z`~{Tpp#ZU z3*LqTNbw)yLMK)eW*n{^ROPo;pd!Z`MI!0};S|EZSz zgVDMT=2_Wn93Ky{Z}|5eW3zAB#SsXx#LJ(m9-3>-s0hjRJ9AqCV-D+iD{J`zX*@R( zu(XO%uy_}kysdKDGvdKI)@A*$JBAmdU>Eb3g{G-i!{lz%v{#~1g6*0#wDe%&@Y2iTDif@;4j5x@>2*dRnT=IbbjV(9Drj}F zT(3~B1-BPqWHo9)Ua(N7M(Er?u=POoH4c6};;xlI4rZZSs^30G?~_!IhV-X3M3Rx= z2PKoSVJ~1Vs43ygKGMw?yQI|pLFIutAhz4SnEF{OrdXVT14?Ue+d=l^Xj_&xxcOi< zt^7--1rIJj&`Fu!XU^HIaaAA>=@7gI>QggW^0DEKMYQxxoo_#z?A-S4EVcaA%f4*v zNf>#ZicGp<7S^Tpr_lN)RrUHKJkXH!u+PteeV`N80(amR^1IwmDGYy?JA$+%&UhLbMv0_@`SDr|Tj7G)sdnZ}P&?9xv#BiEr>p2IH z#~VA@I)WdL1!|vu&=lup*8H|5Dv`zkT$rosqehyg@odQ`nh9**R=z+iZ)fX>!)I}! zA45L`amk&4*3kGmV_+*IM_;mc$O}l0Hi_m%+E&L8|_^P+nHiZ0gS{q&pQ9l zmmbS#^Xs8QnQsnyR@}l`i~nlOJQ6Bc5fn>iFB7!X zsHn??B0ym1U4BTp#<*5R)!hiPa|gZBSSb97P%1m2J?EqPCW`8ydEkQjI0*Rr0;Y-g zF<97=CI61LRuZj8g_uBVuW)Wo+_bmzDob_oa3}-vK0~CN${0LGbV%t!`)t-n$EaeF z19LTy*8Glrc>J7>Wz37sW2_ihqy9&0^a5*R`u_##5f<*1?=Jwiig1|-eZc!w`8Ms_ z^1cN86UrAwI8Bw~Q4Yhz+uciqPCNGZR+TF;dBH!638OYTW#t1V4s%~|lw0Q>gRdcP zUjGP1@mZAJ#34g#7)WF&i06*M9huzC^l{d9>Avav(_0)xMRwaspk{Skt|}{Q#gW7i zwen|K#E?l5@l|!t8i6*(?6wJfN0s9-IiTMQdP|9MMl^Dws&5jJUMnbW12$~;O}P2* zuT=F4QAgdYq zv}wCKN;Ykgz-fsbX%gh~Kyl2meT*H7-~*Yl&4HlG2yl%m|AzH;9*?%8wXs=FIr%<> z%f@n8m&>QB;)&2%u`w{ky-9?BRpp6F59*pWKMy#{lEt5h@O$&#hKI#ZW8xkDEzk$@ zUSfmI$J6@~F*VtTG##mD*?Kps)wUY77eu%plMDSPsw}TRtfc7%s$~YM<^1pORn?W0 z?fj*(mgbqhqJ1EHh;XDTZv##g;WAYY5#im{ryq69K;ETlY(ho&4$4bfr=X=a6XgL_ zy#?fQ(7i?EEx^~CwLu+i34-86T!$XU(or3iB|&$t>`-3mjCct}4eBEK&ER%KQpm~?%IS(3ck zYJyj_MG|AVZL7PEif?WO8Em23H=A+Gy_S&un<|%xaID=7U5H6XSg*`;fj5iUbPlO< z3&ydhSpkPYeql@jq*s*>SQCJerbDdp@plfR;eHC?4r>l`vc+boCF|#mCfJY!g)=N4~r6ZG58y?ECu6cHswfheDnt;?z zly{9*^0?#9st<-#`KArX-G@0MswR`CSq%ojPgV7qHfuBv+t;%2PPEn*)nC@TQ2G(M zMj?fAnXT7gE6vx!`+klILG)>(CFGxgm*=S{-IT@IO@fRBLrEkGWp~|GPmG+y>ks!dz931U{nhzXftpTiccz zxgsaLwABZyA|fXExEXIPPm&ig5f6@5EGm0auOZ9PG z|EXlc0tGL@!t7=)TKfmGUYbxKq_xvOXL;$!V6}cbAcDM9Ioym3zXMhv?1OTw z)ou-rmt0k*4HPgoSqH1~UQB4E&jZVWWfjlS))!~{y}%0VN9*y&EPAb+Qd4}2CW#g?p37#nY5TGL@i)(7G>wNm>Ct!+2EIV_>CLGexo&bIH~ zq&l`9jXgi+=SZK35Q>!Z#UBBBF369@H)iXr9Blob2UcRpWS^>b?9TGUuac=-eWT3& z(d;9+efAMezaNOl?EDayuezVK7I9H69huzeh-4-#lJgQ=zu;t^@0dwPSa1zP)Kgg& z3%pigX_E*dQS1d$IwD+duNE{O@_ki4BElk+b5*$>^f?i^#4N}Z#N-Aa7jYOb(P@|P zln9C?mA$}xE8Rv!Dzk;eYJw=GCu|lKu#9cM2Y|apScI{syAm{QG%w<{qj8FaB9c-) zwnuoZi99nxSftvMx7~h>BVcs^1$asI7F4#i_oXaSAf@Jj`m|#y!zv*ej93?IVF`54a%Qu?B~(J zUD|M|vR7mrj>M!C1!KZ-i3myi=TBfFB@lIpEBi)i?M_gnw`x9#DoOi!Ruv~$BJd((sugoGPnA`fY1};TSl;*YeGK|(*NAPkx5IWt zCM=Tm5?sIF6#mjVmrh3{;lrjKX4caJO)k@I4VJ1;{=UcuV7q zGg6*VQ>uCwL6T}kbaoIhXkSTmLBB*axWm}Y_ z#S*26(@fk|KfR#bX+z#1qP?mtQq@GgW9oWo^2U>LZ#H~TUEEI>a~`jcZukQK=&BosLjjX*e!zQDwjbf{Q#MT~~(PpCd7CUeA%7>~+0EC&7)k*f>|Q$+Y8 zbpnew(6T)b`b{+g?rc>)tE$t0Eh2LLj?{pG85U~7&m@6PjJ;Ld)roO{ISjZApT~rC(g}{auc|U)-}5?< zV`8E`+#$k~wq^DNu12{7V@|SPomZrQa}jPtIm6cFFBq?JXNW&RBs64odOCmZUPM}p zu0h&qJJ$r;i71K3b^S|tp<^c9yLT+fMk)|vjwC#a@+II7lrsywT=@JY-O;z^MgNy~k2gGXxJ9o8#8UnF8zDD4pP z21HGO^Htf~zTj?b-slNpHE_KM|I2FE-i8SsBQr!tFq|q)(4^$}?j*h0tfRM3wK^f^sD$1z~j`3Gi-J zy6tbbtMbew^vTnhxR39&*?2Bitx0|OFmOKTKY;udCcfzDHZb`rRe2s^C1Xx*(^NSK zSd22=&d@cWA5yK&1@15oxefSXk#v}MGTV{KonuHY?LUo|I;N8e3odpQbqR@Qo2wgm z+-9t}T9xC0GezV~>j>jT?Ii`-Y%{%FO(XzuQ)WcCKWYU^2)cB9wtM4VbOt~} z`~5!EF;7#G+AJAS+f4g&5qMsNuc-1Z5e8ZyHUVR%Xn`_rqy1G1{}Z?Y*jp7rq|kzj zGaS>PhzHHK|ME(XPcz;`8ftp0Jck=01-#LcfDubJ#@YTgnOJVb9FtB5UWL(sF0UQ~ zRgPtaHs(v|^DHJv_Cab={p|^Q7RUpQJKl_QJC+MAjME%293k`jR%^nmZH~*o+nh8e zkT`c?(oNROVVwkCYg0_FLHN;lHGEYBI>dxfy42{AITB$Ou(8TV`&r=qBK!dOTUC~* zlEUPo-@9TPT-l_7$(F>&CTTSLFfF2SK*f+tkF6o7EsoML1uT?=en0D_iH^DEeU$`6pF2qxzc` z$lxa67L+3}+SF$!eJeNd-CJn2SZ@GX3H)r5#zG#rO;rv>xYgEwvhWn}FQ9*?DnA5$ zsSYu`%i8MAP}N)8+<=v^26zu}jmeiSceKzlw*aRDNilVAhOMf6U4#x*I;rx8E}Mf^ zIt>LN!)mm34+HmrET^&AM?sYzh{>R}RF!)tu}}U8@;QWep!(lL(|=aBZ5H?m#!Te| z;7#`5eO1XYmJz84x((!E&>yObvy*h#X&~#~v`^K-uXTihHW3oqr{Cb6Z)nN zPiQTNLW#jYtMY$rIHGr99C==WHYnAl6z)XJp>iZ9PjXfJRo<(Ov_lnhqN^1_)JmF5 z5lH|=Z7bL<~-h&5GeH@0P)NZEJfTj|4AHx-r~W$VSQjLX|xt(|WCY#oiZ zRCVxko2eyerWJ0MsYaCqs1K5hn|CqeAY;QH6zYb$gZZC>b+&)!L9#4aQ5C zNLdqFcP`YXX>BG?1VlSg8L5_6RdXnA?9Rr7{OUy{MA?Fg#XHzso;r*dBYuk0w=CoQ z^^Y*rc6gv>+@eXa(w&1A#*A&L8S6qR?VS0q(@Z+N(im=_9eQ>R6GyUBc*tp46$F9(O69?qNFVX9*vOscU= zs?bhI5;>sIJ6I^nRJ-SI7(HPg7#X^$aR?fyhj3iT8;gU7)Klj z2L}fS2M0$c9d<`Fuyg7b(Zf_FDZ+l$9?@von|s8yqJx8jgM))3lMW8mcu`;2Rw^#i@>d_&KuIf!NI}7VIuhd(hM1gOCVS300000NkvXXu0mjfb&bPJ literal 0 HcmV?d00001 diff --git a/firebase.json b/firebase.json new file mode 100644 index 00000000..6b4c2aa6 --- /dev/null +++ b/firebase.json @@ -0,0 +1,16 @@ +{ + "flutter": { + "platforms": { + "dart": { + "lib/firebase_options.dart": { + "projectId": "sharekhan-dhruv", + "configurations": { + "android": "1:64647061409:android:a52c96120dd0ace789f5e0", + "web": "1:64647061409:web:a5241dce8216b26989f5e0", + "windows": "1:64647061409:web:4e3c3388406d0e2789f5e0" + } + } + } + } + } +} \ No newline at end of file diff --git a/ios/Flutter/Generated.xcconfig b/ios/Flutter/Generated.xcconfig new file mode 100644 index 00000000..6ebce106 --- /dev/null +++ b/ios/Flutter/Generated.xcconfig @@ -0,0 +1,14 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=C:\Users\DHRUV\Downloads\Flutter SDK\flutter +FLUTTER_APPLICATION_PATH=D:\Flutter\sharekhan_dhruv_headref +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_TARGET=lib\main.dart +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=1.0.0 +FLUTTER_BUILD_NUMBER=1 +EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386 +EXCLUDED_ARCHS[sdk=iphoneos*]=armv7 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=true +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/ios/Flutter/flutter_export_environment.sh b/ios/Flutter/flutter_export_environment.sh new file mode 100644 index 00000000..7bf44cf1 --- /dev/null +++ b/ios/Flutter/flutter_export_environment.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=C:\Users\DHRUV\Downloads\Flutter SDK\flutter" +export "FLUTTER_APPLICATION_PATH=D:\Flutter\sharekhan_dhruv_headref" +export "COCOAPODS_PARALLEL_CODE_SIGN=true" +export "FLUTTER_TARGET=lib\main.dart" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=true" +export "TREE_SHAKE_ICONS=false" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/ios/Runner/GeneratedPluginRegistrant.h b/ios/Runner/GeneratedPluginRegistrant.h new file mode 100644 index 00000000..7a890927 --- /dev/null +++ b/ios/Runner/GeneratedPluginRegistrant.h @@ -0,0 +1,19 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GeneratedPluginRegistrant_h +#define GeneratedPluginRegistrant_h + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GeneratedPluginRegistrant : NSObject ++ (void)registerWithRegistry:(NSObject*)registry; +@end + +NS_ASSUME_NONNULL_END +#endif /* GeneratedPluginRegistrant_h */ diff --git a/ios/Runner/GeneratedPluginRegistrant.m b/ios/Runner/GeneratedPluginRegistrant.m new file mode 100644 index 00000000..6f999d66 --- /dev/null +++ b/ios/Runner/GeneratedPluginRegistrant.m @@ -0,0 +1,98 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#import "GeneratedPluginRegistrant.h" + +#if __has_include() +#import +#else +@import cloud_firestore; +#endif + +#if __has_include() +#import +#else +@import desktop_webview_auth; +#endif + +#if __has_include() +#import +#else +@import file_picker; +#endif + +#if __has_include() +#import +#else +@import firebase_analytics; +#endif + +#if __has_include() +#import +#else +@import firebase_auth; +#endif + +#if __has_include() +#import +#else +@import firebase_core; +#endif + +#if __has_include() +#import +#else +@import firebase_dynamic_links; +#endif + +#if __has_include() +#import +#else +@import firebase_remote_config; +#endif + +#if __has_include() +#import +#else +@import firebase_storage; +#endif + +#if __has_include() +#import +#else +@import google_sign_in_ios; +#endif + +#if __has_include() +#import +#else +@import image_picker_ios; +#endif + +#if __has_include() +#import +#else +@import path_provider_foundation; +#endif + +@implementation GeneratedPluginRegistrant + ++ (void)registerWithRegistry:(NSObject*)registry { + [FLTFirebaseFirestorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseFirestorePlugin"]]; + [DesktopWebviewAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"DesktopWebviewAuthPlugin"]]; + [FilePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FilePickerPlugin"]]; + [FLTFirebaseAnalyticsPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAnalyticsPlugin"]]; + [FLTFirebaseAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAuthPlugin"]]; + [FLTFirebaseCorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseCorePlugin"]]; + [FLTFirebaseDynamicLinksPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseDynamicLinksPlugin"]]; + [FLTFirebaseRemoteConfigPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseRemoteConfigPlugin"]]; + [FLTFirebaseStoragePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseStoragePlugin"]]; + [FLTGoogleSignInPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleSignInPlugin"]]; + [FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]]; + [PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]]; +} + +@end diff --git a/lib/app/middleware/auth_middleware.dart b/lib/app/middleware/auth_middleware.dart index 827dd96c..37e0c1c6 100644 --- a/lib/app/middleware/auth_middleware.dart +++ b/lib/app/middleware/auth_middleware.dart @@ -6,9 +6,9 @@ import 'package:get_flutter_fire/app/routes/app_pages.dart'; Future loginVerify(bool check, GetNavConfig route, Future Function(GetNavConfig) redirector) async { - final newRoute = route.location == Routes.LOGIN + final newRoute = route.locationString == Routes.LOGIN ? Routes.LOGIN - : Routes.LOGIN_THEN(route.location); + : Routes.LOGIN_THEN(route.locationString); if (check) { return GetNavConfig.fromRoute(newRoute); } @@ -17,9 +17,9 @@ Future loginVerify(bool check, GetNavConfig route, // This will never get reached if server is sending error in login due to non verification // With customClaims status == "creating", it will reach here for SignUp case only if (!AuthService.to.isEmailVerified && !AuthService.to.registered.value) { - return GetNavConfig.fromRoute(route.location == Routes.REGISTER + return GetNavConfig.fromRoute(route.locationString == Routes.REGISTER ? Routes.REGISTER - : Routes.REGISTER_THEN(route.location)); + : Routes.REGISTER_THEN(route.locationString)); } return await redirector(route); @@ -68,7 +68,7 @@ class EnsureRoleMiddleware extends GetMiddleware { @override Future redirectDelegate(GetNavConfig route) async { if (!AuthService.to.isLoggedInValue || !AuthService.to.hasRole(role)) { - final newRoute = Routes.LOGIN_THEN(route.location); + final newRoute = Routes.LOGIN_THEN(route.locationString); return GetNavConfig.fromRoute(newRoute); } return await super.redirectDelegate(route); diff --git a/lib/app/modules/cart/controllers/cart_controller.dart b/lib/app/modules/cart/controllers/cart_controller.dart index c938ec4c..f4bb5440 100644 --- a/lib/app/modules/cart/controllers/cart_controller.dart +++ b/lib/app/modules/cart/controllers/cart_controller.dart @@ -1,23 +1,72 @@ import 'package:get/get.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import '../../../../models/cart_item.dart'; class CartController extends GetxController { - //TODO: Implement CartController + final FirebaseFirestore _firestore = FirebaseFirestore.instance; + final cartItems = [].obs; - final count = 0.obs; @override void onInit() { super.onInit(); + fetchCartItems(); + + // Add a dummy item for demonstration purposes + final dummyItem = CartItem( + id: 'dummy-id', + name: 'Dummy Item', + quantity: 1, + ); + cartItems.add(dummyItem); } - @override - void onReady() { - super.onReady(); + Future fetchCartItems() async { + final userId = 'your-user-id'; // Replace with the actual user ID + final snapshot = await _firestore + .collection('carts') + .doc(userId) + .collection('items') + .get(); + + final items = snapshot.docs + .map((doc) => CartItem.fromDocument(doc)) + .toList(); + + cartItems.assignAll(items); } - @override - void onClose() { - super.onClose(); + Future addCartItem(CartItem item) async { + final userId = 'your-user-id'; // Replace with the actual user ID + await _firestore + .collection('carts') + .doc(userId) + .collection('items') + .add(item.toMap()); + + fetchCartItems(); } - void increment() => count.value++; + Future removeCartItem(String itemId) async { + final userId = 'your-user-id'; // Replace with the actual user ID + await _firestore + .collection('carts') + .doc(userId) + .collection('items') + .doc(itemId) + .delete(); + + fetchCartItems(); + } + + Future updateCartItem(CartItem item) async { + final userId = 'your-user-id'; // Replace with the actual user ID + await _firestore + .collection('carts') + .doc(userId) + .collection('items') + .doc(item.id) + .update(item.toMap()); + + fetchCartItems(); + } } diff --git a/lib/app/modules/cart/views/cart_view.dart b/lib/app/modules/cart/views/cart_view.dart index 3e048c79..05800d34 100644 --- a/lib/app/modules/cart/views/cart_view.dart +++ b/lib/app/modules/cart/views/cart_view.dart @@ -1,27 +1,78 @@ import 'package:flutter/material.dart'; - import 'package:get/get.dart'; -import 'package:get_flutter_fire/app/routes/app_pages.dart'; -import '../../../widgets/screen_widget.dart'; +import '../../../../models/cart_item.dart'; import '../../../../services/auth_service.dart'; import '../controllers/cart_controller.dart'; class CartView extends GetView { const CartView({super.key}); + @override Widget build(BuildContext context) { - return ScreenWidget( + return Scaffold( appBar: AppBar( title: Text('${AuthService.to.userName} Cart'), centerTitle: true, ), - body: const Center( - child: Text( - 'CartView is working', - style: TextStyle(fontSize: 20), - ), + body: Obx(() { + if (controller.cartItems.isEmpty) { + return const Center( + child: Text( + 'No items in cart', + style: TextStyle(fontSize: 20), + ), + ); + } + + return ListView.builder( + padding: const EdgeInsets.all(8.0), + itemCount: controller.cartItems.length, + itemBuilder: (context, index) { + final item = controller.cartItems[index]; + return Card( + margin: const EdgeInsets.symmetric(vertical: 8.0), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.name, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 8), + Text('Quantity: ${item.quantity}'), + ], + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () => controller.removeCartItem(item.id), + ), + ], + ), + ), + ); + }, + ); + }), + floatingActionButton: FloatingActionButton( + onPressed: () { + // Add new cart item + final newItem = CartItem( + id: DateTime.now().millisecondsSinceEpoch.toString(), // Unique ID + name: 'New Item', + quantity: 1, + ); + controller.addCartItem(newItem); + }, + child: const Icon(Icons.add), ), - screen: screen!, ); } } diff --git a/lib/app/modules/categories/bindings/categories_binding.dart b/lib/app/modules/categories/bindings/categories_binding.dart index 06e278c8..f69ba042 100644 --- a/lib/app/modules/categories/bindings/categories_binding.dart +++ b/lib/app/modules/categories/bindings/categories_binding.dart @@ -1,12 +1,11 @@ import 'package:get/get.dart'; - import '../controllers/categories_controller.dart'; class CategoriesBinding extends Bindings { @override void dependencies() { - Get.lazyPut( - () => CategoriesController(), + Get.lazyPut( + () => CategoryController(), ); } } diff --git a/lib/app/modules/categories/controllers/categories_controller.dart b/lib/app/modules/categories/controllers/categories_controller.dart index 6612e511..67150e2f 100644 --- a/lib/app/modules/categories/controllers/categories_controller.dart +++ b/lib/app/modules/categories/controllers/categories_controller.dart @@ -1,23 +1,26 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:get/get.dart'; +import 'package:get_flutter_fire/models/category.dart'; -class CategoriesController extends GetxController { - //TODO: Implement CategoriesController +class CategoryController extends GetxController { + final FirebaseFirestore _firestore = FirebaseFirestore.instance; + var categories = [].obs; - final count = 0.obs; @override void onInit() { super.onInit(); + fetchCategories(); } - @override - void onReady() { - super.onReady(); + void fetchCategories() { + _firestore.collection('categories').snapshots().listen((snapshot) { + categories.value = snapshot.docs + .map((doc) => Category.fromMap(doc.data())) + .toList(); + }); } - @override - void onClose() { - super.onClose(); + void addCategory(Category category) async { + await _firestore.collection('categories').add(category.toMap()); } - - void increment() => count.value++; } diff --git a/lib/app/modules/categories/views/categories_view.dart b/lib/app/modules/categories/views/categories_view.dart index 97bfef38..87d862f6 100644 --- a/lib/app/modules/categories/views/categories_view.dart +++ b/lib/app/modules/categories/views/categories_view.dart @@ -1,24 +1,31 @@ import 'package:flutter/material.dart'; - import 'package:get/get.dart'; +import 'package:get_flutter_fire/app/modules/categories/controllers/categories_controller.dart'; -import '../controllers/categories_controller.dart'; - -class CategoriesView extends GetView { +class CategoriesView extends GetView { const CategoriesView({super.key}); + @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('CategoriesView'), - centerTitle: true, - ), - body: const Center( - child: Text( - 'CategoriesView is working', - style: TextStyle(fontSize: 20), - ), - ), + appBar: AppBar(title: const Text('Categories')), + body: Obx(() { + if (controller.categories.isEmpty) { + return const Center(child: Text('No categories')); + } + return ListView.builder( + itemCount: controller.categories.length, + itemBuilder: (context, index) { + final category = controller.categories[index]; + return ExpansionTile( + title: Text(category.name), + children: category.subCategories.map((subCategory) => ListTile( + title: Text(subCategory.name), + )).toList(), + ); + }, + ); + }), ); } } diff --git a/lib/app/modules/login/views/login_view.dart b/lib/app/modules/login/views/login_view.dart index 00c3af3f..cdcb6537 100644 --- a/lib/app/modules/login/views/login_view.dart +++ b/lib/app/modules/login/views/login_view.dart @@ -39,8 +39,8 @@ class LoginView extends GetView { return Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: action == AuthAction.signIn - ? const Text('Welcome to Get Flutter Fire, please sign in!') - : const Text('New to Get Flutter Fire, please sign up!'), + ? const Text('Welcome to Sharekhan App, please sign in!') + : const Text('New to Get Sharekhan App? please sign up!'), ); } diff --git a/lib/app/modules/role_requests/bindings/role_requests_binding.dart b/lib/app/modules/role_requests/bindings/role_requests_binding.dart new file mode 100644 index 00000000..8f7233e2 --- /dev/null +++ b/lib/app/modules/role_requests/bindings/role_requests_binding.dart @@ -0,0 +1,11 @@ +import 'package:get/get.dart'; +import '../controllers/role_requests_controllers.dart'; + +class RoleRequestsBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => RoleRequestController(), + ); + } +} diff --git a/lib/app/modules/role_requests/controllers/role_requests_controllers.dart b/lib/app/modules/role_requests/controllers/role_requests_controllers.dart new file mode 100644 index 00000000..cc37f1b0 --- /dev/null +++ b/lib/app/modules/role_requests/controllers/role_requests_controllers.dart @@ -0,0 +1,33 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:get/get.dart'; +import 'package:get_flutter_fire/models/role_requests.dart'; + +class RoleRequestController extends GetxController { + final FirebaseFirestore _firestore = FirebaseFirestore.instance; + var roleRequests = [].obs; + + @override + void onInit() { + super.onInit(); + fetchRoleRequests(); + } + + void fetchRoleRequests() { + _firestore.collection('role_requests').snapshots().listen((snapshot) { + roleRequests.value = snapshot.docs + .map((doc) => RoleRequest.fromMap(doc.data() as Map)) + .toList(); + }); + } + + void addRoleRequest(RoleRequest request) async { + await _firestore.collection('role_requests').add(request.toMap()); + } + + void approveRoleRequest(String requestId) async { + await _firestore + .collection('role_requests') + .doc(requestId) + .update({'approved': true}); + } +} diff --git a/lib/app/modules/role_requests/views/role_requests_view.dart b/lib/app/modules/role_requests/views/role_requests_view.dart new file mode 100644 index 00000000..821dfaa0 --- /dev/null +++ b/lib/app/modules/role_requests/views/role_requests_view.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../controllers/role_requests_controllers.dart'; + +class RoleRequestsView extends GetView { + const RoleRequestsView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Role Requests')), + body: Obx(() { + if (controller.roleRequests.isEmpty) { + return const Center(child: Text('No role requests')); + } + return ListView.builder( + itemCount: controller.roleRequests.length, + itemBuilder: (context, index) { + final request = controller.roleRequests[index]; + return ListTile( + title: Text('User: ${request.userId}'), + subtitle: Text('Requested Role: ${request.requestedRole}'), + trailing: request.approved + ? const Icon(Icons.check, color: Colors.green) + : ElevatedButton( + onPressed: () => + controller.approveRoleRequest(request.userId), + child: const Text('Approve'), + ), + ); + }, + ); + }), + ); + } +} diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index 7269755d..7aa8cc94 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:get_flutter_fire/app/modules/role_requests/bindings/role_requests_binding.dart'; +import 'package:get_flutter_fire/app/modules/role_requests/views/role_requests_view.dart'; import '../../models/access_level.dart'; import '../../models/role.dart'; @@ -60,6 +62,10 @@ class AppPages { page: () => const LoginView(), binding: LoginBinding(), ), + Screen.LOGOUT.getPage( + page: () => const LoginView(), + binding: LoginBinding(), + ), Screen.REGISTER.getPage( page: () => const RegisterView(), binding: RegisterBinding(), @@ -146,6 +152,10 @@ class AppPages { ), ], ), + Screen.ROLE_REQUEST.getPage( + page: () => const RoleRequestsView(), + binding: RoleRequestsBinding(), + ), ], ) ], diff --git a/lib/app/routes/app_routes.dart b/lib/app/routes/app_routes.dart index f3129d21..8ce81c6f 100644 --- a/lib/app/routes/app_routes.dart +++ b/lib/app/routes/app_routes.dart @@ -17,6 +17,7 @@ abstract class Routes { // static const TASKS = _Paths.HOME + _Paths.TASKS; // static const USERS = _Paths.HOME + _Paths.USERS; // static const MY_PRODUCTS = _Paths.HOME + _Paths.MY_PRODUCTS; + static String ROLE_REQUEST = Screen.ROLE_REQUEST.route; static String PRODUCT_DETAILS(String productId) => '${Screen.PRODUCTS.route}/$productId'; diff --git a/lib/app/routes/screen_extension.dart b/lib/app/routes/screen_extension.dart index aaf138b0..909379c1 100644 --- a/lib/app/routes/screen_extension.dart +++ b/lib/app/routes/screen_extension.dart @@ -110,7 +110,7 @@ extension ScreenExtension on Screen { extension RoleExtension on Role { int getCurrentIndexFromRoute(GetNavConfig? currentRoute) { - final String? currentLocation = currentRoute?.location; + final String? currentLocation = currentRoute?.locationString; int currentIndex = 0; if (currentLocation != null) { currentIndex = diff --git a/lib/app/widgets/login_widgets.dart b/lib/app/widgets/login_widgets.dart index 98d6f950..8ed5241b 100644 --- a/lib/app/widgets/login_widgets.dart +++ b/lib/app/widgets/login_widgets.dart @@ -15,7 +15,7 @@ class LoginWidgets { padding: const EdgeInsets.all(20), child: AspectRatio( aspectRatio: 1, - child: Image.asset(ImgConstants.flutterfire), + child: Image.asset('assets/icons/Official_Logo_of_Sharekhan_by_BNP_Paribas.png'), ), ); } @@ -25,7 +25,7 @@ class LoginWidgets { children: [ myWidget, const Padding( - padding: EdgeInsets.only(top: 16), + padding: EdgeInsets.only(top: 20), child: Text( 'By signing in, you agree to our terms and conditions.', style: TextStyle(color: Colors.grey), diff --git a/lib/models/cart_item.dart b/lib/models/cart_item.dart new file mode 100644 index 00000000..5d737ccc --- /dev/null +++ b/lib/models/cart_item.dart @@ -0,0 +1,29 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; + +class CartItem { + String id; + String name; + int quantity; + + CartItem({ + required this.id, + required this.name, + required this.quantity, + }); + + factory CartItem.fromDocument(DocumentSnapshot doc) { + final data = doc.data() as Map; + return CartItem( + id: doc.id, + name: data['name'] ?? '', + quantity: data['quantity'] ?? 0, + ); + } + + Map toMap() { + return { + 'name': name, + 'quantity': quantity, + }; + } +} diff --git a/lib/models/category.dart b/lib/models/category.dart new file mode 100644 index 00000000..e8a10903 --- /dev/null +++ b/lib/models/category.dart @@ -0,0 +1,26 @@ +class Category { + String id; + String name; + List subCategories; + + Category( + {required this.id, required this.name, this.subCategories = const []}); + + factory Category.fromMap(Map data) { + return Category( + id: data['id'], + name: data['name'], + subCategories: (data['subCategories'] as List) + .map((subCat) => Category.fromMap(subCat)) + .toList(), + ); + } + + Map toMap() { + return { + 'id': id, + 'name': name, + 'subCategories': subCategories.map((subCat) => subCat.toMap()).toList(), + }; + } +} diff --git a/lib/models/role_requests.dart b/lib/models/role_requests.dart new file mode 100644 index 00000000..56bb9efe --- /dev/null +++ b/lib/models/role_requests.dart @@ -0,0 +1,23 @@ +class RoleRequest { + String userId; + String requestedRole; + bool approved; + + RoleRequest({required this.userId, required this.requestedRole, this.approved = false}); + + factory RoleRequest.fromMap(Map data) { + return RoleRequest( + userId: data['userId'], + requestedRole: data['requestedRole'], + approved: data['approved'] ?? false, + ); + } + + Map toMap() { + return { + 'userId': userId, + 'requestedRole': requestedRole, + 'approved': approved, + }; + } +} diff --git a/lib/models/screens.dart b/lib/models/screens.dart index 24dee39f..ca6a7a3e 100644 --- a/lib/models/screens.dart +++ b/lib/models/screens.dart @@ -101,6 +101,11 @@ enum Screen implements ActionEnum { label: "Logout", accessor_: AccessedVia.bottomSheet, accessLevel: AccessLevel.authenticated), + ROLE_REQUEST('/role-request', + icon: Icons.request_page, + label: "Role Request", + accessor_: AccessedVia.drawer, + accessLevel: AccessLevel.authenticated), ; const Screen(this.path, diff --git a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig new file mode 100644 index 00000000..887b9e76 --- /dev/null +++ b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig @@ -0,0 +1,11 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=C:\Users\DHRUV\Downloads\Flutter SDK\flutter +FLUTTER_APPLICATION_PATH=D:\Flutter\sharekhan_dhruv_headref +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=1.0.0 +FLUTTER_BUILD_NUMBER=1 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=true +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/macos/Flutter/ephemeral/flutter_export_environment.sh b/macos/Flutter/ephemeral/flutter_export_environment.sh new file mode 100644 index 00000000..abed0b45 --- /dev/null +++ b/macos/Flutter/ephemeral/flutter_export_environment.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=C:\Users\DHRUV\Downloads\Flutter SDK\flutter" +export "FLUTTER_APPLICATION_PATH=D:\Flutter\sharekhan_dhruv_headref" +export "COCOAPODS_PARALLEL_CODE_SIGN=true" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=true" +export "TREE_SHAKE_ICONS=false" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/pubspec.lock b/pubspec.lock index 877fc75e..70a33abd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -49,6 +49,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + cloud_firestore: + dependency: "direct main" + description: + name: cloud_firestore + sha256: a0f161b92610e078b4962d7e6ebeb66dc9cce0ada3514aeee442f68165d78185 + url: "https://pub.dev" + source: hosted + version: "4.17.5" + cloud_firestore_platform_interface: + dependency: transitive + description: + name: cloud_firestore_platform_interface + sha256: "6a55b319f8d33c307396b9104512e8130a61904528ab7bd8b5402678fca54b81" + url: "https://pub.dev" + source: hosted + version: "6.2.5" + cloud_firestore_web: + dependency: transitive + description: + name: cloud_firestore_web + sha256: "89dfa1304d3da48b3039abbb2865e3d30896ef858e569a16804a99f4362283a9" + url: "https://pub.dev" + source: hosted + version: "3.12.5" collection: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d000e5f5..3b85c0c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,36 +1,38 @@ name: get_flutter_fire version: 1.0.0+1 publish_to: none -description: Boilerplate for Flutter with GetX, showing sample utilization of Firebase capabilities -environment: +description: Boilerplate for Flutter with GetX, showing sample utilization of + Firebase capabilities +environment: sdk: '>=3.3.4 <4.0.0' -dependencies: +dependencies: + cloud_firestore: ^4.17.5 cupertino_icons: ^1.0.6 - get: 4.6.6 - flutter: - sdk: flutter + file_picker: ^8.0.3 + firebase_analytics: ^10.10.7 + firebase_auth: ^4.19.5 firebase_core: ^2.31.0 + firebase_remote_config: ^4.4.7 + firebase_storage: ^11.7.5 firebase_ui_auth: ^1.14.0 - firebase_auth: ^4.19.5 - google_sign_in: ^6.2.1 + firebase_ui_localizations: ^1.12.0 firebase_ui_oauth_google: ^1.3.2 + flutter: + sdk: flutter + get: 4.6.6 + get_storage: ^2.1.1 google_fonts: ^6.2.1 - firebase_storage: ^11.7.5 + google_sign_in: ^6.2.1 image_picker: ^1.1.1 - file_picker: ^8.0.3 path: ^1.9.0 - get_storage: ^2.1.1 - firebase_ui_localizations: ^1.12.0 - firebase_remote_config: ^4.4.7 - firebase_analytics: ^10.10.7 -dev_dependencies: +dev_dependencies: flutter_lints: 3.0.2 - flutter_test: + flutter_test: sdk: flutter -flutter: +flutter: fonts: - family: SocialIcons fonts: @@ -40,4 +42,3 @@ flutter: - assets/images/ - assets/icons/ uses-material-design: true - From b99f3fdd48934da5b8ddf78823e97e7ed7770bb0 Mon Sep 17 00:00:00 2001 From: Aastha Gadhvi Date: Fri, 19 Jul 2024 11:21:12 +0530 Subject: [PATCH 15/20] Added Search Bar (Toggle Button for phones) on Top Center with Title --- devtools_options.yaml | 3 + .../controllers/dashboard_controller.dart | 5 + .../dashboard/views/dashboard_view.dart | 137 +++++++++++++-- .../login/controllers/login_controller.dart | 51 +++++- lib/app/modules/login/views/login_view.dart | 56 +++++++ .../login/views/phone_verification_view.dart | 53 ++++++ .../controllers/profile_controller.dart | 91 ++++++++++ .../modules/profile/views/profile_view.dart | 158 ++++++++++++++++++ lib/services/auth_service.dart | 3 + lib/services/phone_auth_screen.dart | 111 ++++++++++++ pubspec.lock | 16 ++ pubspec.yaml | 1 + 12 files changed, 673 insertions(+), 12 deletions(-) create mode 100644 devtools_options.yaml create mode 100644 lib/app/modules/login/views/phone_verification_view.dart create mode 100644 lib/services/phone_auth_screen.dart diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 00000000..fa0b357c --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/lib/app/modules/dashboard/controllers/dashboard_controller.dart b/lib/app/modules/dashboard/controllers/dashboard_controller.dart index 24d91a16..4732b7b6 100644 --- a/lib/app/modules/dashboard/controllers/dashboard_controller.dart +++ b/lib/app/modules/dashboard/controllers/dashboard_controller.dart @@ -14,4 +14,9 @@ class DashboardController extends GetxController { }, ); } + var isSearchBarVisible = true.obs; + void toggleSearchBarVisibility() { + isSearchBarVisible.value = !isSearchBarVisible.value; + } + // Observable state for current time (example) } diff --git a/lib/app/modules/dashboard/views/dashboard_view.dart b/lib/app/modules/dashboard/views/dashboard_view.dart index f475030f..648f164a 100644 --- a/lib/app/modules/dashboard/views/dashboard_view.dart +++ b/lib/app/modules/dashboard/views/dashboard_view.dart @@ -1,6 +1,36 @@ +// import 'package:flutter/material.dart'; +// import 'package:get/get.dart'; +// +// import '../controllers/dashboard_controller.dart'; +// +// class DashboardView extends GetView { +// const DashboardView({super.key}); +// +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// body: Center( +// child: Obx( +// () => Column( +// mainAxisSize: MainAxisSize.min, +// children: [ +// const Text( +// 'DashboardView is working', +// style: TextStyle(fontSize: 20), +// ), +// Text('Time: ${controller.now.value.toString()}'), +// ], +// ), +// ), +// ), +// ); +// } +// } import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:firebase_remote_config/firebase_remote_config.dart'; +import '../../../../services/remote_config.dart'; import '../controllers/dashboard_controller.dart'; class DashboardView extends GetView { @@ -9,20 +39,105 @@ class DashboardView extends GetView { @override Widget build(BuildContext context) { return Scaffold( - body: Center( - child: Obx( - () => Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Text( - 'DashboardView is working', - style: TextStyle(fontSize: 20), - ), - Text('Time: ${controller.now.value.toString()}'), - ], + appBar: AppBar( + title: const Text('Dashboard'), + centerTitle: true, + actions: [ + GetBuilder( + builder: (controller) { + return FutureBuilder( + future: RemoteConfig.instance.then((config) => config.showSearchBarOnTop()), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || !snapshot.data!) { + return const SizedBox.shrink(); + } else { + return GetPlatform.isMobile + ? PopupMenuButton( + onSelected: (value) { + if (value == 'toggleSearchBar') { + controller.toggleSearchBarVisibility(); + } + }, + itemBuilder: (BuildContext context) { + return [ + PopupMenuItem( + value: 'toggleSearchBar', + child: Row( + children: [ + const Icon(Icons.search), + const SizedBox(width: 8), + const Text('Toggle Search Bar'), + ], + ), + ), + ]; + }, + ) + : const SizedBox.shrink(); + } + }, + ); + }, ), + ], + ), + body: Obx( + () { + // Use the controller's state for search bar visibility + bool isSearchBarVisible = controller.isSearchBarVisible.value; + + return FutureBuilder( + future: RemoteConfig.instance.then((config) => config.showSearchBarOnTop()), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } else { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (isSearchBarVisible) + Padding( + padding: const EdgeInsets.all(8.0), + child: _buildSearchBar(), + ), + Expanded( + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'DashboardView is working', + style: TextStyle(fontSize: 20), + ), + Text('Time: ${controller.now.value.toString()}'), + ], + ), + ), + ), + ], + ); + } + }, + ); + }, + ), + ); + } + + Widget _buildSearchBar() { + return TextField( + decoration: InputDecoration( + hintText: 'Search...', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), ), + prefixIcon: Icon(Icons.search), ), ); } } + diff --git a/lib/app/modules/login/controllers/login_controller.dart b/lib/app/modules/login/controllers/login_controller.dart index 5178fec9..ce2df429 100644 --- a/lib/app/modules/login/controllers/login_controller.dart +++ b/lib/app/modules/login/controllers/login_controller.dart @@ -1,11 +1,59 @@ +// // import 'package:get/get.dart'; +// // +// // import '../../../../services/auth_service.dart'; +// // +// // class LoginController extends GetxController { +// // static AuthService get to => Get.find(); +// // +// // final Rx showReverificationButton = Rx(false); +// // +// // bool get isRobot => AuthService.to.robot.value == true; +// // +// // set robot(bool v) => AuthService.to.robot.value = v; +// // +// // bool get isLoggedIn => AuthService.to.isLoggedInValue; +// // +// // bool get isAnon => AuthService.to.isAnon; +// // +// // bool get isRegistered => +// // AuthService.to.registered.value || AuthService.to.isEmailVerified; +// // // } +// import 'package:firebase_auth/firebase_auth.dart' as fba; +// import 'package:get/get.dart'; +// import '../../../../services/auth_service.dart'; +// +// class LoginController extends GetxController { +// static AuthService get to => Get.find(); +// +// final Rx showReverificationButton = Rx(false); +// final Rx verificationId = ''.obs; +// final Rxn credential = Rxn(); +// final Rx isPhoneVerified = false.obs; +// +// bool get isRobot => AuthService.to.robot.value == true; +// +// set robot(bool v) => AuthService.to.robot.value = v; +// +// bool get isLoggedIn => AuthService.to.isLoggedInValue; +// +// bool get isAnon => AuthService.to.isAnon; +// +// bool get isRegistered => AuthService.to.registered.value || AuthService.to.isEmailVerified; +// +// } +import 'package:firebase_auth/firebase_auth.dart' as fba; +import 'package:firebase_auth/firebase_auth.dart'; import 'package:get/get.dart'; - import '../../../../services/auth_service.dart'; class LoginController extends GetxController { static AuthService get to => Get.find(); final Rx showReverificationButton = Rx(false); + final Rx verificationId = ''.obs; + final Rxn credential = + Rxn(); + final Rx isPhoneVerified = false.obs; // New observable bool get isRobot => AuthService.to.robot.value == true; @@ -17,4 +65,5 @@ class LoginController extends GetxController { bool get isRegistered => AuthService.to.registered.value || AuthService.to.isEmailVerified; + } diff --git a/lib/app/modules/login/views/login_view.dart b/lib/app/modules/login/views/login_view.dart index 00c3af3f..ce0ea586 100644 --- a/lib/app/modules/login/views/login_view.dart +++ b/lib/app/modules/login/views/login_view.dart @@ -16,6 +16,7 @@ class LoginView extends GetView { bool show, fba.EmailAuthCredential? credential) { // Below is very important. // See [https://stackoverflow.com/questions/69351845/this-obx-widget-cannot-be-marked-as-needing-to-build-because-the-framework-is-al] + WidgetsBinding.instance.addPostFrameCallback((_) { controller.showReverificationButton.value = show; }); @@ -149,6 +150,61 @@ class EmailLinkButton extends StatelessWidget { }); @override + // Widget loginScreen(BuildContext context) { + // Widget ui; + // bool showPhoneVerification = controller.showReverificationButton.value; + // + // if (!controller.isLoggedIn) { + // ui = !(GetPlatform.isAndroid || GetPlatform.isIOS) && controller.isRobot + // ? recaptcha() + // : SignInScreen( + // providers: [ + // GoogleProvider(clientId: DefaultFirebaseOptions.webClientId), + // MyEmailAuthProvider(), + // ], + // showAuthActionSwitch: !controller.isRegistered, + // showPasswordVisibilityToggle: true, + // headerBuilder: LoginWidgets.headerBuilder, + // subtitleBuilder: subtitleBuilder, + // footerBuilder: (context, action) => footerBuilder( + // controller.showReverificationButton, + // LoginController.to.credential), + // sideBuilder: LoginWidgets.sideBuilder, + // actions: getActions(), + // ); + // } else if (controller.isAnon) { + // ui = RegisterScreen( + // providers: [ + // MyEmailAuthProvider(), + // ], + // showAuthActionSwitch: !controller.isAnon, //if Anon only SignUp + // showPasswordVisibilityToggle: true, + // headerBuilder: LoginWidgets.headerBuilder, + // subtitleBuilder: subtitleBuilder, + // footerBuilder: (context, action) => footerBuilder( + // controller.showReverificationButton, LoginController.to.credential), + // sideBuilder: LoginWidgets.sideBuilder, + // actions: getActions(), + // ); + // } else { + // final thenTo = Get + // .rootDelegate.currentConfiguration!.currentPage!.parameters?['then']; + // Get.rootDelegate.offNamed(thenTo ?? + // (controller.isRegistered ? Screen.HOME : Screen.REGISTER).route); + // ui = const Scaffold(); + // } + // + // // Add phone verification UI below the existing UI components + // return Column( + // crossAxisAlignment: CrossAxisAlignment.stretch, + // children: [ + // ui, // Existing UI + // if (showPhoneVerification) + // phoneVerificationUI(context), + // ], + // ); + // } + Widget build(BuildContext context) { return Obx(() => Visibility( visible: show.value, diff --git a/lib/app/modules/login/views/phone_verification_view.dart b/lib/app/modules/login/views/phone_verification_view.dart new file mode 100644 index 00000000..960f3362 --- /dev/null +++ b/lib/app/modules/login/views/phone_verification_view.dart @@ -0,0 +1,53 @@ +// import 'package:flutter/material.dart'; +// import 'package:get/get.dart'; +// import '../controllers/login_controller.dart'; +// +// class PhoneVerificationView extends StatelessWidget { +// final TextEditingController phoneController = TextEditingController(); +// final TextEditingController codeController = TextEditingController(); +// +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// appBar: AppBar( +// title: Text('Phone Verification'), +// ), +// body: Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// children: [ +// TextField( +// controller: phoneController, +// decoration: InputDecoration(labelText: 'Phone Number'), +// ), +// ElevatedButton( +// onPressed: () { +// LoginController.to.verifyPhoneNumber( +// phoneController.text, +// (credential) { +// // Handle verification completed +// Get.snackbar('Success', 'Phone number verified'); +// }, +// ); +// }, +// child: Text('Verify Phone Number'), +// ), +// TextField( +// controller: codeController, +// decoration: InputDecoration(labelText: 'Verification Code'), +// ), +// ElevatedButton( +// onPressed: () { +// LoginController.to.signInWithPhoneNumber( +// LoginController.to.verificationId.value, +// codeController.text, +// ); +// }, +// child: Text('Sign In'), +// ), +// ], +// ), +// ), +// ); +// } +// } diff --git a/lib/app/modules/profile/controllers/profile_controller.dart b/lib/app/modules/profile/controllers/profile_controller.dart index 0c1e059e..f09baa3a 100644 --- a/lib/app/modules/profile/controllers/profile_controller.dart +++ b/lib/app/modules/profile/controllers/profile_controller.dart @@ -60,3 +60,94 @@ class ProfileController extends GetxController { Get.snackbar('Success', 'Picture stored and linked'); } } +// import 'dart:io'; +// import 'package:firebase_auth/firebase_auth.dart'; +// import 'package:firebase_storage/firebase_storage.dart'; +// import 'package:flutter/material.dart'; +// import 'package:get/get.dart'; +// import 'package:get_storage/get_storage.dart'; +// import 'package:path/path.dart'; +// import '../../../../services/auth_service.dart'; +// +// // Define the Persona class +// class Persona { +// final String name; +// final String imagePath; +// final bool isDarkTheme; +// +// Persona({required this.name, required this.imagePath, required this.isDarkTheme}); +// } +// +// // List of personas +// final List personas = [ +// Persona(name: 'Default', imagePath: 'assets/persona/default.png', isDarkTheme: false), +// Persona(name: 'Kids', imagePath: 'assets/persona/kids.png', isDarkTheme: false), +// Persona(name: 'Dark', imagePath: 'assets/persona/dark.png', isDarkTheme: true), +// ]; +// +// class ProfileController extends GetxController { +// FirebaseStorage storage = FirebaseStorage.instance; +// User? currentUser = AuthService.to.user; +// final Rxn _photoURL = Rxn(); +// final RxString selectedPersona = 'Default'.obs; +// +// File? _photo; +// +// String? get photoURL => _photoURL.value; +// +// @override +// void onInit() { +// super.onInit(); +// _photoURL.value = currentUser!.photoURL; +// _photoURL.bindStream(currentUser!.photoURL.obs.stream); +// selectedPersona.value = GetStorage().read('selectedPersona') ?? 'Default'; +// } +// +// Future uploadFile(String path) async { +// try { +// var byt = GetStorage().read(path); +// if (byt != null) { +// final fileName = path; +// final destination = 'profilePics/${currentUser!.uid}'; +// +// final ref = storage.ref(destination).child(fileName); +// await ref.putData(byt); +// return "$destination/$fileName"; +// } else { +// _photo = File(path); +// if (_photo == null) return null; +// final fileName = basename(_photo!.path); +// final destination = 'profilePics/${currentUser!.uid}'; +// +// final ref = storage.ref(destination).child(fileName); +// await ref.putFile(_photo!); +// return "$destination/$fileName"; +// } +// } catch (e) { +// Get.snackbar('Error', 'Image Not Uploaded as ${e.toString()}'); +// } +// return null; +// } +// +// Future updatePhotoURL(String dest) async { +// _photoURL.value = await storage.ref().child(dest).getDownloadURL(); +// await currentUser?.updatePhotoURL(_photoURL.value); +// Get.snackbar('Success', 'Picture stored and linked'); +// } +// +// void logout() { +// AuthService.to.logout(); +// } +// +// void updatePersona(String persona) { +// selectedPersona.value = persona; +// GetStorage().write('selectedPersona', persona); +// applyPersonaSettings(persona); +// } +// +// void applyPersonaSettings(String persona) { +// final selected = personas.firstWhere((p) => p.name == persona); +// Get.changeTheme(selected.isDarkTheme ? ThemeData.dark() : ThemeData.light()); +// // Add any other persona-specific settings here +// } +// } diff --git a/lib/app/modules/profile/views/profile_view.dart b/lib/app/modules/profile/views/profile_view.dart index cd9b8e28..7b834176 100644 --- a/lib/app/modules/profile/views/profile_view.dart +++ b/lib/app/modules/profile/views/profile_view.dart @@ -123,3 +123,161 @@ class ProfileView extends GetView { onConfirm: dlg.onSubmit); } } + +// import 'package:firebase_ui_auth/firebase_ui_auth.dart'; +// import 'package:flutter/material.dart'; +// import 'package:get/get.dart'; +// +// import '../../../../services/auth_service.dart'; +// import '../../../../models/screens.dart'; +// import '../../../utils/img_constants.dart'; +// import '../../../widgets/change_password_dialog.dart'; +// import '../../../widgets/image_picker_button.dart'; +// import '../controllers/profile_controller.dart'; +// +// class ProfileView extends GetView { +// const ProfileView({super.key}); +// ShapeBorder get shape => const CircleBorder(); +// double get size => 120; +// Color get placeholderColor => Colors.grey; +// +// Widget _imageFrameBuilder( +// BuildContext context, +// Widget? child, +// int? frame, +// bool? _, +// ) { +// if (frame == null) { +// return Container(color: placeholderColor); +// } +// +// return child!; +// } +// +// @override +// Widget build(BuildContext context) { +// return Obx(() => profileScreen()); +// } +// +// Widget profileScreen() { +// return AuthService.to.isLoggedInValue +// ? ProfileScreen( +// avatar: SizedBox( +// height: size, +// width: size, +// child: ClipPath( +// clipper: ShapeBorderClipper(shape: shape), +// clipBehavior: Clip.hardEdge, +// child: controller.photoURL != null +// ? Image.network( +// controller.photoURL!, +// width: size, +// height: size, +// cacheWidth: size.toInt(), +// cacheHeight: size.toInt(), +// fit: BoxFit.contain, +// frameBuilder: _imageFrameBuilder, +// ) +// : Center( +// child: Image.asset( +// ImgConstants.dash, +// width: size, +// fit: BoxFit.contain, +// ), +// ), +// ), +// ), +// actions: [ +// SignedOutAction((context) { +// Get.back(); +// controller.logout(); +// Get.rootDelegate.toNamed(Screen.PROFILE.route); +// }), +// AccountDeletedAction((context, user) { +// Get.defaultDialog( +// title: 'Deleted Account of ${user.displayName}', +// barrierDismissible: true, +// navigatorKey: Get.nestedKey(Screen.HOME.route), +// ); +// }) +// ], +// children: [ +// const Divider(), +// controller.currentUser?.email != null +// ? TextButton.icon( +// onPressed: callChangePwdDialog, +// label: const Text('Change Password'), +// icon: const Icon(Icons.password_rounded), +// ) +// : const SizedBox.shrink(), +// ImagePickerButton(callback: (String? path) async { +// if (path != null) { +// String? dest = await controller.uploadFile(path); +// if (dest != null) { +// await controller.updatePhotoURL(dest); +// } +// } +// }), +// const Divider(), +// Padding( +// padding: const EdgeInsets.all(8.0), +// child: Text('Select Persona:', style: TextStyle(fontSize: 18)), +// ), +// Expanded( +// child: PersonaSelection( +// personas: personas, +// onPersonaSelected: (persona) { +// controller.updatePersona(persona); +// }, +// ), +// ), +// ], +// ) +// : const Scaffold(); +// } +// +// void callChangePwdDialog() { +// var dlg = ChangePasswordDialog(controller.currentUser!); +// Get.defaultDialog( +// title: "Change Password", +// content: dlg, +// textConfirm: "Submit", +// textCancel: "Cancel", +// onConfirm: dlg.onSubmit); +// } +// } +// +// class PersonaSelection extends StatelessWidget { +// final List personas; +// final ValueChanged onPersonaSelected; +// +// PersonaSelection({required this.personas, required this.onPersonaSelected}); +// +// @override +// Widget build(BuildContext context) { +// return GridView.builder( +// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( +// crossAxisCount: 2, +// childAspectRatio: 1, +// ), +// itemCount: personas.length, +// itemBuilder: (context, index) { +// final persona = personas[index]; +// return GestureDetector( +// onTap: () { +// onPersonaSelected(persona.name); +// Get.snackbar('Persona Selected', '${persona.name} persona applied!'); +// }, +// child: Card( +// child: Column( +// children: [ +// Image.asset(persona.imagePath, height: 100, width: 100), +// Text(persona.name), +// ], +// ), +// ), +// ); +// }, +// ); +// } +// } diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart index 8bf72aaa..832b7c85 100644 --- a/lib/services/auth_service.dart +++ b/lib/services/auth_service.dart @@ -181,6 +181,8 @@ class AuthService extends GetxService { }; }; } + + //void verifyPhoneNumber(String text, Null Function(dynamic credential) param1) {} } class MyCredential extends AuthCredential { @@ -199,3 +201,4 @@ parseEmail(String message) { int j = message.indexOf('"', i); return message.substring(i, j - 1); } + diff --git a/lib/services/phone_auth_screen.dart b/lib/services/phone_auth_screen.dart new file mode 100644 index 00000000..d2c34f59 --- /dev/null +++ b/lib/services/phone_auth_screen.dart @@ -0,0 +1,111 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; + +class PhoneAuthScreen extends StatefulWidget { + @override + _PhoneAuthScreenState createState() => _PhoneAuthScreenState(); +} + +class _PhoneAuthScreenState extends State { + late String _verificationId; + late TextEditingController _phoneNumberController; + late TextEditingController _smsController; + + @override + void initState() { + super.initState(); + _phoneNumberController = TextEditingController(); + _smsController = TextEditingController(); + } + + @override + void dispose() { + _phoneNumberController.dispose(); + _smsController.dispose(); + super.dispose(); + } + + Future _verifyPhoneNumber() async { + await FirebaseAuth.instance.verifyPhoneNumber( + phoneNumber: '+91${_phoneNumberController.text}', + verificationCompleted: (PhoneAuthCredential credential) async { + // Auto-retrieve the SMS code and sign in + await FirebaseAuth.instance.signInWithCredential(credential); + // Navigate to your desired screen after authentication + }, + verificationFailed: (FirebaseAuthException e) { + // Handle verification failed + print(e.message); + }, + codeSent: (String verificationId, int? resendToken) { + setState(() { + _verificationId = verificationId; + }); + }, + codeAutoRetrievalTimeout: (String verificationId) { + setState(() { + _verificationId = verificationId; + }); + }, + ); + } + + void _signInWithPhoneNumber() async { + try { + final credential = PhoneAuthProvider.credential( + verificationId: _verificationId, + smsCode: _smsController.text, + ); + await FirebaseAuth.instance.signInWithCredential(credential); + // Navigate to your desired screen after authentication + } catch (e) { + print(e.toString()); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Phone Authentication'), + ), + body: Padding( + padding: EdgeInsets.all(16.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + TextField( + controller: _phoneNumberController, + decoration: InputDecoration(labelText: 'Enter phone number'), + keyboardType: TextInputType.phone, + ), + SizedBox(height: 16.0), + ElevatedButton( + onPressed: _verifyPhoneNumber, + child: Text('Verify Phone Number'), + ), + SizedBox(height: 16.0), + Visibility( + visible: _verificationId != null, + child: Column( + children: [ + TextField( + controller: _smsController, + decoration: InputDecoration(labelText: 'Enter SMS Code'), + keyboardType: TextInputType.number, + ), + SizedBox(height: 16.0), + ElevatedButton( + onPressed: _signInWithPhoneNumber, + child: Text('Sign In with SMS Code'), + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 877fc75e..886c9f62 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -597,6 +597,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: "direct main" description: @@ -685,6 +693,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + provider: + dependency: "direct main" + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index d000e5f5..bf263fec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,6 +10,7 @@ dependencies: get: 4.6.6 flutter: sdk: flutter + provider: ^6.1.2 firebase_core: ^2.31.0 firebase_ui_auth: ^1.14.0 firebase_auth: ^4.19.5 From 134d8a4d0a8a54c3d857c833c20dc769e913123f Mon Sep 17 00:00:00 2001 From: neminsheth <102215781+neminsheth@users.noreply.github.com> Date: Fri, 19 Jul 2024 18:16:55 +0530 Subject: [PATCH 16/20] Removed firebase.json I had already added firebase.json to .gitignore, but I'm not sure how it got committed. Have removed it now. --- firebase.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 firebase.json diff --git a/firebase.json b/firebase.json deleted file mode 100644 index 6643fe7b..00000000 --- a/firebase.json +++ /dev/null @@ -1 +0,0 @@ -{"flutter":{"platforms":{"dart":{"lib/firebase_options.dart":{"projectId":"sharekhan-nemin","configurations":{"android":"1:56053565487:android:f0e57a6359c84f97cb5eda","ios":"1:56053565487:ios:3515d315addadb07cb5eda","macos":"1:56053565487:ios:3515d315addadb07cb5eda","web":"1:56053565487:web:bdde9b35d35b497ccb5eda","windows":"1:56053565487:web:5c35ec65002974a6cb5eda"}}},"ios":{"default":{"projectId":"sharekhan-nemin","appId":"1:56053565487:ios:3515d315addadb07cb5eda","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"android":{"default":{"projectId":"sharekhan-nemin","appId":"1:56053565487:android:f0e57a6359c84f97cb5eda","fileOutput":"android/app/google-services.json"}},"macos":{"default":{"projectId":"sharekhan-nemin","appId":"1:56053565487:ios:3515d315addadb07cb5eda","uploadDebugSymbols":false,"fileOutput":"macos/Runner/GoogleService-Info.plist"}}}}} \ No newline at end of file From 1dee2ce5cbb0f2d1eaac004df4b1ba101784d725 Mon Sep 17 00:00:00 2001 From: Anshuman Das Date: Sat, 20 Jul 2024 12:00:32 +0530 Subject: [PATCH 17/20] fixed async bug --- lib/app/widgets/remotely_config_obx.dart | 13 ------------- lib/app/widgets/screen_widget.dart | 13 ++++++------- lib/models/screens.dart | 2 +- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/lib/app/widgets/remotely_config_obx.dart b/lib/app/widgets/remotely_config_obx.dart index a1ea63db..684503c0 100644 --- a/lib/app/widgets/remotely_config_obx.dart +++ b/lib/app/widgets/remotely_config_obx.dart @@ -8,8 +8,6 @@ class RemoteConfigController extends GetxController { final Rx values; } -/// final _name = "GetX".obs; -/// Obx(() => Text( _name.value )),... ; class RemotelyConfigObx>> extends ObxWidget { final Widget Function() builder; @@ -34,17 +32,6 @@ class RemotelyConfigObx>> Widget build() => builder(); } -/// Similar to Obx, but manages a local state. -/// Pass the initial data in constructor. -/// Useful for simple local states, like toggles, visibility, themes, -/// button states, etc. -/// Sample: -/// ObxValue((data) => Switch( -/// value: data.value, -/// onChanged: (flag) => data.value = flag, -/// ), -/// false.obs, -/// ), class RemotelyConfigObxVal extends ObxWidget { final Widget Function(T) builder; final T data; diff --git a/lib/app/widgets/screen_widget.dart b/lib/app/widgets/screen_widget.dart index edb4ead1..438f91e5 100644 --- a/lib/app/widgets/screen_widget.dart +++ b/lib/app/widgets/screen_widget.dart @@ -93,13 +93,12 @@ extension ScreenWidgetExtension on Screen { //This could be used to add icon buttons in expanded web view instead of the context menu static List topRightMenuButtons(GetNavConfig current) { List widgets = []; - Screen.topRightMenu().then((v) { - for (var screen in v) { - widgets.add(Container( - margin: const EdgeInsets.only(right: 15), - child: screen.widget(current))); - } - }); + for (var screen in Screen.topRightMenu()) { + widgets.add(Container( + margin: const EdgeInsets.only(right: 15), + child: screen.widget(current))); + } + return widgets; //This will return empty. We need a Obx } } diff --git a/lib/models/screens.dart b/lib/models/screens.dart index eb89b5a5..585f29f5 100644 --- a/lib/models/screens.dart +++ b/lib/models/screens.dart @@ -184,7 +184,7 @@ enum Screen implements ActionEnum { return list; } - static Future> topRightMenu() async { + static Iterable topRightMenu() { return Screen.values.where((Screen screen) => screen.accessor_ == AccessedVia.topRight && AuthService.to.accessLevel.index >= screen.accessLevel.index); From 170bedbdda9d5904c07d32546a87d30a7c533c1c Mon Sep 17 00:00:00 2001 From: Anshuman Das Date: Sat, 20 Jul 2024 12:29:35 +0530 Subject: [PATCH 18/20] Revert "Bugs, ToDo 13.3" --- README.md | 2 +- devtools_options.yaml | 3 - ios/Flutter/Generated.xcconfig | 14 -- ios/Flutter/flutter_export_environment.sh | 13 -- ios/Runner/GeneratedPluginRegistrant.h | 19 --- ios/Runner/GeneratedPluginRegistrant.m | 91 ---------- .../controllers/dashboard_controller.dart | 5 - .../dashboard/views/dashboard_view.dart | 137 ++------------- .../login/controllers/login_controller.dart | 51 +----- lib/app/modules/login/views/login_view.dart | 56 ------- .../login/views/phone_verification_view.dart | 53 ------ .../controllers/profile_controller.dart | 91 ---------- .../modules/profile/views/profile_view.dart | 158 ------------------ lib/services/auth_service.dart | 3 - lib/services/phone_auth_screen.dart | 111 ------------ .../ephemeral/Flutter-Generated.xcconfig | 11 -- .../ephemeral/flutter_export_environment.sh | 12 -- pubspec.lock | 16 -- pubspec.yaml | 1 - 19 files changed, 13 insertions(+), 834 deletions(-) delete mode 100644 devtools_options.yaml delete mode 100644 ios/Flutter/Generated.xcconfig delete mode 100644 ios/Flutter/flutter_export_environment.sh delete mode 100644 ios/Runner/GeneratedPluginRegistrant.h delete mode 100644 ios/Runner/GeneratedPluginRegistrant.m delete mode 100644 lib/app/modules/login/views/phone_verification_view.dart delete mode 100644 lib/services/phone_auth_screen.dart delete mode 100644 macos/Flutter/ephemeral/Flutter-Generated.xcconfig delete mode 100644 macos/Flutter/ephemeral/flutter_export_environment.sh diff --git a/README.md b/README.md index 7d403519..be46f067 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Step 5: Add Guest User/Anonymous login with a Cart and Checkout use case [https: * delete unlinked anonymous user post logout -Step 6: Add ImagePicker and Firebase Storage for profile image (Done) +Step 6: Add ImagePicker and Firebase Storage for profile image * Create PopupMenu button for web [https://api.flutter.dev/flutter/material/PopupMenuButton-class.html] * BottomSheet for phones and single file button for desktops diff --git a/devtools_options.yaml b/devtools_options.yaml deleted file mode 100644 index fa0b357c..00000000 --- a/devtools_options.yaml +++ /dev/null @@ -1,3 +0,0 @@ -description: This file stores settings for Dart & Flutter DevTools. -documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states -extensions: diff --git a/ios/Flutter/Generated.xcconfig b/ios/Flutter/Generated.xcconfig deleted file mode 100644 index ebe7b0ca..00000000 --- a/ios/Flutter/Generated.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// This is a generated file; do not edit or check into version control. -FLUTTER_ROOT=C:\Users\Aastha Gadhvi\Downloads\flutter_windows_3.10.5-stable\flutter -FLUTTER_APPLICATION_PATH=C:\Users\Aastha Gadhvi\get-flutter-fire -COCOAPODS_PARALLEL_CODE_SIGN=true -FLUTTER_TARGET=lib\main.dart -FLUTTER_BUILD_DIR=build -FLUTTER_BUILD_NAME=1.0.0 -FLUTTER_BUILD_NUMBER=1 -EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386 -EXCLUDED_ARCHS[sdk=iphoneos*]=armv7 -DART_OBFUSCATION=false -TRACK_WIDGET_CREATION=true -TREE_SHAKE_ICONS=false -PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/ios/Flutter/flutter_export_environment.sh b/ios/Flutter/flutter_export_environment.sh deleted file mode 100644 index e9e43111..00000000 --- a/ios/Flutter/flutter_export_environment.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -# This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=C:\Users\Aastha Gadhvi\Downloads\flutter_windows_3.10.5-stable\flutter" -export "FLUTTER_APPLICATION_PATH=C:\Users\Aastha Gadhvi\get-flutter-fire" -export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=lib\main.dart" -export "FLUTTER_BUILD_DIR=build" -export "FLUTTER_BUILD_NAME=1.0.0" -export "FLUTTER_BUILD_NUMBER=1" -export "DART_OBFUSCATION=false" -export "TRACK_WIDGET_CREATION=true" -export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/ios/Runner/GeneratedPluginRegistrant.h b/ios/Runner/GeneratedPluginRegistrant.h deleted file mode 100644 index 7a890927..00000000 --- a/ios/Runner/GeneratedPluginRegistrant.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GeneratedPluginRegistrant_h -#define GeneratedPluginRegistrant_h - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface GeneratedPluginRegistrant : NSObject -+ (void)registerWithRegistry:(NSObject*)registry; -@end - -NS_ASSUME_NONNULL_END -#endif /* GeneratedPluginRegistrant_h */ diff --git a/ios/Runner/GeneratedPluginRegistrant.m b/ios/Runner/GeneratedPluginRegistrant.m deleted file mode 100644 index 11a184c0..00000000 --- a/ios/Runner/GeneratedPluginRegistrant.m +++ /dev/null @@ -1,91 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#import "GeneratedPluginRegistrant.h" - -#if __has_include() -#import -#else -@import desktop_webview_auth; -#endif - -#if __has_include() -#import -#else -@import file_picker; -#endif - -#if __has_include() -#import -#else -@import firebase_analytics; -#endif - -#if __has_include() -#import -#else -@import firebase_auth; -#endif - -#if __has_include() -#import -#else -@import firebase_core; -#endif - -#if __has_include() -#import -#else -@import firebase_dynamic_links; -#endif - -#if __has_include() -#import -#else -@import firebase_remote_config; -#endif - -#if __has_include() -#import -#else -@import firebase_storage; -#endif - -#if __has_include() -#import -#else -@import google_sign_in_ios; -#endif - -#if __has_include() -#import -#else -@import image_picker_ios; -#endif - -#if __has_include() -#import -#else -@import path_provider_foundation; -#endif - -@implementation GeneratedPluginRegistrant - -+ (void)registerWithRegistry:(NSObject*)registry { - [DesktopWebviewAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"DesktopWebviewAuthPlugin"]]; - [FilePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FilePickerPlugin"]]; - [FLTFirebaseAnalyticsPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAnalyticsPlugin"]]; - [FLTFirebaseAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAuthPlugin"]]; - [FLTFirebaseCorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseCorePlugin"]]; - [FLTFirebaseDynamicLinksPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseDynamicLinksPlugin"]]; - [FLTFirebaseRemoteConfigPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseRemoteConfigPlugin"]]; - [FLTFirebaseStoragePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseStoragePlugin"]]; - [FLTGoogleSignInPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleSignInPlugin"]]; - [FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]]; - [PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]]; -} - -@end diff --git a/lib/app/modules/dashboard/controllers/dashboard_controller.dart b/lib/app/modules/dashboard/controllers/dashboard_controller.dart index 4732b7b6..24d91a16 100644 --- a/lib/app/modules/dashboard/controllers/dashboard_controller.dart +++ b/lib/app/modules/dashboard/controllers/dashboard_controller.dart @@ -14,9 +14,4 @@ class DashboardController extends GetxController { }, ); } - var isSearchBarVisible = true.obs; - void toggleSearchBarVisibility() { - isSearchBarVisible.value = !isSearchBarVisible.value; - } - // Observable state for current time (example) } diff --git a/lib/app/modules/dashboard/views/dashboard_view.dart b/lib/app/modules/dashboard/views/dashboard_view.dart index 648f164a..f475030f 100644 --- a/lib/app/modules/dashboard/views/dashboard_view.dart +++ b/lib/app/modules/dashboard/views/dashboard_view.dart @@ -1,36 +1,6 @@ -// import 'package:flutter/material.dart'; -// import 'package:get/get.dart'; -// -// import '../controllers/dashboard_controller.dart'; -// -// class DashboardView extends GetView { -// const DashboardView({super.key}); -// -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// body: Center( -// child: Obx( -// () => Column( -// mainAxisSize: MainAxisSize.min, -// children: [ -// const Text( -// 'DashboardView is working', -// style: TextStyle(fontSize: 20), -// ), -// Text('Time: ${controller.now.value.toString()}'), -// ], -// ), -// ), -// ), -// ); -// } -// } import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:firebase_remote_config/firebase_remote_config.dart'; -import '../../../../services/remote_config.dart'; import '../controllers/dashboard_controller.dart'; class DashboardView extends GetView { @@ -39,105 +9,20 @@ class DashboardView extends GetView { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('Dashboard'), - centerTitle: true, - actions: [ - GetBuilder( - builder: (controller) { - return FutureBuilder( - future: RemoteConfig.instance.then((config) => config.showSearchBarOnTop()), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError || !snapshot.data!) { - return const SizedBox.shrink(); - } else { - return GetPlatform.isMobile - ? PopupMenuButton( - onSelected: (value) { - if (value == 'toggleSearchBar') { - controller.toggleSearchBarVisibility(); - } - }, - itemBuilder: (BuildContext context) { - return [ - PopupMenuItem( - value: 'toggleSearchBar', - child: Row( - children: [ - const Icon(Icons.search), - const SizedBox(width: 8), - const Text('Toggle Search Bar'), - ], - ), - ), - ]; - }, - ) - : const SizedBox.shrink(); - } - }, - ); - }, + body: Center( + child: Obx( + () => Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'DashboardView is working', + style: TextStyle(fontSize: 20), + ), + Text('Time: ${controller.now.value.toString()}'), + ], ), - ], - ), - body: Obx( - () { - // Use the controller's state for search bar visibility - bool isSearchBarVisible = controller.isSearchBarVisible.value; - - return FutureBuilder( - future: RemoteConfig.instance.then((config) => config.showSearchBarOnTop()), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError) { - return Center(child: Text('Error: ${snapshot.error}')); - } else { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (isSearchBarVisible) - Padding( - padding: const EdgeInsets.all(8.0), - child: _buildSearchBar(), - ), - Expanded( - child: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Text( - 'DashboardView is working', - style: TextStyle(fontSize: 20), - ), - Text('Time: ${controller.now.value.toString()}'), - ], - ), - ), - ), - ], - ); - } - }, - ); - }, - ), - ); - } - - Widget _buildSearchBar() { - return TextField( - decoration: InputDecoration( - hintText: 'Search...', - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0), ), - prefixIcon: Icon(Icons.search), ), ); } } - diff --git a/lib/app/modules/login/controllers/login_controller.dart b/lib/app/modules/login/controllers/login_controller.dart index ce2df429..5178fec9 100644 --- a/lib/app/modules/login/controllers/login_controller.dart +++ b/lib/app/modules/login/controllers/login_controller.dart @@ -1,59 +1,11 @@ -// // import 'package:get/get.dart'; -// // -// // import '../../../../services/auth_service.dart'; -// // -// // class LoginController extends GetxController { -// // static AuthService get to => Get.find(); -// // -// // final Rx showReverificationButton = Rx(false); -// // -// // bool get isRobot => AuthService.to.robot.value == true; -// // -// // set robot(bool v) => AuthService.to.robot.value = v; -// // -// // bool get isLoggedIn => AuthService.to.isLoggedInValue; -// // -// // bool get isAnon => AuthService.to.isAnon; -// // -// // bool get isRegistered => -// // AuthService.to.registered.value || AuthService.to.isEmailVerified; -// // // } -// import 'package:firebase_auth/firebase_auth.dart' as fba; -// import 'package:get/get.dart'; -// import '../../../../services/auth_service.dart'; -// -// class LoginController extends GetxController { -// static AuthService get to => Get.find(); -// -// final Rx showReverificationButton = Rx(false); -// final Rx verificationId = ''.obs; -// final Rxn credential = Rxn(); -// final Rx isPhoneVerified = false.obs; -// -// bool get isRobot => AuthService.to.robot.value == true; -// -// set robot(bool v) => AuthService.to.robot.value = v; -// -// bool get isLoggedIn => AuthService.to.isLoggedInValue; -// -// bool get isAnon => AuthService.to.isAnon; -// -// bool get isRegistered => AuthService.to.registered.value || AuthService.to.isEmailVerified; -// -// } -import 'package:firebase_auth/firebase_auth.dart' as fba; -import 'package:firebase_auth/firebase_auth.dart'; import 'package:get/get.dart'; + import '../../../../services/auth_service.dart'; class LoginController extends GetxController { static AuthService get to => Get.find(); final Rx showReverificationButton = Rx(false); - final Rx verificationId = ''.obs; - final Rxn credential = - Rxn(); - final Rx isPhoneVerified = false.obs; // New observable bool get isRobot => AuthService.to.robot.value == true; @@ -65,5 +17,4 @@ class LoginController extends GetxController { bool get isRegistered => AuthService.to.registered.value || AuthService.to.isEmailVerified; - } diff --git a/lib/app/modules/login/views/login_view.dart b/lib/app/modules/login/views/login_view.dart index ce0ea586..00c3af3f 100644 --- a/lib/app/modules/login/views/login_view.dart +++ b/lib/app/modules/login/views/login_view.dart @@ -16,7 +16,6 @@ class LoginView extends GetView { bool show, fba.EmailAuthCredential? credential) { // Below is very important. // See [https://stackoverflow.com/questions/69351845/this-obx-widget-cannot-be-marked-as-needing-to-build-because-the-framework-is-al] - WidgetsBinding.instance.addPostFrameCallback((_) { controller.showReverificationButton.value = show; }); @@ -150,61 +149,6 @@ class EmailLinkButton extends StatelessWidget { }); @override - // Widget loginScreen(BuildContext context) { - // Widget ui; - // bool showPhoneVerification = controller.showReverificationButton.value; - // - // if (!controller.isLoggedIn) { - // ui = !(GetPlatform.isAndroid || GetPlatform.isIOS) && controller.isRobot - // ? recaptcha() - // : SignInScreen( - // providers: [ - // GoogleProvider(clientId: DefaultFirebaseOptions.webClientId), - // MyEmailAuthProvider(), - // ], - // showAuthActionSwitch: !controller.isRegistered, - // showPasswordVisibilityToggle: true, - // headerBuilder: LoginWidgets.headerBuilder, - // subtitleBuilder: subtitleBuilder, - // footerBuilder: (context, action) => footerBuilder( - // controller.showReverificationButton, - // LoginController.to.credential), - // sideBuilder: LoginWidgets.sideBuilder, - // actions: getActions(), - // ); - // } else if (controller.isAnon) { - // ui = RegisterScreen( - // providers: [ - // MyEmailAuthProvider(), - // ], - // showAuthActionSwitch: !controller.isAnon, //if Anon only SignUp - // showPasswordVisibilityToggle: true, - // headerBuilder: LoginWidgets.headerBuilder, - // subtitleBuilder: subtitleBuilder, - // footerBuilder: (context, action) => footerBuilder( - // controller.showReverificationButton, LoginController.to.credential), - // sideBuilder: LoginWidgets.sideBuilder, - // actions: getActions(), - // ); - // } else { - // final thenTo = Get - // .rootDelegate.currentConfiguration!.currentPage!.parameters?['then']; - // Get.rootDelegate.offNamed(thenTo ?? - // (controller.isRegistered ? Screen.HOME : Screen.REGISTER).route); - // ui = const Scaffold(); - // } - // - // // Add phone verification UI below the existing UI components - // return Column( - // crossAxisAlignment: CrossAxisAlignment.stretch, - // children: [ - // ui, // Existing UI - // if (showPhoneVerification) - // phoneVerificationUI(context), - // ], - // ); - // } - Widget build(BuildContext context) { return Obx(() => Visibility( visible: show.value, diff --git a/lib/app/modules/login/views/phone_verification_view.dart b/lib/app/modules/login/views/phone_verification_view.dart deleted file mode 100644 index 960f3362..00000000 --- a/lib/app/modules/login/views/phone_verification_view.dart +++ /dev/null @@ -1,53 +0,0 @@ -// import 'package:flutter/material.dart'; -// import 'package:get/get.dart'; -// import '../controllers/login_controller.dart'; -// -// class PhoneVerificationView extends StatelessWidget { -// final TextEditingController phoneController = TextEditingController(); -// final TextEditingController codeController = TextEditingController(); -// -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// appBar: AppBar( -// title: Text('Phone Verification'), -// ), -// body: Padding( -// padding: const EdgeInsets.all(16.0), -// child: Column( -// children: [ -// TextField( -// controller: phoneController, -// decoration: InputDecoration(labelText: 'Phone Number'), -// ), -// ElevatedButton( -// onPressed: () { -// LoginController.to.verifyPhoneNumber( -// phoneController.text, -// (credential) { -// // Handle verification completed -// Get.snackbar('Success', 'Phone number verified'); -// }, -// ); -// }, -// child: Text('Verify Phone Number'), -// ), -// TextField( -// controller: codeController, -// decoration: InputDecoration(labelText: 'Verification Code'), -// ), -// ElevatedButton( -// onPressed: () { -// LoginController.to.signInWithPhoneNumber( -// LoginController.to.verificationId.value, -// codeController.text, -// ); -// }, -// child: Text('Sign In'), -// ), -// ], -// ), -// ), -// ); -// } -// } diff --git a/lib/app/modules/profile/controllers/profile_controller.dart b/lib/app/modules/profile/controllers/profile_controller.dart index f09baa3a..0c1e059e 100644 --- a/lib/app/modules/profile/controllers/profile_controller.dart +++ b/lib/app/modules/profile/controllers/profile_controller.dart @@ -60,94 +60,3 @@ class ProfileController extends GetxController { Get.snackbar('Success', 'Picture stored and linked'); } } -// import 'dart:io'; -// import 'package:firebase_auth/firebase_auth.dart'; -// import 'package:firebase_storage/firebase_storage.dart'; -// import 'package:flutter/material.dart'; -// import 'package:get/get.dart'; -// import 'package:get_storage/get_storage.dart'; -// import 'package:path/path.dart'; -// import '../../../../services/auth_service.dart'; -// -// // Define the Persona class -// class Persona { -// final String name; -// final String imagePath; -// final bool isDarkTheme; -// -// Persona({required this.name, required this.imagePath, required this.isDarkTheme}); -// } -// -// // List of personas -// final List personas = [ -// Persona(name: 'Default', imagePath: 'assets/persona/default.png', isDarkTheme: false), -// Persona(name: 'Kids', imagePath: 'assets/persona/kids.png', isDarkTheme: false), -// Persona(name: 'Dark', imagePath: 'assets/persona/dark.png', isDarkTheme: true), -// ]; -// -// class ProfileController extends GetxController { -// FirebaseStorage storage = FirebaseStorage.instance; -// User? currentUser = AuthService.to.user; -// final Rxn _photoURL = Rxn(); -// final RxString selectedPersona = 'Default'.obs; -// -// File? _photo; -// -// String? get photoURL => _photoURL.value; -// -// @override -// void onInit() { -// super.onInit(); -// _photoURL.value = currentUser!.photoURL; -// _photoURL.bindStream(currentUser!.photoURL.obs.stream); -// selectedPersona.value = GetStorage().read('selectedPersona') ?? 'Default'; -// } -// -// Future uploadFile(String path) async { -// try { -// var byt = GetStorage().read(path); -// if (byt != null) { -// final fileName = path; -// final destination = 'profilePics/${currentUser!.uid}'; -// -// final ref = storage.ref(destination).child(fileName); -// await ref.putData(byt); -// return "$destination/$fileName"; -// } else { -// _photo = File(path); -// if (_photo == null) return null; -// final fileName = basename(_photo!.path); -// final destination = 'profilePics/${currentUser!.uid}'; -// -// final ref = storage.ref(destination).child(fileName); -// await ref.putFile(_photo!); -// return "$destination/$fileName"; -// } -// } catch (e) { -// Get.snackbar('Error', 'Image Not Uploaded as ${e.toString()}'); -// } -// return null; -// } -// -// Future updatePhotoURL(String dest) async { -// _photoURL.value = await storage.ref().child(dest).getDownloadURL(); -// await currentUser?.updatePhotoURL(_photoURL.value); -// Get.snackbar('Success', 'Picture stored and linked'); -// } -// -// void logout() { -// AuthService.to.logout(); -// } -// -// void updatePersona(String persona) { -// selectedPersona.value = persona; -// GetStorage().write('selectedPersona', persona); -// applyPersonaSettings(persona); -// } -// -// void applyPersonaSettings(String persona) { -// final selected = personas.firstWhere((p) => p.name == persona); -// Get.changeTheme(selected.isDarkTheme ? ThemeData.dark() : ThemeData.light()); -// // Add any other persona-specific settings here -// } -// } diff --git a/lib/app/modules/profile/views/profile_view.dart b/lib/app/modules/profile/views/profile_view.dart index dbb1432a..1b6ed535 100644 --- a/lib/app/modules/profile/views/profile_view.dart +++ b/lib/app/modules/profile/views/profile_view.dart @@ -143,161 +143,3 @@ class ProfileView extends GetView { } } } - -// import 'package:firebase_ui_auth/firebase_ui_auth.dart'; -// import 'package:flutter/material.dart'; -// import 'package:get/get.dart'; -// -// import '../../../../services/auth_service.dart'; -// import '../../../../models/screens.dart'; -// import '../../../utils/img_constants.dart'; -// import '../../../widgets/change_password_dialog.dart'; -// import '../../../widgets/image_picker_button.dart'; -// import '../controllers/profile_controller.dart'; -// -// class ProfileView extends GetView { -// const ProfileView({super.key}); -// ShapeBorder get shape => const CircleBorder(); -// double get size => 120; -// Color get placeholderColor => Colors.grey; -// -// Widget _imageFrameBuilder( -// BuildContext context, -// Widget? child, -// int? frame, -// bool? _, -// ) { -// if (frame == null) { -// return Container(color: placeholderColor); -// } -// -// return child!; -// } -// -// @override -// Widget build(BuildContext context) { -// return Obx(() => profileScreen()); -// } -// -// Widget profileScreen() { -// return AuthService.to.isLoggedInValue -// ? ProfileScreen( -// avatar: SizedBox( -// height: size, -// width: size, -// child: ClipPath( -// clipper: ShapeBorderClipper(shape: shape), -// clipBehavior: Clip.hardEdge, -// child: controller.photoURL != null -// ? Image.network( -// controller.photoURL!, -// width: size, -// height: size, -// cacheWidth: size.toInt(), -// cacheHeight: size.toInt(), -// fit: BoxFit.contain, -// frameBuilder: _imageFrameBuilder, -// ) -// : Center( -// child: Image.asset( -// ImgConstants.dash, -// width: size, -// fit: BoxFit.contain, -// ), -// ), -// ), -// ), -// actions: [ -// SignedOutAction((context) { -// Get.back(); -// controller.logout(); -// Get.rootDelegate.toNamed(Screen.PROFILE.route); -// }), -// AccountDeletedAction((context, user) { -// Get.defaultDialog( -// title: 'Deleted Account of ${user.displayName}', -// barrierDismissible: true, -// navigatorKey: Get.nestedKey(Screen.HOME.route), -// ); -// }) -// ], -// children: [ -// const Divider(), -// controller.currentUser?.email != null -// ? TextButton.icon( -// onPressed: callChangePwdDialog, -// label: const Text('Change Password'), -// icon: const Icon(Icons.password_rounded), -// ) -// : const SizedBox.shrink(), -// ImagePickerButton(callback: (String? path) async { -// if (path != null) { -// String? dest = await controller.uploadFile(path); -// if (dest != null) { -// await controller.updatePhotoURL(dest); -// } -// } -// }), -// const Divider(), -// Padding( -// padding: const EdgeInsets.all(8.0), -// child: Text('Select Persona:', style: TextStyle(fontSize: 18)), -// ), -// Expanded( -// child: PersonaSelection( -// personas: personas, -// onPersonaSelected: (persona) { -// controller.updatePersona(persona); -// }, -// ), -// ), -// ], -// ) -// : const Scaffold(); -// } -// -// void callChangePwdDialog() { -// var dlg = ChangePasswordDialog(controller.currentUser!); -// Get.defaultDialog( -// title: "Change Password", -// content: dlg, -// textConfirm: "Submit", -// textCancel: "Cancel", -// onConfirm: dlg.onSubmit); -// } -// } -// -// class PersonaSelection extends StatelessWidget { -// final List personas; -// final ValueChanged onPersonaSelected; -// -// PersonaSelection({required this.personas, required this.onPersonaSelected}); -// -// @override -// Widget build(BuildContext context) { -// return GridView.builder( -// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( -// crossAxisCount: 2, -// childAspectRatio: 1, -// ), -// itemCount: personas.length, -// itemBuilder: (context, index) { -// final persona = personas[index]; -// return GestureDetector( -// onTap: () { -// onPersonaSelected(persona.name); -// Get.snackbar('Persona Selected', '${persona.name} persona applied!'); -// }, -// child: Card( -// child: Column( -// children: [ -// Image.asset(persona.imagePath, height: 100, width: 100), -// Text(persona.name), -// ], -// ), -// ), -// ); -// }, -// ); -// } -// } diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart index 908fdd62..a6bef783 100644 --- a/lib/services/auth_service.dart +++ b/lib/services/auth_service.dart @@ -190,8 +190,6 @@ class AuthService extends GetxService { }; }; } - - //void verifyPhoneNumber(String text, Null Function(dynamic credential) param1) {} } class MyCredential extends AuthCredential { @@ -210,4 +208,3 @@ parseEmail(String message) { int j = message.indexOf('"', i); return message.substring(i, j - 1); } - diff --git a/lib/services/phone_auth_screen.dart b/lib/services/phone_auth_screen.dart deleted file mode 100644 index d2c34f59..00000000 --- a/lib/services/phone_auth_screen.dart +++ /dev/null @@ -1,111 +0,0 @@ -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/material.dart'; - -class PhoneAuthScreen extends StatefulWidget { - @override - _PhoneAuthScreenState createState() => _PhoneAuthScreenState(); -} - -class _PhoneAuthScreenState extends State { - late String _verificationId; - late TextEditingController _phoneNumberController; - late TextEditingController _smsController; - - @override - void initState() { - super.initState(); - _phoneNumberController = TextEditingController(); - _smsController = TextEditingController(); - } - - @override - void dispose() { - _phoneNumberController.dispose(); - _smsController.dispose(); - super.dispose(); - } - - Future _verifyPhoneNumber() async { - await FirebaseAuth.instance.verifyPhoneNumber( - phoneNumber: '+91${_phoneNumberController.text}', - verificationCompleted: (PhoneAuthCredential credential) async { - // Auto-retrieve the SMS code and sign in - await FirebaseAuth.instance.signInWithCredential(credential); - // Navigate to your desired screen after authentication - }, - verificationFailed: (FirebaseAuthException e) { - // Handle verification failed - print(e.message); - }, - codeSent: (String verificationId, int? resendToken) { - setState(() { - _verificationId = verificationId; - }); - }, - codeAutoRetrievalTimeout: (String verificationId) { - setState(() { - _verificationId = verificationId; - }); - }, - ); - } - - void _signInWithPhoneNumber() async { - try { - final credential = PhoneAuthProvider.credential( - verificationId: _verificationId, - smsCode: _smsController.text, - ); - await FirebaseAuth.instance.signInWithCredential(credential); - // Navigate to your desired screen after authentication - } catch (e) { - print(e.toString()); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('Phone Authentication'), - ), - body: Padding( - padding: EdgeInsets.all(16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - TextField( - controller: _phoneNumberController, - decoration: InputDecoration(labelText: 'Enter phone number'), - keyboardType: TextInputType.phone, - ), - SizedBox(height: 16.0), - ElevatedButton( - onPressed: _verifyPhoneNumber, - child: Text('Verify Phone Number'), - ), - SizedBox(height: 16.0), - Visibility( - visible: _verificationId != null, - child: Column( - children: [ - TextField( - controller: _smsController, - decoration: InputDecoration(labelText: 'Enter SMS Code'), - keyboardType: TextInputType.number, - ), - SizedBox(height: 16.0), - ElevatedButton( - onPressed: _signInWithPhoneNumber, - child: Text('Sign In with SMS Code'), - ), - ], - ), - ), - ], - ), - ), - ); - } -} diff --git a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig deleted file mode 100644 index 75656d0a..00000000 --- a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig +++ /dev/null @@ -1,11 +0,0 @@ -// This is a generated file; do not edit or check into version control. -FLUTTER_ROOT=C:\Users\Aastha Gadhvi\Downloads\flutter_windows_3.10.5-stable\flutter -FLUTTER_APPLICATION_PATH=C:\Users\Aastha Gadhvi\get-flutter-fire -COCOAPODS_PARALLEL_CODE_SIGN=true -FLUTTER_BUILD_DIR=build -FLUTTER_BUILD_NAME=1.0.0 -FLUTTER_BUILD_NUMBER=1 -DART_OBFUSCATION=false -TRACK_WIDGET_CREATION=true -TREE_SHAKE_ICONS=false -PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/macos/Flutter/ephemeral/flutter_export_environment.sh b/macos/Flutter/ephemeral/flutter_export_environment.sh deleted file mode 100644 index accf248b..00000000 --- a/macos/Flutter/ephemeral/flutter_export_environment.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -# This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=C:\Users\Aastha Gadhvi\Downloads\flutter_windows_3.10.5-stable\flutter" -export "FLUTTER_APPLICATION_PATH=C:\Users\Aastha Gadhvi\get-flutter-fire" -export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_BUILD_DIR=build" -export "FLUTTER_BUILD_NAME=1.0.0" -export "FLUTTER_BUILD_NUMBER=1" -export "DART_OBFUSCATION=false" -export "TRACK_WIDGET_CREATION=true" -export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/pubspec.lock b/pubspec.lock index f7672bfa..d0fdd9cb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -597,14 +597,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" - nested: - dependency: transitive - description: - name: nested - sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" - source: hosted - version: "1.0.0" path: dependency: "direct main" description: @@ -693,14 +685,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" - provider: - dependency: "direct main" - description: - name: provider - sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c - url: "https://pub.dev" - source: hosted - version: "6.1.2" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index bf263fec..d000e5f5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,7 +10,6 @@ dependencies: get: 4.6.6 flutter: sdk: flutter - provider: ^6.1.2 firebase_core: ^2.31.0 firebase_ui_auth: ^1.14.0 firebase_auth: ^4.19.5 From 7fe9df8b29d8f98de5a11cea432c3f0226acf72c Mon Sep 17 00:00:00 2001 From: ITDhruv <120175094+ITDhruv@users.noreply.github.com> Date: Sat, 20 Jul 2024 13:23:11 +0530 Subject: [PATCH 19/20] final-conflicts-resolved --- lib/app/modules/cart/controllers/cart_controller.dart | 8 ++++---- lib/app/modules/profile/views/profile_view.dart | 2 -- lib/models/screens.dart | 4 ++++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/app/modules/cart/controllers/cart_controller.dart b/lib/app/modules/cart/controllers/cart_controller.dart index f4bb5440..c4588ef5 100644 --- a/lib/app/modules/cart/controllers/cart_controller.dart +++ b/lib/app/modules/cart/controllers/cart_controller.dart @@ -21,7 +21,7 @@ class CartController extends GetxController { } Future fetchCartItems() async { - final userId = 'your-user-id'; // Replace with the actual user ID + const userId = 'your-user-id'; // Replace with the actual user ID final snapshot = await _firestore .collection('carts') .doc(userId) @@ -36,7 +36,7 @@ class CartController extends GetxController { } Future addCartItem(CartItem item) async { - final userId = 'your-user-id'; // Replace with the actual user ID + const userId = 'your-user-id'; // Replace with the actual user ID await _firestore .collection('carts') .doc(userId) @@ -47,7 +47,7 @@ class CartController extends GetxController { } Future removeCartItem(String itemId) async { - final userId = 'your-user-id'; // Replace with the actual user ID + const userId = 'your-user-id'; // Replace with the actual user ID await _firestore .collection('carts') .doc(userId) @@ -59,7 +59,7 @@ class CartController extends GetxController { } Future updateCartItem(CartItem item) async { - final userId = 'your-user-id'; // Replace with the actual user ID + const userId = 'your-user-id'; // Replace with the actual user ID await _firestore .collection('carts') .doc(userId) diff --git a/lib/app/modules/profile/views/profile_view.dart b/lib/app/modules/profile/views/profile_view.dart index 1b6ed535..834db809 100644 --- a/lib/app/modules/profile/views/profile_view.dart +++ b/lib/app/modules/profile/views/profile_view.dart @@ -7,8 +7,6 @@ import 'package:get/get.dart'; import '../../../../services/auth_service.dart'; import '../../../../models/screens.dart'; -import '../../../utils/img_constants.dart'; -import '../../../widgets/change_password_dialog.dart'; import '../../../widgets/image_picker_button.dart'; import '../controllers/profile_controller.dart'; diff --git a/lib/models/screens.dart b/lib/models/screens.dart index 7e8dee15..700eb7b1 100644 --- a/lib/models/screens.dart +++ b/lib/models/screens.dart @@ -113,6 +113,10 @@ enum Screen implements ActionEnum { label: "Role Request", accessor_: AccessedVia.drawer, accessLevel: AccessLevel.authenticated), + LOGIN('/login', + icon: Icons.login, + accessor_: AccessedVia.topRight, + accessLevel: AccessLevel.notAuthed), ; const Screen(this.path, From ded704a1ab1eec0e44ce240dd56b6605982340a1 Mon Sep 17 00:00:00 2001 From: ITDhruv <120175094+ITDhruv@users.noreply.github.com> Date: Sat, 20 Jul 2024 13:37:40 +0530 Subject: [PATCH 20/20] removed-services.json --- android/app/google-services.json | 47 -------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 android/app/google-services.json diff --git a/android/app/google-services.json b/android/app/google-services.json deleted file mode 100644 index 29c901db..00000000 --- a/android/app/google-services.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "project_info": { - "project_number": "64647061409", - "project_id": "sharekhan-dhruv", - "storage_bucket": "sharekhan-dhruv.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:64647061409:android:a52c96120dd0ace789f5e0", - "android_client_info": { - "package_name": "com.sharekhan.app" - } - }, - "oauth_client": [ - { - "client_id": "64647061409-aoongerjfjrn2pmgtoukvknpnpl12lgo.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "com.sharekhan.app", - "certificate_hash": "198480b6d8094d0a7c9fb03e1874e270745c4c2f" - } - }, - { - "client_id": "64647061409-rtduueba7plkbr9uaqid0vuusjn3a7fk.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyCSA4qFzD3_qsxw5WnbX0rJPwmtGWmfZ0k" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "64647061409-rtduueba7plkbr9uaqid0vuusjn3a7fk.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file