commit 0fec5c03e2bfc4cec7d2ec3ce93d1e55cd21543d Author: webshunternet Date: Sat Sep 7 07:58:50 2024 +0700 first commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..93536d1 Binary files /dev/null and b/.DS_Store differ diff --git a/.flutter-plugins b/.flutter-plugins new file mode 100644 index 0000000..99c53ea --- /dev/null +++ b/.flutter-plugins @@ -0,0 +1,119 @@ +# This is a generated file; do not edit or check into version control. +audioplayers=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\audioplayers-3.0.1\\ +audioplayers_android=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\audioplayers_android-2.0.0\\ +audioplayers_darwin=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\audioplayers_darwin-3.0.1\\ +audioplayers_linux=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\audioplayers_linux-1.0.4\\ +audioplayers_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\audioplayers_web-2.2.0\\ +audioplayers_windows=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\audioplayers_windows-1.1.3\\ +awesome_notifications=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\awesome_notifications-0.7.7\\ +connectivity_plus=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\connectivity_plus-2.3.9\\ +connectivity_plus_linux=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\connectivity_plus_linux-1.3.1\\ +connectivity_plus_macos=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\connectivity_plus_macos-1.2.6\\ +connectivity_plus_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\connectivity_plus_web-1.2.5\\ +connectivity_plus_windows=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\connectivity_plus_windows-1.2.2\\ +device_info_plus=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\device_info_plus-9.1.2\\ +file_picker=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\file_picker-5.5.0\\ +file_selector_linux=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\file_selector_linux-0.9.2+1\\ +file_selector_macos=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\file_selector_macos-0.9.3+3\\ +file_selector_windows=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\file_selector_windows-0.9.3+1\\ +firebase_analytics=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\firebase_analytics-10.8.9\\ +firebase_analytics_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\firebase_analytics_web-0.5.5+21\\ +firebase_auth=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\firebase_auth-4.17.8\\ +firebase_auth_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\firebase_auth_web-5.9.8\\ +firebase_core=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\firebase_core-2.27.0\\ +firebase_core_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\firebase_core_web-2.11.5\\ +firebase_dynamic_links=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\firebase_dynamic_links-5.4.17\\ +firebase_messaging=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\firebase_messaging-14.7.19\\ +firebase_messaging_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\firebase_messaging_web-3.6.8\\ +flutter_inappwebview=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\flutter_inappwebview-5.8.0\\ +flutter_keyboard_visibility=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\flutter_keyboard_visibility-5.4.1\\ +flutter_keyboard_visibility_linux=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\flutter_keyboard_visibility_linux-1.0.0\\ +flutter_keyboard_visibility_macos=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\flutter_keyboard_visibility_macos-1.0.0\\ +flutter_keyboard_visibility_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\flutter_keyboard_visibility_web-2.0.0\\ +flutter_keyboard_visibility_windows=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\flutter_keyboard_visibility_windows-1.0.0\\ +flutter_native_image=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\flutter_native_image-0.0.6+1\\ +flutter_paystack=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\flutter_paystack-1.0.7\\ +flutter_plugin_android_lifecycle=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\flutter_plugin_android_lifecycle-2.0.17\\ +flutter_sim_country_code=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\flutter_sim_country_code-0.1.2\\ +flutter_stripe=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\flutter_stripe-9.6.0\\ +flutter_vibrate=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\flutter_vibrate-1.3.0\\ +fluttertoast=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\fluttertoast-8.2.4\\ +geocoding=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\geocoding-2.2.2\\ +geocoding_android=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\geocoding_android-3.3.0\\ +geocoding_ios=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\geocoding_ios-2.3.0\\ +google_maps_flutter=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\google_maps_flutter-2.2.6\\ +google_maps_flutter_android=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\google_maps_flutter_android-2.7.0\\ +google_maps_flutter_ios=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\google_maps_flutter_ios-2.3.6\\ +google_mobile_ads=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\google_mobile_ads-2.4.0\\ +google_sign_in=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\google_sign_in-6.2.1\\ +google_sign_in_android=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\google_sign_in_android-6.1.22\\ +google_sign_in_ios=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\google_sign_in_ios-5.7.3\\ +google_sign_in_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\google_sign_in_web-0.12.3+2\\ +image_cropper=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\image_cropper-3.0.3\\ +image_cropper_for_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\image_cropper_for_web-1.0.3\\ +image_picker=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\image_picker-0.8.9\\ +image_picker_android=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\image_picker_android-0.8.9+3\\ +image_picker_for_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\image_picker_for_web-2.2.0\\ +image_picker_ios=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\image_picker_ios-0.8.9+1\\ +image_picker_linux=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\image_picker_linux-0.2.1+1\\ +image_picker_macos=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\image_picker_macos-0.2.1+1\\ +image_picker_windows=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\image_picker_windows-0.2.1+1\\ +in_app_purchase=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\in_app_purchase-3.1.13\\ +in_app_purchase_android=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\in_app_purchase_android-0.3.2\\ +in_app_purchase_storekit=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\in_app_purchase_storekit-0.3.8+1\\ +launch_review=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\launch_review-3.0.1\\ +motion_sensors=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\git\\motion_sensors-6dafc3639b3e96460fabc639768a60b431b53610\\ +open_filex=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\open_filex-4.4.0\\ +package_info_plus=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\package_info_plus-4.2.0\\ +path_provider=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\path_provider-2.1.2\\ +path_provider_android=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\path_provider_android-2.2.2\\ +path_provider_foundation=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\path_provider_foundation-2.3.2\\ +path_provider_linux=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\path_provider_linux-2.2.1\\ +path_provider_windows=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\path_provider_windows-2.2.1\\ +permission_handler=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\permission_handler-11.3.0\\ +permission_handler_android=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\permission_handler_android-12.0.5\\ +permission_handler_apple=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\permission_handler_apple-9.4.1\\ +permission_handler_html=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\permission_handler_html-0.1.1\\ +permission_handler_windows=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\permission_handler_windows-0.2.1\\ +razorpay_flutter=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\razorpay_flutter-1.3.6\\ +record=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\record-4.4.4\\ +record_linux=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\record_linux-0.4.1\\ +record_macos=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\record_macos-0.2.2\\ +record_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\record_web-0.5.0\\ +record_windows=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\record_windows-0.7.1\\ +share_plus=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\share_plus-4.5.3\\ +share_plus_linux=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\share_plus_linux-3.0.1\\ +share_plus_macos=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\share_plus_macos-3.0.1\\ +share_plus_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\share_plus_web-3.1.0\\ +share_plus_windows=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\share_plus_windows-3.0.1\\ +shared_preferences=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\shared_preferences-2.2.2\\ +shared_preferences_android=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\shared_preferences_android-2.2.1\\ +shared_preferences_foundation=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\shared_preferences_foundation-2.3.5\\ +shared_preferences_linux=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\shared_preferences_linux-2.3.2\\ +shared_preferences_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\shared_preferences_web-2.2.2\\ +shared_preferences_windows=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\shared_preferences_windows-2.3.2\\ +sign_in_with_apple=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\sign_in_with_apple-5.0.0\\ +sign_in_with_apple_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\sign_in_with_apple_web-1.0.1\\ +sms_autofill=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\sms_autofill-2.3.1\\ +sqflite=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\sqflite-2.3.2\\ +stripe_android=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\stripe_android-9.6.0+2\\ +stripe_ios=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\stripe_ios-9.6.0\\ +uni_links=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\uni_links-0.5.1\\ +uni_links_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\uni_links_web-0.1.0\\ +url_launcher=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\url_launcher-6.2.5\\ +url_launcher_android=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\url_launcher_android-6.3.0\\ +url_launcher_ios=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\url_launcher_ios-6.2.4\\ +url_launcher_linux=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\url_launcher_linux-3.1.1\\ +url_launcher_macos=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\url_launcher_macos-3.1.0\\ +url_launcher_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\url_launcher_web-2.2.3\\ +url_launcher_windows=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\url_launcher_windows-3.1.1\\ +video_player=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\video_player-2.8.3\\ +video_player_android=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\video_player_android-2.4.12\\ +video_player_avfoundation=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\video_player_avfoundation-2.5.6\\ +video_player_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\video_player_web-2.1.3\\ +wakelock=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\wakelock-0.6.2\\ +wakelock_macos=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\wakelock_macos-0.4.0\\ +wakelock_web=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\wakelock_web-0.4.0\\ +webview_flutter=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\webview_flutter-2.8.0\\ +webview_flutter_android=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\webview_flutter_android-2.10.4\\ +webview_flutter_wkwebview=C:\\Users\\User\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\webview_flutter_wkwebview-2.9.5\\ diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies new file mode 100644 index 0000000..26f1556 --- /dev/null +++ b/.flutter-plugins-dependencies @@ -0,0 +1 @@ +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"audioplayers_darwin","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\audioplayers_darwin-3.0.1\\\\","native_build":true,"dependencies":[]},{"name":"awesome_notifications","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\awesome_notifications-0.7.7\\\\","native_build":true,"dependencies":[]},{"name":"connectivity_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\connectivity_plus-2.3.9\\\\","native_build":true,"dependencies":[]},{"name":"device_info_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\device_info_plus-9.1.2\\\\","native_build":true,"dependencies":[]},{"name":"file_picker","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_picker-5.5.0\\\\","native_build":true,"dependencies":[]},{"name":"firebase_analytics","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_analytics-10.8.9\\\\","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_auth","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_auth-4.17.8\\\\","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_core-2.27.0\\\\","native_build":true,"dependencies":[]},{"name":"firebase_dynamic_links","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_dynamic_links-5.4.17\\\\","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_messaging","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_messaging-14.7.19\\\\","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_inappwebview","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_inappwebview-5.8.0\\\\","native_build":true,"dependencies":[]},{"name":"flutter_keyboard_visibility","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_keyboard_visibility-5.4.1\\\\","native_build":true,"dependencies":[]},{"name":"flutter_native_image","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_native_image-0.0.6+1\\\\","native_build":true,"dependencies":[]},{"name":"flutter_paystack","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_paystack-1.0.7\\\\","native_build":true,"dependencies":[]},{"name":"flutter_sim_country_code","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_sim_country_code-0.1.2\\\\","native_build":true,"dependencies":[]},{"name":"flutter_vibrate","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_vibrate-1.3.0\\\\","native_build":true,"dependencies":[]},{"name":"fluttertoast","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\fluttertoast-8.2.4\\\\","native_build":true,"dependencies":[]},{"name":"geocoding_ios","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\geocoding_ios-2.3.0\\\\","native_build":true,"dependencies":[]},{"name":"google_maps_flutter_ios","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\google_maps_flutter_ios-2.3.6\\\\","native_build":true,"dependencies":[]},{"name":"google_mobile_ads","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\google_mobile_ads-2.4.0\\\\","native_build":true,"dependencies":[]},{"name":"google_sign_in_ios","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\google_sign_in_ios-5.7.3\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"image_cropper","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_cropper-3.0.3\\\\","native_build":true,"dependencies":[]},{"name":"image_picker_ios","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_ios-0.8.9+1\\\\","native_build":true,"dependencies":[]},{"name":"in_app_purchase_storekit","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\in_app_purchase_storekit-0.3.8+1\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"launch_review","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\launch_review-3.0.1\\\\","native_build":true,"dependencies":[]},{"name":"motion_sensors","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\git\\\\motion_sensors-6dafc3639b3e96460fabc639768a60b431b53610\\\\","native_build":true,"dependencies":[]},{"name":"open_filex","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\open_filex-4.4.0\\\\","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-4.2.0\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.3.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"permission_handler_apple","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\permission_handler_apple-9.4.1\\\\","native_build":true,"dependencies":[]},{"name":"razorpay_flutter","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\razorpay_flutter-1.3.6\\\\","native_build":true,"dependencies":["fluttertoast","package_info_plus"]},{"name":"record","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\record-4.4.4\\\\","native_build":true,"dependencies":[]},{"name":"share_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-4.5.3\\\\","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.3.5\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"sign_in_with_apple","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sign_in_with_apple-5.0.0\\\\","native_build":true,"dependencies":[]},{"name":"sms_autofill","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sms_autofill-2.3.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sqflite-2.3.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"stripe_ios","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\stripe_ios-9.6.0\\\\","native_build":true,"dependencies":[]},{"name":"uni_links","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\uni_links-0.5.1\\\\","native_build":true,"dependencies":[]},{"name":"url_launcher_ios","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_ios-6.2.4\\\\","native_build":true,"dependencies":[]},{"name":"video_player_avfoundation","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\video_player_avfoundation-2.5.6\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"wakelock","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock-0.6.2\\\\","native_build":true,"dependencies":[]},{"name":"webview_flutter_wkwebview","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\webview_flutter_wkwebview-2.9.5\\\\","native_build":true,"dependencies":[]}],"android":[{"name":"audioplayers_android","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\audioplayers_android-2.0.0\\\\","native_build":true,"dependencies":[]},{"name":"awesome_notifications","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\awesome_notifications-0.7.7\\\\","native_build":true,"dependencies":[]},{"name":"connectivity_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\connectivity_plus-2.3.9\\\\","native_build":true,"dependencies":[]},{"name":"device_info_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\device_info_plus-9.1.2\\\\","native_build":true,"dependencies":[]},{"name":"file_picker","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_picker-5.5.0\\\\","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"]},{"name":"firebase_analytics","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_analytics-10.8.9\\\\","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_auth","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_auth-4.17.8\\\\","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_core-2.27.0\\\\","native_build":true,"dependencies":[]},{"name":"firebase_dynamic_links","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_dynamic_links-5.4.17\\\\","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_messaging","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_messaging-14.7.19\\\\","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_inappwebview","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_inappwebview-5.8.0\\\\","native_build":true,"dependencies":[]},{"name":"flutter_keyboard_visibility","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_keyboard_visibility-5.4.1\\\\","native_build":true,"dependencies":[]},{"name":"flutter_native_image","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_native_image-0.0.6+1\\\\","native_build":true,"dependencies":[]},{"name":"flutter_paystack","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_paystack-1.0.7\\\\","native_build":true,"dependencies":[]},{"name":"flutter_plugin_android_lifecycle","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_plugin_android_lifecycle-2.0.17\\\\","native_build":true,"dependencies":[]},{"name":"flutter_sim_country_code","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_sim_country_code-0.1.2\\\\","native_build":true,"dependencies":[]},{"name":"flutter_vibrate","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_vibrate-1.3.0\\\\","native_build":true,"dependencies":[]},{"name":"fluttertoast","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\fluttertoast-8.2.4\\\\","native_build":true,"dependencies":[]},{"name":"geocoding_android","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\geocoding_android-3.3.0\\\\","native_build":true,"dependencies":[]},{"name":"google_maps_flutter_android","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\google_maps_flutter_android-2.7.0\\\\","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"]},{"name":"google_mobile_ads","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\google_mobile_ads-2.4.0\\\\","native_build":true,"dependencies":[]},{"name":"google_sign_in_android","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\google_sign_in_android-6.1.22\\\\","native_build":true,"dependencies":[]},{"name":"image_cropper","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_cropper-3.0.3\\\\","native_build":true,"dependencies":[]},{"name":"image_picker_android","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_android-0.8.9+3\\\\","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"]},{"name":"in_app_purchase_android","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\in_app_purchase_android-0.3.2\\\\","native_build":true,"dependencies":[]},{"name":"launch_review","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\launch_review-3.0.1\\\\","native_build":true,"dependencies":[]},{"name":"motion_sensors","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\git\\\\motion_sensors-6dafc3639b3e96460fabc639768a60b431b53610\\\\","native_build":true,"dependencies":[]},{"name":"open_filex","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\open_filex-4.4.0\\\\","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-4.2.0\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.2.2\\\\","native_build":true,"dependencies":[]},{"name":"permission_handler_android","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\permission_handler_android-12.0.5\\\\","native_build":true,"dependencies":[]},{"name":"razorpay_flutter","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\razorpay_flutter-1.3.6\\\\","native_build":true,"dependencies":["fluttertoast","package_info_plus"]},{"name":"record","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\record-4.4.4\\\\","native_build":true,"dependencies":[]},{"name":"share_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-4.5.3\\\\","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_android-2.2.1\\\\","native_build":true,"dependencies":[]},{"name":"sign_in_with_apple","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sign_in_with_apple-5.0.0\\\\","native_build":true,"dependencies":[]},{"name":"sms_autofill","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sms_autofill-2.3.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sqflite-2.3.2\\\\","native_build":true,"dependencies":[]},{"name":"stripe_android","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\stripe_android-9.6.0+2\\\\","native_build":true,"dependencies":[]},{"name":"uni_links","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\uni_links-0.5.1\\\\","native_build":true,"dependencies":[]},{"name":"url_launcher_android","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_android-6.3.0\\\\","native_build":true,"dependencies":[]},{"name":"video_player_android","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\video_player_android-2.4.12\\\\","native_build":true,"dependencies":[]},{"name":"wakelock","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock-0.6.2\\\\","native_build":true,"dependencies":[]},{"name":"webview_flutter_android","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\webview_flutter_android-2.10.4\\\\","native_build":true,"dependencies":[]}],"macos":[{"name":"audioplayers_darwin","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\audioplayers_darwin-3.0.1\\\\","native_build":true,"dependencies":[]},{"name":"awesome_notifications","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\awesome_notifications-0.7.7\\\\","native_build":true,"dependencies":[]},{"name":"connectivity_plus_macos","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\connectivity_plus_macos-1.2.6\\\\","native_build":true,"dependencies":[]},{"name":"device_info_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\device_info_plus-9.1.2\\\\","native_build":true,"dependencies":[]},{"name":"file_selector_macos","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_macos-0.9.3+3\\\\","native_build":true,"dependencies":[]},{"name":"firebase_analytics","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_analytics-10.8.9\\\\","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_auth","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_auth-4.17.8\\\\","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_core-2.27.0\\\\","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_messaging-14.7.19\\\\","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_keyboard_visibility_macos","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_keyboard_visibility_macos-1.0.0\\\\","native_build":false,"dependencies":[]},{"name":"google_sign_in_ios","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\google_sign_in_ios-5.7.3\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"image_picker_macos","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_macos-0.2.1+1\\\\","native_build":false,"dependencies":["file_selector_macos"]},{"name":"in_app_purchase_storekit","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\in_app_purchase_storekit-0.3.8+1\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-4.2.0\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.3.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"record_macos","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\record_macos-0.2.2\\\\","native_build":true,"dependencies":[]},{"name":"share_plus_macos","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus_macos-3.0.1\\\\","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.3.5\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"sign_in_with_apple","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sign_in_with_apple-5.0.0\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sqflite-2.3.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"url_launcher_macos","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_macos-3.1.0\\\\","native_build":true,"dependencies":[]},{"name":"video_player_avfoundation","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\video_player_avfoundation-2.5.6\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"wakelock_macos","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_macos-0.4.0\\\\","native_build":true,"dependencies":[]}],"linux":[{"name":"audioplayers_linux","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\audioplayers_linux-1.0.4\\\\","native_build":true,"dependencies":[]},{"name":"awesome_notifications","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\awesome_notifications-0.7.7\\\\","native_build":true,"dependencies":[]},{"name":"connectivity_plus_linux","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\connectivity_plus_linux-1.3.1\\\\","native_build":false,"dependencies":[]},{"name":"device_info_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\device_info_plus-9.1.2\\\\","native_build":false,"dependencies":[]},{"name":"file_selector_linux","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_linux-0.9.2+1\\\\","native_build":true,"dependencies":[]},{"name":"flutter_keyboard_visibility_linux","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_keyboard_visibility_linux-1.0.0\\\\","native_build":false,"dependencies":[]},{"name":"image_picker_linux","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_linux-0.2.1+1\\\\","native_build":false,"dependencies":["file_selector_linux"]},{"name":"package_info_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-4.2.0\\\\","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.2.1\\\\","native_build":false,"dependencies":[]},{"name":"record_linux","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\record_linux-0.4.1\\\\","native_build":true,"dependencies":[]},{"name":"share_plus_linux","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus_linux-3.0.1\\\\","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_linux-2.3.2\\\\","native_build":false,"dependencies":["path_provider_linux"]},{"name":"url_launcher_linux","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_linux-3.1.1\\\\","native_build":true,"dependencies":[]}],"windows":[{"name":"audioplayers_windows","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\audioplayers_windows-1.1.3\\\\","native_build":true,"dependencies":[]},{"name":"awesome_notifications","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\awesome_notifications-0.7.7\\\\","native_build":true,"dependencies":[]},{"name":"connectivity_plus_windows","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\connectivity_plus_windows-1.2.2\\\\","native_build":true,"dependencies":[]},{"name":"device_info_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\device_info_plus-9.1.2\\\\","native_build":false,"dependencies":[]},{"name":"file_selector_windows","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_windows-0.9.3+1\\\\","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_auth-4.17.8\\\\","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_core-2.27.0\\\\","native_build":true,"dependencies":[]},{"name":"flutter_keyboard_visibility_windows","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_keyboard_visibility_windows-1.0.0\\\\","native_build":false,"dependencies":[]},{"name":"image_picker_windows","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_windows-0.2.1+1\\\\","native_build":false,"dependencies":["file_selector_windows"]},{"name":"package_info_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-4.2.0\\\\","native_build":false,"dependencies":[]},{"name":"path_provider_windows","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.2.1\\\\","native_build":false,"dependencies":[]},{"name":"permission_handler_windows","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\permission_handler_windows-0.2.1\\\\","native_build":true,"dependencies":[]},{"name":"record_windows","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\record_windows-0.7.1\\\\","native_build":true,"dependencies":[]},{"name":"share_plus_windows","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus_windows-3.0.1\\\\","native_build":false,"dependencies":[]},{"name":"shared_preferences_windows","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_windows-2.3.2\\\\","native_build":false,"dependencies":["path_provider_windows"]},{"name":"url_launcher_windows","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_windows-3.1.1\\\\","native_build":true,"dependencies":[]}],"web":[{"name":"audioplayers_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\audioplayers_web-2.2.0\\\\","dependencies":[]},{"name":"awesome_notifications","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\awesome_notifications-0.7.7\\\\","dependencies":[]},{"name":"connectivity_plus_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\connectivity_plus_web-1.2.5\\\\","dependencies":[]},{"name":"device_info_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\device_info_plus-9.1.2\\\\","dependencies":[]},{"name":"file_picker","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_picker-5.5.0\\\\","dependencies":[]},{"name":"firebase_analytics_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_analytics_web-0.5.5+21\\\\","dependencies":["firebase_core_web"]},{"name":"firebase_auth_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_auth_web-5.9.8\\\\","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_core_web-2.11.5\\\\","dependencies":[]},{"name":"firebase_messaging_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\firebase_messaging_web-3.6.8\\\\","dependencies":["firebase_core_web"]},{"name":"flutter_keyboard_visibility_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_keyboard_visibility_web-2.0.0\\\\","dependencies":[]},{"name":"fluttertoast","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\fluttertoast-8.2.4\\\\","dependencies":[]},{"name":"google_sign_in_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\google_sign_in_web-0.12.3+2\\\\","dependencies":[]},{"name":"image_cropper_for_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_cropper_for_web-1.0.3\\\\","dependencies":[]},{"name":"image_picker_for_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_for_web-2.2.0\\\\","dependencies":[]},{"name":"package_info_plus","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-4.2.0\\\\","dependencies":[]},{"name":"permission_handler_html","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\permission_handler_html-0.1.1\\\\","dependencies":[]},{"name":"record_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\record_web-0.5.0\\\\","dependencies":[]},{"name":"share_plus_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus_web-3.1.0\\\\","dependencies":[]},{"name":"shared_preferences_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_web-2.2.2\\\\","dependencies":[]},{"name":"sign_in_with_apple_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\sign_in_with_apple_web-1.0.1\\\\","dependencies":[]},{"name":"uni_links_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\uni_links_web-0.1.0\\\\","dependencies":[]},{"name":"url_launcher_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_web-2.2.3\\\\","dependencies":[]},{"name":"video_player_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\video_player_web-2.1.3\\\\","dependencies":[]},{"name":"wakelock_web","path":"C:\\\\Users\\\\User\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_web-0.4.0\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"audioplayers","dependencies":["audioplayers_android","audioplayers_darwin","audioplayers_linux","audioplayers_web","audioplayers_windows","path_provider"]},{"name":"audioplayers_android","dependencies":[]},{"name":"audioplayers_darwin","dependencies":[]},{"name":"audioplayers_linux","dependencies":[]},{"name":"audioplayers_web","dependencies":[]},{"name":"audioplayers_windows","dependencies":[]},{"name":"awesome_notifications","dependencies":[]},{"name":"connectivity_plus","dependencies":["connectivity_plus_linux","connectivity_plus_macos","connectivity_plus_web","connectivity_plus_windows"]},{"name":"connectivity_plus_linux","dependencies":[]},{"name":"connectivity_plus_macos","dependencies":[]},{"name":"connectivity_plus_web","dependencies":[]},{"name":"connectivity_plus_windows","dependencies":[]},{"name":"device_info_plus","dependencies":[]},{"name":"file_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"file_selector_linux","dependencies":[]},{"name":"file_selector_macos","dependencies":[]},{"name":"file_selector_windows","dependencies":[]},{"name":"firebase_analytics","dependencies":["firebase_analytics_web","firebase_core"]},{"name":"firebase_analytics_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_dynamic_links","dependencies":["firebase_core"]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"flutter_keyboard_visibility","dependencies":["flutter_keyboard_visibility_linux","flutter_keyboard_visibility_macos","flutter_keyboard_visibility_web","flutter_keyboard_visibility_windows"]},{"name":"flutter_keyboard_visibility_linux","dependencies":[]},{"name":"flutter_keyboard_visibility_macos","dependencies":[]},{"name":"flutter_keyboard_visibility_web","dependencies":[]},{"name":"flutter_keyboard_visibility_windows","dependencies":[]},{"name":"flutter_native_image","dependencies":[]},{"name":"flutter_paystack","dependencies":[]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"flutter_sim_country_code","dependencies":[]},{"name":"flutter_stripe","dependencies":["stripe_android","stripe_ios"]},{"name":"flutter_vibrate","dependencies":[]},{"name":"fluttertoast","dependencies":[]},{"name":"geocoding","dependencies":["geocoding_android","geocoding_ios"]},{"name":"geocoding_android","dependencies":[]},{"name":"geocoding_ios","dependencies":[]},{"name":"google_maps_flutter","dependencies":["google_maps_flutter_android","google_maps_flutter_ios"]},{"name":"google_maps_flutter_android","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"google_maps_flutter_ios","dependencies":[]},{"name":"google_mobile_ads","dependencies":[]},{"name":"google_sign_in","dependencies":["google_sign_in_android","google_sign_in_ios","google_sign_in_web"]},{"name":"google_sign_in_android","dependencies":[]},{"name":"google_sign_in_ios","dependencies":[]},{"name":"google_sign_in_web","dependencies":[]},{"name":"image_cropper","dependencies":["image_cropper_for_web"]},{"name":"image_cropper_for_web","dependencies":[]},{"name":"image_picker","dependencies":["image_picker_android","image_picker_for_web","image_picker_ios","image_picker_linux","image_picker_macos","image_picker_windows"]},{"name":"image_picker_android","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"image_picker_for_web","dependencies":[]},{"name":"image_picker_ios","dependencies":[]},{"name":"image_picker_linux","dependencies":["file_selector_linux"]},{"name":"image_picker_macos","dependencies":["file_selector_macos"]},{"name":"image_picker_windows","dependencies":["file_selector_windows"]},{"name":"in_app_purchase","dependencies":["in_app_purchase_android","in_app_purchase_storekit"]},{"name":"in_app_purchase_android","dependencies":[]},{"name":"in_app_purchase_storekit","dependencies":[]},{"name":"launch_review","dependencies":[]},{"name":"motion_sensors","dependencies":[]},{"name":"open_filex","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_html","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_html","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]},{"name":"razorpay_flutter","dependencies":["fluttertoast","package_info_plus"]},{"name":"record","dependencies":["record_web","record_windows","record_macos","record_linux"]},{"name":"record_linux","dependencies":[]},{"name":"record_macos","dependencies":[]},{"name":"record_web","dependencies":[]},{"name":"record_windows","dependencies":[]},{"name":"share_plus","dependencies":["share_plus_linux","share_plus_macos","share_plus_windows","share_plus_web"]},{"name":"share_plus_linux","dependencies":["url_launcher"]},{"name":"share_plus_macos","dependencies":[]},{"name":"share_plus_web","dependencies":["url_launcher"]},{"name":"share_plus_windows","dependencies":["url_launcher"]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"sign_in_with_apple","dependencies":["sign_in_with_apple_web"]},{"name":"sign_in_with_apple_web","dependencies":[]},{"name":"sms_autofill","dependencies":[]},{"name":"sqflite","dependencies":[]},{"name":"stripe_android","dependencies":[]},{"name":"stripe_ios","dependencies":[]},{"name":"uni_links","dependencies":["uni_links_web"]},{"name":"uni_links_web","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]},{"name":"video_player","dependencies":["video_player_android","video_player_avfoundation","video_player_web"]},{"name":"video_player_android","dependencies":[]},{"name":"video_player_avfoundation","dependencies":[]},{"name":"video_player_web","dependencies":[]},{"name":"wakelock","dependencies":["wakelock_macos","wakelock_web"]},{"name":"wakelock_macos","dependencies":[]},{"name":"wakelock_web","dependencies":[]},{"name":"webview_flutter","dependencies":["webview_flutter_android","webview_flutter_wkwebview"]},{"name":"webview_flutter_android","dependencies":[]},{"name":"webview_flutter_wkwebview","dependencies":[]}],"date_created":"2024-06-06 13:14:31.041464","version":"3.16.0"} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..436b31e --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +android/ +.dart_tool/ +.vscode/ +build/ +deeplinkfiles/ +firebase.json \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..d8feb32 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,219 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + - always_declare_return_types + # - always_put_control_body_on_new_line + # - always_put_required_named_parameters_first + # - always_require_non_null_named_parameters + # - always_specify_types + # # - always_use_package_imports + # - annotate_overrides + # #- avoid_annotating_with_dynamic + # - avoid_bool_literals_in_conditional_expressions + # - avoid_catches_without_on_clauses + # - avoid_catching_errors + # - avoid_classes_with_only_static_members + # - avoid_double_and_int_checks + # - avoid_dynamic_calls + # - avoid_empty_else + # - avoid_equals_and_hash_code_on_mutable_classes + # - avoid_escaping_inner_quotes + # - avoid_field_initializers_in_const_classes + # - avoid_function_literals_in_foreach_calls + # - avoid_implementing_value_types + # - avoid_init_to_null + # - avoid_js_rounded_ints + # - avoid_multiple_declarations_per_line + # - avoid_null_checks_in_equality_operators + # - avoid_positional_boolean_parameters + # - avoid_print + # - avoid_private_typedef_functions + # - avoid_redundant_argument_values + # - avoid_relative_lib_imports + # - avoid_renaming_method_parameters + # - avoid_return_types_on_setters + # - avoid_returning_null + # - avoid_returning_null_for_future + # - avoid_returning_null_for_void + # - avoid_returning_this + # - avoid_setters_without_getters + # - avoid_shadowing_type_parameters + # - avoid_single_cascade_in_expression_statements + # - avoid_slow_async_io + # - avoid_type_to_string + # - avoid_types_as_parameter_names + # #- avoid_types_on_closure_parameters + # - avoid_unnecessary_containers + # - avoid_unused_constructor_parameters + # - avoid_void_async + # - avoid_web_libraries_in_flutter + # - await_only_futures + # - camel_case_extensions + # - camel_case_types + # - cancel_subscriptions + # - cascade_invocations + # - cast_nullable_to_non_nullable + # - close_sinks + # - comment_references + # - constant_identifier_names + # - control_flow_in_finally + # - curly_braces_in_flow_control_structures + # - depend_on_referenced_packages + # - deprecated_consistency + # - diagnostic_describe_all_properties + # - directives_ordering + # - do_not_use_environment + # - empty_catches + # - empty_constructor_bodies + # - empty_statements + # - exhaustive_cases + # - file_names + # - flutter_style_todos + # - hash_and_equals + # - implementation_imports + # - invariant_booleans + # - iterable_contains_unrelated_type + # - join_return_with_assignment + # - leading_newlines_in_multiline_strings + # - library_names + # - library_prefixes + # - library_private_types_in_public_api + # - lines_longer_than_80_chars + # - list_remove_unrelated_type + # - literal_only_boolean_expressions + # - missing_whitespace_between_adjacent_strings + # - no_adjacent_strings_in_list + # - no_default_cases + # - no_duplicate_case_values + # - no_logic_in_create_state + # - no_runtimeType_toString + # - non_constant_identifier_names + # - noop_primitive_operations + # - null_check_on_nullable_type_parameter + # - null_closures + # - omit_local_variable_types + # - one_member_abstracts + # - only_throw_errors + # - overridden_fields + # - package_api_docs + # - package_names + # - package_prefixed_library_names + # - parameter_assignments + # - prefer_adjacent_string_concatenation + # - prefer_asserts_in_initializer_lists + # - prefer_asserts_with_message + # - prefer_collection_literals + # - prefer_conditional_assignment + # - prefer_const_constructors + # - prefer_const_constructors_in_immutables + # - prefer_const_declarations + # - prefer_const_literals_to_create_immutables + # - prefer_constructors_over_static_methods + # - prefer_contains + # - prefer_double_quotes + # - prefer_equal_for_default_values + # - prefer_expression_function_bodies + # - prefer_final_fields + # - prefer_final_in_for_each + # - prefer_final_locals + # - prefer_final_parameters + # - prefer_for_elements_to_map_fromIterable + # - prefer_foreach + # - prefer_function_declarations_over_variables + # - prefer_generic_function_type_aliases + # - prefer_if_elements_to_conditional_expressions + # - prefer_if_null_operators + # - prefer_initializing_formals + # - prefer_inlined_adds + # - prefer_int_literals + # - prefer_interpolation_to_compose_strings + # - prefer_is_empty + # - prefer_is_not_empty + # - prefer_is_not_operator + # - prefer_iterable_whereType + # - prefer_mixin + # - prefer_null_aware_method_calls + # - prefer_null_aware_operators + # - prefer_relative_imports + # - prefer_single_quotes + # - prefer_spread_collections + # - prefer_typing_uninitialized_variables + # - prefer_void_to_null + # - provide_deprecation_message + # - public_member_api_docs + # - recursive_getters + # - require_trailing_commas + # - sized_box_for_whitespace + # - slash_for_doc_comments + # - sort_child_properties_last + # - sort_constructors_first + # - sort_pub_dependencies + # - sort_unnamed_constructors_first + # - test_types_in_equals + # - throw_in_finally + # - tighten_type_of_initializing_formals + # - type_annotate_public_apis + # - type_init_formals + # - unawaited_futures + # - unnecessary_await_in_return + # - unnecessary_brace_in_string_interps + # - unnecessary_const + # #- unnecessary_final + # - unnecessary_getters_setters + # - unnecessary_lambdas + # - unnecessary_new + # - unnecessary_null_aware_assignments + # - unnecessary_null_checks + # - unnecessary_null_in_if_null_operators + # - unnecessary_nullable_for_final_variable_declarations + # - unnecessary_overrides + # - unnecessary_parenthesis + # - unnecessary_raw_strings + # - unnecessary_statements + # - unnecessary_string_escapes + # - unnecessary_string_interpolations + # - unnecessary_this + # - unrelated_type_equality_checks + # - unsafe_html + # - use_build_context_synchronously + # - use_full_hex_values_for_flutter_colors + # - use_function_type_syntax_for_parameters + # - use_if_null_to_convert_nulls_to_bools + # - use_is_even_rather_than_modulo + # - use_key_in_widget_constructors + # - use_late_for_private_fields_and_variables + # - use_named_constants + # - use_raw_strings + # - use_rethrow_when_possible + # - use_setters_to_change_properties + # - use_string_buffers + # - use_test_throws_matchers + # - use_to_and_as_if_applicable + # - valid_regexps + # - void_checks + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/assets/.DS_Store b/assets/.DS_Store new file mode 100644 index 0000000..c00a1de Binary files /dev/null and b/assets/.DS_Store differ diff --git a/assets/AppIcon/.DS_Store b/assets/AppIcon/.DS_Store new file mode 100644 index 0000000..dddea90 Binary files /dev/null and b/assets/AppIcon/.DS_Store differ diff --git a/assets/AppIcon/icon.png b/assets/AppIcon/icon.png new file mode 100644 index 0000000..10af91a Binary files /dev/null and b/assets/AppIcon/icon.png differ diff --git a/assets/chat_background/.DS_Store b/assets/chat_background/.DS_Store new file mode 100644 index 0000000..b4aa8a5 Binary files /dev/null and b/assets/chat_background/.DS_Store differ diff --git a/assets/chat_background/__light.jpg b/assets/chat_background/__light.jpg new file mode 100644 index 0000000..c1ea8e8 Binary files /dev/null and b/assets/chat_background/__light.jpg differ diff --git a/assets/chat_background/dark.jpg b/assets/chat_background/dark.jpg new file mode 100644 index 0000000..7af30b2 Binary files /dev/null and b/assets/chat_background/dark.jpg differ diff --git a/assets/chat_background/light.svg b/assets/chat_background/light.svg new file mode 100644 index 0000000..d4dc576 --- /dev/null +++ b/assets/chat_background/light.svg @@ -0,0 +1,1413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/city.jpg b/assets/city.jpg new file mode 100644 index 0000000..2ac78fd Binary files /dev/null and b/assets/city.jpg differ diff --git a/assets/fonts/Manrope-Bold.ttf b/assets/fonts/Manrope-Bold.ttf new file mode 100644 index 0000000..f875b87 Binary files /dev/null and b/assets/fonts/Manrope-Bold.ttf differ diff --git a/assets/fonts/Manrope-ExtraBold.ttf b/assets/fonts/Manrope-ExtraBold.ttf new file mode 100644 index 0000000..be66e26 Binary files /dev/null and b/assets/fonts/Manrope-ExtraBold.ttf differ diff --git a/assets/fonts/Manrope-ExtraLight.ttf b/assets/fonts/Manrope-ExtraLight.ttf new file mode 100644 index 0000000..50fc233 Binary files /dev/null and b/assets/fonts/Manrope-ExtraLight.ttf differ diff --git a/assets/fonts/Manrope-Light.ttf b/assets/fonts/Manrope-Light.ttf new file mode 100644 index 0000000..1710980 Binary files /dev/null and b/assets/fonts/Manrope-Light.ttf differ diff --git a/assets/fonts/Manrope-Medium.ttf b/assets/fonts/Manrope-Medium.ttf new file mode 100644 index 0000000..c36a054 Binary files /dev/null and b/assets/fonts/Manrope-Medium.ttf differ diff --git a/assets/fonts/Manrope-Regular.ttf b/assets/fonts/Manrope-Regular.ttf new file mode 100644 index 0000000..7d21e1b Binary files /dev/null and b/assets/fonts/Manrope-Regular.ttf differ diff --git a/assets/fonts/Manrope-SemiBold.ttf b/assets/fonts/Manrope-SemiBold.ttf new file mode 100644 index 0000000..0c888b9 Binary files /dev/null and b/assets/fonts/Manrope-SemiBold.ttf differ diff --git a/assets/fonts/Manrope.ttf b/assets/fonts/Manrope.ttf new file mode 100644 index 0000000..8c4a952 Binary files /dev/null and b/assets/fonts/Manrope.ttf differ diff --git a/assets/languages/.DS_Store b/assets/languages/.DS_Store new file mode 100644 index 0000000..f73c793 Binary files /dev/null and b/assets/languages/.DS_Store differ diff --git a/assets/languages/en.json b/assets/languages/en.json new file mode 100644 index 0000000..e69de29 diff --git a/assets/languages/template.json b/assets/languages/template.json new file mode 100644 index 0000000..b6e5812 --- /dev/null +++ b/assets/languages/template.json @@ -0,0 +1,374 @@ +{ + "_platform_": "APP", + "aboutThisPropLbl": "Tentang properti ini", + "Meta Image": "Gambar Meta", + "Meta Details": "Detail Meta", + "metaDescriptionLength": "Panjang Deskripsi Meta harus antara 50 hingga 160 karakter.", + "Title": "Judul", + "Keywords": "Kata Kunci", + "propertyType": "Tipe Properti", + "metaKeywordsLength": "Kata Kunci Meta tidak lebih dari 10 frasa kata kunci & harus dipisahkan dengan koma.", + "metaTitleLength": "Panjang Judul Meta tidak boleh melebihi 60 karakter.", + "Description": "Deskripsi", + "uploadMetaTitleImage": "Unggah gambar judul meta", + "aboutUs": "Tentang kami", + "Unlocked Private Properties": "Properti Pribadi yang Terbuka", + "addNumerical": "Tambahkan nilai numerik", + "acceptLbl": "Terima", + "acceptPolicy": "Harap terima Syarat & Ketentuan serta Kebijakan Privasi terlebih dahulu", + "active": "Aktif", + "addressError": "Harap pilih alamat lagi", + "addressLbl": "Alamat", + "addvalues": "Tambahkan nilai", + "adrsNotFoundErrorMsg": "Alamat yang Ditentukan Tidak Ditemukan", + "and": "&", + "chooseYourInterest": "Pilih minat Anda\n", + "skip": "Lewati", + "selectedLocation": "Lokasi yang Dipilih: ", + "choosePropertyType":"Pilih tipe properti yang Anda minati", + "chooseNearbyPlaces": "Pilih tempat\n di sekitar", + "chooseTheBudeget": "Pilih anggaran untuk\n properti yang Anda minati.", + "getRecommandation": "Dapatkan rekomendasi untuk properti yang dekat dengan lokasi ini.", + "selectCityYouWantToSee":"Pilih kota\n yang ingin Anda lihat\n propertinya", + "selectPlaces": "Pilih Tempat", + "selectedItems": "Item yang Dipilih", + "outdoorFacilities": "Fasilitas Outdoor", + "applyFilter":"Terapkan Filter", + "anytimeLbl": "Kapan saja", + "areaConvertor": "Konverter Area", + "NearbyProperties":"Properti di Sekitar", + "changeStatus":"Ubah Status", + "from":"Dari", + "useYourLocation":"Pilih alamat Anda", + "mostLikedProperties":"Properti yang Paling Disukai", + "mostLiked":"Paling Disukai", + "nearByProperties":"Properti di Sekitar", + "msgWillNotRecover":"Pesan ini tidak akan bisa dipulihkan", + "projectWillNotRecover": "Proyek yang dihapus tidak akan bisa dipulihkan", + "areYouSure":"Apakah Anda yakin?", + "today":"Hari Ini", + "toArrow":"-KE->", + "chooseNumber":"Pilih Nomor", + "viewMap":"Lihat di peta", + "yesterday":"Kemarin", + "thisActionNotValidDemo":"Tindakan ini tidak valid dalam mode demo", + "sms":" SMS", + "call":" Panggilan", + "viewProperty":"Lihat Properti", + "articles": "Artikel", + "budgetLbl": "Anggaran(Harga)", + "callBtnLbl": "Panggil", + "camera": "Kamera", + "cancelBtnLbl": "Batal", + "cancelLbl": "Batal", + "report": "Laporan", + "categories": "Kategori", + "categoriesLbl": "Kategori", + "category": "Kategori", + "chooseAdrsLbl": "Pilih Alamat", + "chooseLocation": "Pilih Lokasi", + "city": "Kota", + "clearfilter": "Bersihkan Filter", + "myAds": "Iklan Saya", "transactionHistory": "Riwayat Transaksi", + "clientaddressLbl": "Alamat Klien", + "comfirmAdrsBtnLbl": "Konfirmasi Alamat", + "comfirmBtnLbl": "Konfirmasi", + "companyAdrsLbl": "Alamat", + "companyEmailLbl": "Email", + "sendEmail": "Kirim Email", + "subject":"Subjek", + "completeLbl": "Selesai", + "convert":"Konversi", + "maintenanceModeMessage":"Maaf, kami sedang dalam masa pemeliharaan. Silakan coba lagi nanti.", + "addMainPicture":"Tambahkan Gambar Utama", + "add360degPicture":"Tambahkan Gambar 360 Derajat", + "howCanWeHelp":"Bagaimana kami bisa membantu Anda?", + "itLooksLikeYouHasError":"Sepertinya Anda mengalami masalah dengan sistem kami. Kami di sini untuk membantu Anda, jadi, silakan hubungi kami.", + "uploadPictures":"Unggah Gambar", + "addOtherPicture":"Tambahkan Gambar Lainnya", + "price":"Harga", + "paypal":"Paypal", + "purchaseFailed":"Pembelian paket gagal", + "packageNotValid":"Upgrade untuk Fitur Premium!", + "additionals":"Tambahan", + "packageNotForProperty": "Berlangganan sekarang untuk membuka fitur premium dan tingkatkan pengalaman Anda.", + "confirmDeleteAdvert":"Apakah Anda benar-benar ingin Menghapus Iklan ini?", + "confirmLogOutMsg": "Apakah Anda yakin ingin keluar?", + "confirmLogoutTitle": "Konfirmasi Keluar", + "pleaseChangeNetwork":"Silakan ubah jaringan atau coba mengubah DNS", + "contactUs": "Hubungi kami", + "continue": "Lanjutkan", + "copylink":"Salin tautan", + "country": "Negara", + "uptoDate":"Versi terbaru", + "pressAgainToExit":"Tekan lagi untuk keluar", + "newVersionAvailableForce":"Harap perbarui ke versi terbaru aplikasi kami untuk terus menggunakannya.", + "newVersionAvailable":"Kami menulis untuk memberitahu Anda bahwa versi terbaru aplikasi kami sekarang tersedia. Versi terbaru mencakup beberapa fitur baru dan peningkatan kinerja, serta perbaikan bug untuk membuat aplikasi berjalan lebih lancar.", + "currentPackage": "Paket saat ini", + "darkTheme": "Tema Gelap", + "updateAvailable":"Pembaruan tersedia", + "deactive": "Nonaktifkan", + "ddPropertyLbl": "Tambahkan Properti", + "disabled":"Nonaktif", + "pickSliderImage":"Pilih Gambar Slider", + "featured":"Unggulan", + "somethingWentWrng":"Ada yang salah", + "authExpired":"Otentikasi Kadaluarsa", + "defaultErrorMsg": "Ada yang salah. Silakan coba lagi", + "deleteAccount": "Hapus Akun", + "message":"Obrolan", + "deleteBtnLbl": "Hapus", + "deleteProfileMessageContent": "Anda tidak akan bisa mengembalikannya. Anda akan keluar dan semua sesi aktif akan diakhiri", + "deleteProfileMessageTitle": "Hapus Akun?", + "deletepropertywarning": "Apakah Anda yakin ingin menghapus properti ini?", + "descriptionLbl": "Deskripsi", + "adLimitIs":"Batas iklan adalah", + "propertyLimit":"Batas properti adalah", + "lifetime":"Seumur hidup", + "interestedUserCount":"Jumlah Pengguna yang Tertarik:", + "interested":"Tertarik", + "edit":"Edit", + "feature":"Fitur", + "interest":"Minat", + "editBtnLbl": "Edit", + "editprofile": "Edit Profil", + "emptyValueMessage": "Silakan masukkan teks", + "enterCodeSend": "Verifikasi OTP\nLayar", + "enterLocation": "Masukkan lokasi, area kota, dll", + "enterYourNumber": "Masukkan\nNomor Ponsel Anda", + "enteraddress": "Masukkan Alamat", + "enterdescriptionLbl": "Masukkan Deskripsi", + "entername": "Masukkan Nama", + "enabled":"Diaktifkan", + "favorites": "Favorit", + "filterTitle": "Filter", + "forRentLbl": "Disewakan", + "forSaleLbl": "Dijual", + "fullName":"Nama Lengkap", + "noPackage":"Tidak Ada Paket Tersedia", + "plsSubscribe":"Silakan Berlangganan ke paket apa pun untuk menggunakan fungsionalitas ini", + "gallery": "Galeri Foto", + "subsctiptionPlane":"Rencana Langganan", + "dontshowagain":"Jangan tampilkan lagi", + "fileSavedIn":"Berkas Tersimpan di folder unduhan", + "fileNotSaved":"Tidak dapat menemukan folder unduhan", + "errorFileSave":"Kesalahan saat mengunduh berkas", + "to":"KE", + "packageStartedOn":"Dimulai pada", + "selectPaymentMethod":"Pilih Metode Pembayaran", + "deleteEnquiryMessage":"Apakah Anda yakin ingin menghapus pertanyaan ini?", + "andPackageWillEndOn":"dan akan berakhir pada", + "getCodeBtnLbl": "Dapatkan Kode", + "homeTab": "Beranda", + "incomplete": "Belum lengkap", + "language": "Bahasa", + "lastWeekLbl": "Minggu Lalu", + "lattitude": "Lintang", + "turnOnNotification":"Harap aktifkan izin Notifikasi", + "lblEnterOtp": "Masukkan OTP", + "searhCity":"Cari lokasi", + "lblall": "Semua", + "ok":"Oke", + "lblchecknetwork": "Periksa Koneksi Internet", + "lblremove": "Hapus", + "copied": "Disalin!!", + "locationLbl": "Lokasi", + "enablesNewSection":"Lokasi ini akan mengaktifkan bagian baru di layar beranda untuk properti di sekitarnya", + "logout": "Keluar", + "longitude": "Garis Bujur", + "mailMsgLbl": "Hai, saya ingin tahu lebih banyak tentang RumahJo.", + "maxLbl": "Maks", + "minLbl": "Min", + "forSell":"Untuk Dijual", + "searchCity": "Cari kota", + "forRent":"Disewakan", + "sell":"Jual", + "Sell":"Jual", + "cityProj": "Kota: ", + "stateProj": "Negara Bagian: ", + "countryProj": "Negara: ", + "contactUS": "Hubungi Kami!", + "Rent":"Sewa", + "Sold":"Terjual", + "Rented":"Disewakan", + "rent":"Sewa", + "mostViewed": "Paling dilihat", + "view":"Lihat", + "chooseLanguage":"Pilih Bahasa", + "myAdvertisment": "Iklan Saya", + "myEnquiry": "Pertanyaan Saya", + "myProfile": "Profil Saya", + "myProperty": "Properti Saya", + "personalizedFeed": "Feed Personal", + "next": "Selanjutnya", + "retry": "Coba Lagi", + "personalized": "Personalisasi feed", + "startConversation": "Mulai Berbicara untuk melihat pesan Anda di sini...", + "sorryLookingFor": "Maaf, kami tidak menemukan yang Anda cari. Silakan coba cara lain", + "uploadPhoto": "Unggah \n Foto", + "otherPictures": "Foto lain", + "noInternetErrorMsg": "Ada yang salah dengan koneksi Anda, harap periksa dan coba lagi", + "noInternet": "Tidak Ada Internet", + "nodatafound": "Tidak Ada Data Ditemukan", + "notifications": "Notifikasi", + "writeHere": "Tulis di sini", + "notification":"Notifikasi", + "onboarding_1_description": "Temukan tempat ideal sesuai dengan\nkebutuhan dan harapan Anda.", + "onboarding_1_title": "Selamat Datang di RumahJo", + "onboarding_2_description": "Anda dapat mencari ribuan properti, untuk hidup Anda di platform ini dengan mudah dan cepat", + "onboarding_2_title": "Temukan Properti Terbaik Anda", + "onboarding_3_description": "Beli & jual rumah yang Anda harapkan\ndari ponsel dengan RumahJo", + "onboarding_3_title": "Beli, Jual & Sewa Properti", + "optsentsuccessflly": "OTP berhasil dikirim", + "sent":"Terkirim", + "success":"Sukses", + "status":"Status", + "viewFile":"Lihat Berkas", + "type":"Tipe", + "statusSuccess":"Sukses", + "statusFail":"Gagal", + "unlimited":"Tak Terbatas", + "setLocation":"Atur Lokasi Anda", + "setAPIkey":"Harap atur kunci API", + "setLocationforBetter":"Atur lokasi Anda untuk hasil pencarian yang lebih baik", + "pageNotFoundErrorMsg": "Halaman Tidak Ditemukan", + "payWith": "Bayar dengan", + "paystack": "Paystack", + "phoneNumber":"Nomor Telepon", + "pendingLbl": "Menunggu", + "policyAggreementStatement": "Dengan mengeklik masuk, Anda menyetujui", + "postedSinceLbl": "Diposting Sejak", + "previouslbl": "Sebelumnya", + "unableToSave": "Tidak dapat disimpan!", + "successfullyAdded": "Berhasil ditambahkan!", + "successfullySaved": "Berhasil disimpan!", + "privacyPolicy": "Kebijakan Privasi", + "profileTab": "Profil", + "profileUpdatedSuccessMessage": "Profil berhasil diperbarui", + "profileupdated": "Profil berhasil diperbarui", + "updateProperty": "Perbarui Properti", + "progressLbl": "Progres", + "promote":"Promosikan", + "createAdvertisment":"Buat Iklan", + "promoted": "DIPROMOSIKAN", + "promotedProperties": "Properti Unggulan", + "properties": "Properti", + "propertyNameLbl": "Nama Properti", + "taploacktostop":"Ketuk kunci untuk berhenti", + "slidetocancel":"Geser untuk membatalkan", + "rateUs": "Beri peringkat", + "razorpay": "Razorpay", + "subscribeToPackage":"Berlangganan paket", + "readMoreLbl": "Baca Lebih Lanjut", + "readLessLbl": "Baca lebih sedikit", + "more":"Lebih", + "resendCodeBtnLbl": "Kirim ulang kode", + "resendMessage": "Kirim ulang kode dalam", + "resendMessageDuration": " Detik", + "searchHintLbl": "Cari rumah Anda...", + "seeAll": "Lihat Semua", + "all": "Semua", + "selectLocation": "Pilih Lokasi", + "selectLocationOptional":"Pilih Lokasi (opsional)", + "sendEnqBtnLbl": "Kirim Pertanyaan", + "sendingEnquiry": "Mengirim pertanyaan", + "proeprtyType":"Tipe Properti", + "propertyAdded":"Properti Berhasil Ditambahkan", + "propertyUpdated":"Properti Berhasil Diperbarui", + "shareApp": "Bagikan Aplikasi ini", + "share":"Bagikan", + "approved": "Disetujui", + "pending": "Menunggu", + "rejected": "Ditolak", + "listedBy":"Ditayangkan Oleh", + "noChats":"Tidak ada obrolan ditemukan", + "loading":"Memuat...", + "change":"Ubah", + "changePropertyStatus":"Ubah Status Properti", + "sold":"Terjual", + "state": "Negara Bagian", + "submitProperty":"Kirim", + "submitBtnLbl": "Kirim", + "subscribe": "Berlangganan", + "subscription": "Berlangganan", + "termsConditions": "Syarat & Ketentuan", + "typeOfProperty": "Pilih Kategori", + "update": "Perbarui", + "pleaseSelectCategory": "Silakan pilih kategori", + "property":"Properti", + "startedOn":"Dimulai pada", + "willEndOn":"Berakhir pada", + "daysRemining":"Sisa hari", + "advertisment":"Iklan", + "updateProfile": "Perbarui Profil", + "uploadBtnLbl": "Unggah", + "uploadImgMsgLbl": "Unggah Gambar Utama", + "userDeleteErrorMessage": "Pengguna harus masuk kembali sebelum operasi ini dapat dilakukan.\nKemudian Anda dapat memulai prosedur penghapusan.", + "validity": "Validitas", + "packageValidity":"Validitas Paket", + "verificationMessage": "Silakan masukkan kode verifikasi 6 digit yang dikirim ke", + "proceed":"Lanjutkan", + "preview":"Pratinjau", + "selectNearestPlaces": "Pilih Tempat Terdekat", + "rentPrice": "Harga Sewa", + "previewNotAvail":"Pratinjau Tidak Tersedia", + "warning": "Peringatan", + "currentPacakgeActiveWarning":"Anda memiliki langganan yang ada. Berlangganan paket ini akan membatalkan langganan sebelumnya, apakah Anda yakin ingin melanjutkan?", + "weSendYouCode": "kami akan mengirimkan kode konfirmasi kepada Anda", + "weSentCodeOnNumber": "Masukkan kode OTP yang dikirim ke", + "chat":" Obrolan", + "writeSomething": "Tulis sesuatu di sini...", + "yesterdayLbl": "Kemarin", + "days":"Hari", + "yourPackageReachedLimit": "Paket Anda telah mencapai batasnya", + "yourPackageReachedLimitDescription": "Anda telah menggunakan semua unit yang tersedia untuk paket ini", + "maxSize":"(Maks 3MB)", + "maxSizePerImage":"(Maks 3MB per gambar)", + "Monthly": "Bulanan", + "Quarterly": "Triwulanan", + "Yearly": "Tahunan", + "uploadMainPicture": "Unggah gambar utama", + "recentlyAdded": "Baru Ditambahkan", + "advertisement": "Iklan", + "congratulations": "Selamat!", + "submittedSuccess": "Properti Anda \n Berhasil Dikirim", + "previewProperty": "Pratinjau Properti", + "backToHome": "Kembali ke beranda", + "didYoufindProblem": "Apakah Anda menemukan masalah dengan daftar ini?", + "writeReasonHere": "Tulis alasan Anda di sini", + "notReally": "Tidak benar-benar", + "yes": "Ya", + "projectName": "Nama Proyek", + "projectDetails": "Detail Proyek", + "projectStatus": "Status Proyek", + "Upcoming": "Akan Datang", + "upcoming": "Akan Datang", + "under_construction": "Sedang Dibangun", + "Under Construction": "Sedang Dibangun", + "projectLocation": "Lokasi Proyek", + "UploadDocs": "Unggah Dokumen", + "videoLink": "Tautan Video", + "projectDocuments": "Dokumen Proyek", + "floorPlans": "Rencana Lantai", + "FloorPlans": "Rencana Lantai", + "uploadOtherImages": "Unggah Gambar Lain", + "Add": "Tambah", + "Floor Title": "Judul Lantai", + "pickFloorMap": "Pilih Peta Lantai", + "addProjectMeta": "Detail Meta Proyek", + "metaDetails": "Detail Meta", + "metaTitle": "Judul Meta", + "metaKeywords": "Kata Kunci Meta", + "metaDescription": "Deskripsi Meta", + "addMetaImage": "Tambahkan Gambar Meta", + "Project section": "Bagian Proyek", + "myProjects": "Proyek Saya", + "Documents": "Dokumen", + "projectType": "Tipe Proyek", + "noFeaturedAdsYes": "Belum Ada Iklan Unggulan Ditambahkan. ", + "noFeaturedDescription": "Tampilkan Properti Anda untuk Visibilitas Premium!", + "noPropertyAdded": "Belum Ada Properti Ditambahkan", + "noPropertyDescription": "Mulai Terhubung dengan Menambahkan Properti Anda!", + "toAccessSubscribe": "Untuk mengakses properti pribadi, silakan berlangganan." + +} \ No newline at end of file diff --git a/assets/lottie/.DS_Store b/assets/lottie/.DS_Store new file mode 100644 index 0000000..0fe128c Binary files /dev/null and b/assets/lottie/.DS_Store differ diff --git a/assets/lottie/_nodatafound.json b/assets/lottie/_nodatafound.json new file mode 100644 index 0000000..4fe0e0a --- /dev/null +++ b/assets/lottie/_nodatafound.json @@ -0,0 +1 @@ +{"v":"5.9.0","fr":60,"ip":0,"op":316,"w":642,"h":642,"nm":"NEW sin movs 2","ddd":0,"assets":[{"id":"comp_0","nm":"NEW sin movs","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"escalador papel","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[321,161.5,0],"ix":2,"l":2},"a":{"a":0,"k":[60,60,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":421,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"lupa Outlines 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":34,"s":[64]},{"i":{"x":[0.437],"y":[0.995]},"o":{"x":[0.7],"y":[0]},"t":92,"s":[23]},{"i":{"x":[0.358],"y":[1]},"o":{"x":[0.544],"y":[0]},"t":133,"s":[-21]},{"i":{"x":[0.326],"y":[0.985]},"o":{"x":[0.595],"y":[0]},"t":183,"s":[16]},{"t":220,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.222,"y":1},"o":{"x":0.167,"y":0.167},"t":34,"s":[240.252,377.045,0],"to":[13.917,-18.667,0],"ti":[-5.248,0.545,0]},{"i":{"x":0.453,"y":0.965},"o":{"x":0.785,"y":0},"t":88,"s":[323.752,265.045,0],"to":[5.248,-0.545,0],"ti":[-24.248,-17.455,0]},{"i":{"x":0.156,"y":1},"o":{"x":0.423,"y":0.342},"t":133,"s":[334.252,403.045,0],"to":[24.248,17.455,0],"ti":[-27.5,-2,0]},{"t":183.03515625,"s":[499.252,415.045,0]}],"ix":2,"l":2},"a":{"a":0,"k":[47.206,48.054,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.15,0.15,0.15],"y":[0,0,0]},"t":34,"s":[0,0,100]},{"i":{"x":[0.53,0.53,0.53],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":60,"s":[240,240,100]},{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.42,0.42,0.42],"y":[0,0,0]},"t":133,"s":[240,240,100]},{"t":183,"s":[400,400,100]}],"ix":6,"l":2}},"ao":0,"ip":34,"op":12647,"st":18,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"LUPA rotacion 3D","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[26.162,29.375,0],"ix":2,"l":2},"a":{"a":0,"k":[48.528,49.656,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.25,"y":1},"o":{"x":0.167,"y":0},"t":202,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[3.438,-0.004],[0.027,3.406],[0.01,3.421],[0.056,3.461],[-3.375,0.031],[-3.421,-0.01],[-3.397,-0.021],[0.014,-3.432],[-0.01,-3.421],[0.024,-3.444],[3.454,-0.013],[3.421,0.011]],"c":true}]},{"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[12.204,8.794],[8.792,12.204],[0.01,3.422],[-8.706,12.137],[-12.137,8.707],[-3.421,-0.01],[-12.204,-8.793],[-8.792,-12.204],[-0.01,-3.421],[8.706,-12.137],[12.137,-8.706],[3.421,0.011]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[46.174,44.297],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":202,"s":[0]},{"t":205,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0},"t":35,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"i":{"x":0.765,"y":1},"o":{"x":0.8,"y":0},"t":60,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.21,"y":0},"t":84,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0},"t":110,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.765,"y":1},"o":{"x":0.167,"y":0},"t":126,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.21,"y":0},"t":147,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"t":173,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":35,"s":[5.6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.8],"y":[0]},"t":60,"s":[3.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":84,"s":[5.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":110,"s":[3.6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0]},"t":126,"s":[3.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":147,"s":[5.6]},{"t":173,"s":[3.6]}],"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[45.074,45],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-42.5,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.883,-1.88],[0,0],[1.874,-1.878],[0,0],[1.884,1.881],[0,0],[-1.874,1.877]],"o":[[1.885,-1.879],[0,0],[1.877,1.874],[0,0],[-1.885,1.878],[0,0],[-1.877,-1.875],[0,0]],"v":[[-15.301,-15.277],[-8.483,-15.273],[15.305,8.476],[15.31,15.269],[15.302,15.277],[8.483,15.273],[-15.305,-8.475],[-15.31,-15.268]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[79.622,81.906],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":34,"op":421,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"line 1 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[299.82,224.817,0],"ix":2,"l":2},"a":{"a":0,"k":[36.006,1.8,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.8,1.8],[70.211,1.8]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.15],"y":[0]},"t":17,"s":[0]},{"t":39,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":17,"op":421,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"line 2 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[292.734,278.817,0],"ix":2,"l":2},"a":{"a":0,"k":[32.463,1.8,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.8,1.8],[63.125,1.8]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.15],"y":[0]},"t":24,"s":[0]},{"t":46,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":24,"op":428,"st":7,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"line 3 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[258.464,329.218,0],"ix":2,"l":2},"a":{"a":0,"k":[15.327,1.8,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.8,1.8],[28.855,1.8]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.15],"y":[0]},"t":31,"s":[0]},{"t":53,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":31,"op":435,"st":14,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"line 4 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[258.464,379.618,0],"ix":2,"l":2},"a":{"a":0,"k":[15.327,1.8,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.8,1.8],[28.855,1.8]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.15],"y":[0]},"t":38,"s":[0]},{"t":60,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":38,"op":442,"st":21,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Sombra lupa","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[321,321,0],"ix":2,"l":2},"a":{"a":0,"k":[321,321,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[-7.5,3.5],[0,0],[0,0],[0,0],[0,0],[-4,0],[0,-2.5],[-29.5,0],[0,15],[0,5]],"o":[[-13,0],[0,0],[0,0],[0,0],[0,0],[4,0],[0,2.5],[29.5,0],[0,-15],[0,-5]],"v":[[412.5,169.5],[231.5,169.5],[202,190.5],[191,254],[191,428.5],[329,428.5],[333.5,434.5],[365.5,478.5],[399,436.5],[399,193]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"w":642,"h":642,"ip":0,"op":421,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"papel bot Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-8.861,353.102,0],"ix":2,"l":2},"a":{"a":0,"k":[57.389,18.529,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":1,"k":[{"i":{"x":0.35,"y":1},"o":{"x":0.167,"y":0},"t":25,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[114.817,36.647],[1.381,36.647],[1.32,36.478],[114.756,36.478]],"c":true}]},{"t":45,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[114.756,5.022],[1.32,5.022],[1.32,36.478],[114.756,36.478]],"c":true}]}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0.013,-9.145],[0,0],[0,0],[7.994,-2.105],[1.814,0.473],[-0.022,8.366],[0,0],[1.528,0.009],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-1.541],[0,0],[-13.061,-0.011],[0,0],[0,0],[0,0],[-0.609,0.093],[-1.049,0.261],[-0.013,11.099],[0,0],[0,0],[-5.938,0.542],[-0.345,-0.019],[-0.011,-6.47],[0,0],[0,0],[0,1.541],[0,0],[9.485,0.042]],"o":[[0,0],[-9.048,0.552],[0,0],[0,0],[-0.017,8.335],[-1.815,0.473],[-8.019,-2.135],[0,0],[0.011,-1.541],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.527,0],[0,0],[-0.012,13.175],[0,0],[0,0],[0,0],[0.609,0],[1.076,-0.099],[10.716,-2.516],[0,0],[0,0],[0.079,-6.014],[0.344,-0.019],[6.414,0.01],[0,0],[0,0],[1.528,0],[0,0],[0.04,-9.569],[0,0]],"v":[[89.342,-145.931],[88.309,-145.931],[72.188,-128.68],[72.188,-128.419],[72.188,-6.1],[58.594,11.617],[53.059,11.617],[39.481,-6.211],[39.481,-9.933],[36.734,-12.743],[19.195,-12.743],[5.094,-12.743],[-16.645,-12.743],[-39.406,-12.743],[-53.01,-12.743],[-55.777,-9.951],[-55.777,-6.006],[-32.146,17.869],[-32.111,17.869],[-2.276,17.869],[54.109,17.869],[56.268,17.776],[59.46,17.237],[77.777,-6.006],[77.777,-128.586],[77.777,-128.717],[88.365,-140.254],[89.398,-140.254],[101.02,-128.531],[101.02,-115.262],[103.787,-115.262],[106.555,-118.053],[106.555,-128.531],[89.454,-145.931]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[58.74,18.119],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":25,"op":421,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Papel front Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[42.09,219.148,0],"ix":2,"l":2},"a":{"a":0,"k":[74.219,89.605,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":1,"k":[{"i":{"x":0.99,"y":1},"o":{"x":0.28,"y":0},"t":10,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[152.276,7.781],[-1.326,7.781],[-1.326,9.031],[152.276,9.031]],"c":true}]},{"t":25,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[152.276,7.781],[-1.326,7.781],[-1.326,175.031],[152.276,175.031]],"c":true}]}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0.012,-9.145],[0,0],[0,0],[7.994,-2.106],[0.894,-0.009],[0,0],[0,0],[0,0],[0,0],[-5.953,-0.005],[0,0],[0,0],[0,0],[-0.61,0.093],[-1.049,0.26],[-0.014,11.099],[0,0],[0,0],[-5.937,0.544],[-0.326,-0.016]],"o":[[0,0],[-9.048,0.551],[0,0],[0,0],[-0.017,8.335],[-0.874,0.227],[0,0],[0,0],[0,0],[0,0],[4.154,3.667],[0,0],[0,0],[0,0],[0.608,0],[1.075,-0.099],[10.716,-2.517],[0,0],[0,0],[0.079,-6.015],[0.328,-0.018],[0,0]],"v":[[68.364,-81.9],[67.331,-81.9],[51.208,-64.649],[51.208,-64.388],[51.208,57.931],[37.614,75.648],[34.951,75.996],[34.951,75.999],[34.882,75.999],[34.811,75.999],[-68.667,75.999],[-53.126,81.9],[-53.089,81.9],[-23.254,81.9],[33.132,81.9],[35.29,81.807],[38.48,81.268],[56.799,58.024],[56.799,-64.556],[56.799,-64.686],[67.385,-76.224],[68.667,-76.224]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[71.074,92.141],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-57.614,-15.262],[-57.614,-11.587]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,3.997],[0,8.289]],"o":[[5.953,0],[0,-32.687],[0,0]],"v":[[-65.219,79.059],[-58.189,68.737],[-58.135,4.655]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-13.063,0.021],[0,0]],"o":[[0,0],[0.021,-13.171],[0,0],[0,0]],"v":[[-58.135,-24.607],[-58.135,-55.195],[-34.467,-79.059],[65.219,-79.059]],"c":false},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[74.219,91.151],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.02,-13.17],[0,0],[0,0]],"o":[[-13.063,0.021],[0,0],[0,0],[0,0]],"v":[[-6.133,-52.006],[-29.801,-28.142],[-29.801,52.08],[29.801,-52.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176474094,0.941176474094,0.941176474094,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[45.83,62.33],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0.02,-13.17],[0,0],[-1.527,0],[0,0],[-0.009,13.153],[0,0],[0,0],[-6.363,0.009],[-0.025,0.001]],"o":[[0,0],[0,0],[-13.062,0.02],[0,0],[0,1.54],[0,0],[13.045,-0.041],[0,0],[0,0],[0.081,-6.417],[0.026,0],[0,0]],"v":[[61.639,-76.59],[61.639,-81.84],[-38.048,-81.84],[-61.714,-57.977],[-61.714,79.05],[-58.947,81.84],[26.406,81.84],[50.018,57.977],[50.018,-64.542],[50.018,-64.673],[61.639,-76.581],[61.714,-76.59]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[77.799,92.164],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":10,"op":421,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Papel top Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[89.026,94.609,0],"ix":2,"l":2},"a":{"a":0,"k":[69.157,27.261,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[133.251,43.755],[8.394,43.755],[8.394,45.207],[133.251,45.207]],"c":true}]},{"t":10,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[133.324,9.505],[8.466,9.505],[8.394,45.207],[133.251,45.207]],"c":true}]}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.544],[-1.545,0],[-0.018,-0.001],[-0.01,-6.471],[0,0],[0,0],[0,1.541],[0,0],[9.485,0.041],[0,0],[0,0],[0.251,-0.027],[0,0]],"o":[[0,1.544],[97.877,0],[6.414,0.011],[0,0],[0,0],[1.527,0],[0,0],[0.041,-9.569],[0,0],[0,0],[-0.254,0.015],[0,0],[-1.544,0]],"v":[[-58.955,-12.455],[-56.155,-9.658],[41.758,-9.658],[53.379,2.066],[53.379,15.334],[56.147,15.334],[58.914,12.543],[58.914,2.066],[41.814,-15.334],[41.703,-15.334],[40.67,-15.334],[39.918,-15.251],[-56.159,-15.251]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[69.205,25.502],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.67,0],[0,-9.44],[0,0],[-1.527,0],[0,0],[0,1.632],[0,0],[9.475,0]],"o":[[16,0],[0,0],[0,1.632],[0,0],[1.528,0],[0,0],[0,-10.128],[-0.642,0]],"v":[[-58.571,-15.544],[-40.571,3.353],[-40.571,13.652],[-37.803,16.61],[56.472,15.79],[59.24,12.833],[59.24,1.73],[42.083,-16.61]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[68.824,26.86],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":421,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"circulito Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[516.83,232.086,0],"ix":2,"l":2},"a":{"a":0,"k":[7.194,7.198,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.434,0.434,0.67],"y":[1,1,1]},"o":{"x":[0.233,0.233,0.33],"y":[0,0,0]},"t":232,"s":[0,0,100]},{"i":{"x":[0.196,0.196,0.67],"y":[1,1,1]},"o":{"x":[0.172,0.172,0.33],"y":[0,0,0]},"t":252,"s":[240,240,100]},{"t":284,"s":[200,200,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.643,0],[0,1.652],[1.642,0],[0,-1.652]],"o":[[1.642,0],[0,-1.652],[-1.643,0],[0,1.652]],"v":[[0,2.988],[2.976,0],[0,-2.988],[-2.977,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[3.837,0],[0,3.836],[-3.837,0],[0,-3.836]],"o":[[-3.837,0],[0,-3.836],[3.837,0],[0,3.836]],"v":[[0,6.948],[-6.944,0],[0,-6.948],[6.944,0]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[7.194,7.198],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":232,"op":566,"st":145,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"x 2 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[96.613,416.097,0],"ix":2,"l":2},"a":{"a":0,"k":[6.515,6.515,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.434,0.434,0.67],"y":[1,1,1]},"o":{"x":[0.233,0.233,0.33],"y":[0,0,0]},"t":252,"s":[0,0,100]},{"i":{"x":[0.196,0.196,0.67],"y":[1,1,1]},"o":{"x":[0.172,0.172,0.33],"y":[0,0,0]},"t":272,"s":[240,240,100]},{"t":304,"s":[200,200,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[6.258,3.885],[3.885,6.258],[0.001,2.372],[-3.891,6.264],[-6.264,3.891],[-2.374,-0.001],[-6.258,-3.885],[-3.885,-6.26],[-0.001,-2.374],[3.891,-6.264],[6.264,-3.891],[2.374,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.515,6.515],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":252,"op":441,"st":20,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"bg Outlines","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":7,"s":[0]},{"t":49,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[318.455,325.042,0],"ix":2,"l":2},"a":{"a":0,"k":[119.513,102.602,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-34.357,-7.157],[-2.441,47.062],[17.957,18.605],[34.584,38.554],[38.389,-25.44],[-24.266,-40.899]],"o":[[79.337,8.029],[3.66,-70.593],[-17.958,-18.605],[-23.057,-25.703],[-30.965,25.202],[24.266,40.898]],"v":[[-7.063,94.323],[115.603,35.774],[39.645,-45.798],[7.22,-76.517],[-84.949,-76.912],[-94.997,22.24]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.898039215686,0.949019607843,0.949019607843,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[119.513,102.602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":421,"st":0,"bm":0}]},{"id":"comp_1","nm":"Sombra lupa","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"parent sombra","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":34,"s":[64]},{"i":{"x":[0.437],"y":[0.995]},"o":{"x":[0.7],"y":[0]},"t":92,"s":[23]},{"i":{"x":[0.358],"y":[1]},"o":{"x":[0.544],"y":[0]},"t":133,"s":[-21]},{"i":{"x":[0.326],"y":[0.985]},"o":{"x":[0.595],"y":[0]},"t":183,"s":[16]},{"t":220,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.222,"y":1},"o":{"x":0.167,"y":0.167},"t":34,"s":[240.252,377.045,0],"to":[13.917,-18.667,0],"ti":[-5.248,0.545,0]},{"i":{"x":0.453,"y":0.965},"o":{"x":0.785,"y":0},"t":88,"s":[323.752,265.045,0],"to":[5.248,-0.545,0],"ti":[-24.248,-17.455,0]},{"i":{"x":0.156,"y":1},"o":{"x":0.423,"y":0.342},"t":133,"s":[334.252,403.045,0],"to":[24.248,17.455,0],"ti":[-27.5,-2,0]},{"t":183.03515625,"s":[499.252,415.045,0]}],"ix":2,"l":2},"a":{"a":0,"k":[47.206,48.054,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.15,0.15,0.15],"y":[0,0,0]},"t":34,"s":[0,0,100]},{"i":{"x":[0.53,0.53,0.53],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":60,"s":[240,240,100]},{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.42,0.42,0.42],"y":[0,0,0]},"t":133,"s":[240,240,100]},{"t":183,"s":[400,400,100]}],"ix":6,"l":2}},"ao":0,"ip":34,"op":12647,"st":18,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"LUPA sombra","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[26.162,29.375,0],"ix":2,"l":2},"a":{"a":1,"k":[{"i":{"x":0.67,"y":1},"o":{"x":0.33,"y":0},"t":133,"s":[59.528,35.656,0],"to":[-0.683,1.317,0],"ti":[0.683,-1.317,0]},{"t":183,"s":[55.428,43.556,0]}],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0},"t":35,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"i":{"x":0.765,"y":1},"o":{"x":0.8,"y":0},"t":60,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.21,"y":0},"t":84,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0},"t":110,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.765,"y":1},"o":{"x":0.167,"y":0},"t":126,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.21,"y":0},"t":147,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"t":173,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.898039215686,0.949019607843,0.949019607843,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":35,"s":[5.6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.8],"y":[0]},"t":60,"s":[3.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":84,"s":[5.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":110,"s":[3.6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0]},"t":126,"s":[3.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":147,"s":[5.6]},{"t":173,"s":[3.6]}],"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.898039215686,0.949019607843,0.949019607843,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[45.074,45],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-42.5,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.883,-1.88],[0,0],[1.874,-1.878],[0,0],[1.884,1.881],[0,0],[-1.874,1.877]],"o":[[1.885,-1.879],[0,0],[1.877,1.874],[0,0],[-1.885,1.878],[0,0],[-1.877,-1.875],[0,0]],"v":[[-15.301,-15.277],[-8.483,-15.273],[15.305,8.476],[15.31,15.269],[15.302,15.277],[8.483,15.273],[-15.305,-8.475],[-15.31,-15.268]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.898039215686,0.949019607843,0.949019607843,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[79.622,81.906],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":34,"op":421,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"NEW sin movs","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[321,321,0],"ix":2,"l":2},"a":{"a":0,"k":[321,321,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":642,"h":642,"ip":0,"op":316,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/assets/lottie/loading.json b/assets/lottie/loading.json new file mode 100644 index 0000000..879f68b --- /dev/null +++ b/assets/lottie/loading.json @@ -0,0 +1,1526 @@ +{ + "nm": "loading", + "mn": "", + "layers": [ + { + "ty": 4, + "nm": "Layer 5 Outlines", + "mn": "", + "sr": 1, + "st": 0, + "op": 50, + "ip": 0, + "hd": false, + "cl": "", + "ln": "", + "ddd": 0, + "bm": 0, + "tt": 0, + "hasMask": false, + "td": 0, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [ + 39.639, + 39.363, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 273.692, + 264.004, + 0 + ], + "t": 0 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 249.692, + 238.504, + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 102.152, + 237.514, + 0 + ], + "t": 20 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 75.986, + 264.652, + 0 + ], + "t": 35 + } + ], + "ix": 2 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 90 + ], + "t": 20 + } + ], + "ix": 10 + } + }, + "ef": [], + "shapes": [ + { + "ty": "gr", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + -0.002, + 8.87 + ], + [ + 0.307, + 9.885 + ], + [ + -7.001, + -0.101 + ], + [ + -20.127, + 0.246 + ], + [ + -0.027, + -5.58 + ], + [ + 0.213, + -19.786 + ], + [ + 7.326, + 0.11 + ], + [ + 18.424, + -0.127 + ], + [ + -0.751, + 8.993 + ] + ], + "o": [ + [ + 0.001, + -9.896 + ], + [ + -0.195, + -6.285 + ], + [ + 20.126, + 0.289 + ], + [ + 6.265, + -0.078 + ], + [ + 0.095, + 19.788 + ], + [ + -0.074, + 6.933 + ], + [ + -18.42, + -0.276 + ], + [ + -8.522, + 0.057 + ], + [ + 0.736, + -8.81 + ] + ], + "v": [ + [ + -38.476, + 0.024 + ], + [ + -38.556, + -29.658 + ], + [ + -29.966, + -39.012 + ], + [ + 30.422, + -39.001 + ], + [ + 39.284, + -30.48 + ], + [ + 39.176, + 28.883 + ], + [ + 28.115, + 39.003 + ], + [ + -27.157, + 38.947 + ], + [ + -38.638, + 26.624 + ] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "c": { + "a": 0, + "k": [ + 0.0314, + 0.4863, + 0.4863 + ], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [ + 39.639, + 39.363 + ], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 0 + }, + { + "ty": 4, + "nm": "cube 2 Outlines", + "mn": "", + "sr": 1, + "st": 0, + "op": 50, + "ip": 0, + "hd": false, + "cl": "", + "ln": "", + "ddd": 0, + "bm": 0, + "tt": 0, + "hasMask": false, + "td": 0, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [ + 39.628, + 39.611, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 76.311, + 265.093, + 0 + ], + "t": 0 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 103.061, + 237.593, + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 102.601, + 90.397, + 0 + ], + "t": 20 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 76.101, + 65.093, + 0 + ], + "t": 35 + } + ], + "ix": 2 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 90 + ], + "t": 20 + } + ], + "ix": 10 + } + }, + "ef": [], + "shapes": [ + { + "ty": "gr", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + -0.001, + -9.556 + ], + [ + -0.41, + -9.879 + ], + [ + 6.697, + -0.013 + ], + [ + 18.429, + 0.122 + ], + [ + -0.298, + 9.885 + ], + [ + 0.053, + 17.064 + ], + [ + -10.503, + -0.497 + ], + [ + -18.425, + 0.287 + ], + [ + 0.288, + -7.214 + ] + ], + "o": [ + [ + 0.001, + 9.897 + ], + [ + 0.294, + 7.073 + ], + [ + -18.43, + 0.035 + ], + [ + -8.916, + -0.058 + ], + [ + 0.515, + -17.049 + ], + [ + -0.027, + -8.788 + ], + [ + 18.388, + 0.87 + ], + [ + 6.954, + -0.107 + ], + [ + -0.381, + 9.541 + ] + ], + "v": [ + [ + 38.98, + -0.574 + ], + [ + 39.083, + 29.113 + ], + [ + 29.581, + 39.262 + ], + [ + -25.707, + 39.24 + ], + [ + -39.079, + 25.488 + ], + [ + -38.949, + -25.701 + ], + [ + -25.917, + -38.865 + ], + [ + 29.359, + -38.717 + ], + [ + 39.072, + -29.238 + ] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "c": { + "a": 0, + "k": [ + 0.8078, + 0.8588, + 0.8588 + ], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [ + 39.627, + 39.611 + ], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 1 + }, + { + "ty": 4, + "nm": "cube 3 Outlines", + "mn": "", + "sr": 1, + "st": 0, + "op": 50, + "ip": 0, + "hd": false, + "cl": "", + "ln": "", + "ddd": 0, + "bm": 0, + "tt": 0, + "hasMask": false, + "td": 0, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [ + 39.465, + 39.399, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 275.125, + 64.031, + 0 + ], + "t": 0 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 249.625, + 91.031, + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 249.625, + 237.553, + 0 + ], + "t": 20 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 274.969, + 265.125, + 0 + ], + "t": 35 + } + ], + "ix": 2 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 90 + ], + "t": 20 + } + ], + "ix": 10 + } + }, + "ef": [], + "shapes": [ + { + "ty": "gr", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + 8.873, + 0.002 + ], + [ + 9.548, + -0.285 + ], + [ + -0.078, + 6.772 + ], + [ + 0.279, + 19.791 + ], + [ + -6.186, + 0.033 + ], + [ + -19.107, + -0.282 + ], + [ + 0.206, + -8.717 + ], + [ + 0.169, + -17.063 + ], + [ + 12.029, + -0.025 + ] + ], + "o": [ + [ + -9.557, + -0.003 + ], + [ + -6.413, + 0.192 + ], + [ + 0.227, + -19.792 + ], + [ + -0.093, + -6.557 + ], + [ + 19.111, + -0.103 + ], + [ + 8.092, + 0.12 + ], + [ + -0.403, + 17.055 + ], + [ + -0.124, + 12.438 + ], + [ + -8.873, + 0.019 + ] + ], + "v": [ + [ + -1.143, + 38.887 + ], + [ + -29.809, + 38.958 + ], + [ + -39.1, + 30.12 + ], + [ + -39.122, + -29.26 + ], + [ + -30.236, + -38.949 + ], + [ + 27.097, + -38.867 + ], + [ + 39.01, + -25.832 + ], + [ + 38.85, + 25.357 + ], + [ + 25.476, + 38.886 + ] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "c": { + "a": 0, + "k": [ + 0.7843, + 0.8392, + 0.8392 + ], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [ + 39.465, + 39.4 + ], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 2 + }, + { + "ty": 4, + "nm": "cube 4 Outlines", + "mn": "", + "sr": 1, + "st": 0, + "op": 50, + "ip": 0, + "hd": false, + "cl": "", + "ln": "", + "ddd": 0, + "bm": 0, + "tt": 0, + "hasMask": false, + "td": 0, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [ + 39.634, + 39.346, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 76.267, + 63.981, + 0 + ], + "t": 0 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 99.767, + 91.006, + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 249.396, + 91.006, + 0 + ], + "t": 20 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 274.89, + 65.082, + 0 + ], + "t": 35 + } + ], + "ix": 2 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 90 + ], + "t": 20 + } + ], + "ix": 10 + } + }, + "ef": [], + "shapes": [ + { + "ty": "gr", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + 0.003, + -9.212 + ], + [ + -0.288, + -9.886 + ], + [ + 6.112, + 0.064 + ], + [ + 20.127, + -0.256 + ], + [ + 0.031, + 5.612 + ], + [ + -0.209, + 19.786 + ], + [ + -7.384, + -0.112 + ], + [ + -18.423, + 0.131 + ], + [ + 0.741, + -9.077 + ] + ], + "o": [ + [ + -0.003, + 9.895 + ], + [ + 0.181, + 6.219 + ], + [ + -20.127, + -0.208 + ], + [ + -6.228, + 0.079 + ], + [ + -0.108, + -19.788 + ], + [ + 0.072, + -6.877 + ], + [ + 18.42, + 0.278 + ], + [ + 8.459, + -0.06 + ], + [ + -0.747, + 9.151 + ] + ], + "v": [ + [ + 38.477, + 0.925 + ], + [ + 38.551, + 30.607 + ], + [ + 30.062, + 38.999 + ], + [ + -30.327, + 39.018 + ], + [ + -39.268, + 30.579 + ], + [ + -39.176, + -28.784 + ], + [ + -28.195, + -38.984 + ], + [ + 27.076, + -38.931 + ], + [ + 38.644, + -26.697 + ] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "c": { + "a": 0, + "k": [ + 0.3255, + 0.7647, + 0.7647 + ], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [ + 39.895, + 39.223 + ], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 3 + } + + ], + "ddd": 0, + "h": 328, + "w": 352, + "meta": { + "a": "", + "k": "", + "d": "", + "g": "@lottiefiles/toolkit-js 0.21.1", + "tc": "" + }, + "v": "5.5.8", + "fr": 60, + "op": 50, + "ip": 0, + "assets": [] + } \ No newline at end of file diff --git a/assets/lottie/loading_white.json b/assets/lottie/loading_white.json new file mode 100644 index 0000000..08dda10 --- /dev/null +++ b/assets/lottie/loading_white.json @@ -0,0 +1,1526 @@ +{ + "nm": "loading", + "mn": "", + "layers": [ + { + "ty": 4, + "nm": "Layer 5 Outlines", + "mn": "", + "sr": 1, + "st": 0, + "op": 50, + "ip": 0, + "hd": false, + "cl": "", + "ln": "", + "ddd": 0, + "bm": 0, + "tt": 0, + "hasMask": false, + "td": 0, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [ + 39.639, + 39.363, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 273.692, + 264.004, + 0 + ], + "t": 0 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 249.692, + 238.504, + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 102.152, + 237.514, + 0 + ], + "t": 20 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 75.986, + 264.652, + 0 + ], + "t": 35 + } + ], + "ix": 2 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 90 + ], + "t": 20 + } + ], + "ix": 10 + } + }, + "ef": [], + "shapes": [ + { + "ty": "gr", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + -0.002, + 8.87 + ], + [ + 0.307, + 9.885 + ], + [ + -7.001, + -0.101 + ], + [ + -20.127, + 0.246 + ], + [ + -0.027, + -5.58 + ], + [ + 0.213, + -19.786 + ], + [ + 7.326, + 0.11 + ], + [ + 18.424, + -0.127 + ], + [ + -0.751, + 8.993 + ] + ], + "o": [ + [ + 0.001, + -9.896 + ], + [ + -0.195, + -6.285 + ], + [ + 20.126, + 0.289 + ], + [ + 6.265, + -0.078 + ], + [ + 0.095, + 19.788 + ], + [ + -0.074, + 6.933 + ], + [ + -18.42, + -0.276 + ], + [ + -8.522, + 0.057 + ], + [ + 0.736, + -8.81 + ] + ], + "v": [ + [ + -38.476, + 0.024 + ], + [ + -38.556, + -29.658 + ], + [ + -29.966, + -39.012 + ], + [ + 30.422, + -39.001 + ], + [ + 39.284, + -30.48 + ], + [ + 39.176, + 28.883 + ], + [ + 28.115, + 39.003 + ], + [ + -27.157, + 38.947 + ], + [ + -38.638, + 26.624 + ] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1 + ], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [ + 39.639, + 39.363 + ], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 0 + }, + { + "ty": 4, + "nm": "cube 2 Outlines", + "mn": "", + "sr": 1, + "st": 0, + "op": 50, + "ip": 0, + "hd": false, + "cl": "", + "ln": "", + "ddd": 0, + "bm": 0, + "tt": 0, + "hasMask": false, + "td": 0, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [ + 39.628, + 39.611, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 76.311, + 265.093, + 0 + ], + "t": 0 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 103.061, + 237.593, + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 102.601, + 90.397, + 0 + ], + "t": 20 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 76.101, + 65.093, + 0 + ], + "t": 35 + } + ], + "ix": 2 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 90 + ], + "t": 20 + } + ], + "ix": 10 + } + }, + "ef": [], + "shapes": [ + { + "ty": "gr", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + -0.001, + -9.556 + ], + [ + -0.41, + -9.879 + ], + [ + 6.697, + -0.013 + ], + [ + 18.429, + 0.122 + ], + [ + -0.298, + 9.885 + ], + [ + 0.053, + 17.064 + ], + [ + -10.503, + -0.497 + ], + [ + -18.425, + 0.287 + ], + [ + 0.288, + -7.214 + ] + ], + "o": [ + [ + 0.001, + 9.897 + ], + [ + 0.294, + 7.073 + ], + [ + -18.43, + 0.035 + ], + [ + -8.916, + -0.058 + ], + [ + 0.515, + -17.049 + ], + [ + -0.027, + -8.788 + ], + [ + 18.388, + 0.87 + ], + [ + 6.954, + -0.107 + ], + [ + -0.381, + 9.541 + ] + ], + "v": [ + [ + 38.98, + -0.574 + ], + [ + 39.083, + 29.113 + ], + [ + 29.581, + 39.262 + ], + [ + -25.707, + 39.24 + ], + [ + -39.079, + 25.488 + ], + [ + -38.949, + -25.701 + ], + [ + -25.917, + -38.865 + ], + [ + 29.359, + -38.717 + ], + [ + 39.072, + -29.238 + ] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "c": { + "a": 0, + "k": [ + 0.9882, + 0.9882, + 0.9882 + ], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [ + 39.627, + 39.611 + ], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 1 + }, + { + "ty": 4, + "nm": "cube 3 Outlines", + "mn": "", + "sr": 1, + "st": 0, + "op": 50, + "ip": 0, + "hd": false, + "cl": "", + "ln": "", + "ddd": 0, + "bm": 0, + "tt": 0, + "hasMask": false, + "td": 0, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [ + 39.465, + 39.399, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 275.125, + 64.031, + 0 + ], + "t": 0 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 249.625, + 91.031, + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 249.625, + 237.553, + 0 + ], + "t": 20 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 274.969, + 265.125, + 0 + ], + "t": 35 + } + ], + "ix": 2 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 90 + ], + "t": 20 + } + ], + "ix": 10 + } + }, + "ef": [], + "shapes": [ + { + "ty": "gr", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + 8.873, + 0.002 + ], + [ + 9.548, + -0.285 + ], + [ + -0.078, + 6.772 + ], + [ + 0.279, + 19.791 + ], + [ + -6.186, + 0.033 + ], + [ + -19.107, + -0.282 + ], + [ + 0.206, + -8.717 + ], + [ + 0.169, + -17.063 + ], + [ + 12.029, + -0.025 + ] + ], + "o": [ + [ + -9.557, + -0.003 + ], + [ + -6.413, + 0.192 + ], + [ + 0.227, + -19.792 + ], + [ + -0.093, + -6.557 + ], + [ + 19.111, + -0.103 + ], + [ + 8.092, + 0.12 + ], + [ + -0.403, + 17.055 + ], + [ + -0.124, + 12.438 + ], + [ + -8.873, + 0.019 + ] + ], + "v": [ + [ + -1.143, + 38.887 + ], + [ + -29.809, + 38.958 + ], + [ + -39.1, + 30.12 + ], + [ + -39.122, + -29.26 + ], + [ + -30.236, + -38.949 + ], + [ + 27.097, + -38.867 + ], + [ + 39.01, + -25.832 + ], + [ + 38.85, + 25.357 + ], + [ + 25.476, + 38.886 + ] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "c": { + "a": 0, + "k": [ + 0.9882, + 0.9882, + 0.9882 + ], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [ + 39.465, + 39.4 + ], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 2 + }, + { + "ty": 4, + "nm": "cube 4 Outlines", + "mn": "", + "sr": 1, + "st": 0, + "op": 50, + "ip": 0, + "hd": false, + "cl": "", + "ln": "", + "ddd": 0, + "bm": 0, + "tt": 0, + "hasMask": false, + "td": 0, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [ + 39.634, + 39.346, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 76.267, + 63.981, + 0 + ], + "t": 0 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 99.767, + 91.006, + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 249.396, + 91.006, + 0 + ], + "t": 20 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 274.89, + 65.082, + 0 + ], + "t": 35 + } + ], + "ix": 2 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 0 + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + 90 + ], + "t": 20 + } + ], + "ix": 10 + } + }, + "ef": [], + "shapes": [ + { + "ty": "gr", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + 0.003, + -9.212 + ], + [ + -0.288, + -9.886 + ], + [ + 6.112, + 0.064 + ], + [ + 20.127, + -0.256 + ], + [ + 0.031, + 5.612 + ], + [ + -0.209, + 19.786 + ], + [ + -7.384, + -0.112 + ], + [ + -18.423, + 0.131 + ], + [ + 0.741, + -9.077 + ] + ], + "o": [ + [ + -0.003, + 9.895 + ], + [ + 0.181, + 6.219 + ], + [ + -20.127, + -0.208 + ], + [ + -6.228, + 0.079 + ], + [ + -0.108, + -19.788 + ], + [ + 0.072, + -6.877 + ], + [ + 18.42, + 0.278 + ], + [ + 8.459, + -0.06 + ], + [ + -0.747, + 9.151 + ] + ], + "v": [ + [ + 38.477, + 0.925 + ], + [ + 38.551, + 30.607 + ], + [ + 30.062, + 38.999 + ], + [ + -30.327, + 39.018 + ], + [ + -39.268, + 30.579 + ], + [ + -39.176, + -28.784 + ], + [ + -28.195, + -38.984 + ], + [ + 27.076, + -38.931 + ], + [ + 38.644, + -26.697 + ] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "cl": "", + "ln": "", + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1 + ], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [ + 39.895, + 39.223 + ], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 3 + } + + ], + "ddd": 0, + "h": 328, + "w": 352, + "meta": { + "a": "", + "k": "", + "d": "", + "g": "@lottiefiles/toolkit-js 0.21.1", + "tc": "#ffffff" + }, + "v": "5.5.8", + "fr": 60, + "op": 50, + "ip": 0, + "assets": [] +} diff --git a/assets/lottie/maintenancemode.json b/assets/lottie/maintenancemode.json new file mode 100644 index 0000000..7b55437 --- /dev/null +++ b/assets/lottie/maintenancemode.json @@ -0,0 +1 @@ +{"nm":"Design_03","mn":"","layers":[{"ty":4,"nm":"Lines 2","mn":"","sr":1,"st":0,"op":92.0000037472368,"ip":30.0000012219251,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[540,542.5,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Shape 5","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[187.5,300.5],[307.5,300.5]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Shape 4","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[187,249.5],[307.5,249.5]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Shape 3","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[188,206.5],[309,206.5]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Shape 2","ix":4,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-22,300.5],[-141,300.5]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":5,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":false,"i":[[-11,0],[0,0]],"o":[[11,0],[0,0]],"v":[[-20,249.5],[-80,249.5]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"tm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Trim","nm":"Trim Paths 1","ix":6,"e":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":30},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100],"t":60.0000024438501}],"ix":2},"o":{"a":0,"k":0,"ix":3},"s":{"a":0,"k":0,"ix":1},"m":1}],"ind":1},{"ty":4,"nm":"Hook Left Outlines","mn":"","sr":1,"st":0,"op":300.00001221925,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[18.752,10.629,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[94.327,153.825,0],"ix":2},"r":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[8],"t":30},{"o":{"x":0.167,"y":0},"i":{"x":0.667,"y":1},"s":[8],"t":36},{"o":{"x":0.167,"y":0},"i":{"x":0.833,"y":1},"s":[8],"t":55},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":61.0000024845809}],"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-29.598,36.05],[-11.727,50.748],[30.592,0.658],[-11.667,-50.757],[-29.737,-36.303],[0.477,0.451]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-10.746,61.378],[-40.378,37.019],[-9.433,0.386],[-40.502,-37.415],[-10.544,-61.378],[40.502,0.719]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[40.752,61.629],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-11.335,55.503],[34.978,0.684],[-11.206,-55.503],[-34.978,-36.486],[-4.647,0.418],[-34.848,36.171]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0314,0.4863,0.4863],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[40.448,57.121],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":2,"parent":6},{"ty":4,"nm":"Hexagon Outlines","mn":"","sr":1,"st":0,"op":300.00001221925,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[56.767,64.395,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[625.596,736.619,0],"t":30,"ti":[0,-9,0],"to":[0,9,0]},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[625.596,790.619,0],"t":52,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[625.596,790.619,0],"t":73.000002973351}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":6,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[5.803,0],[2.597,-1.457],[-4.55,-7.881],[-7.989,4.479],[4.547,7.881]],"o":[[-2.794,0],[-7.99,4.488],[4.547,7.884],[7.99,-4.485],[-3.069,-5.321]],"v":[[-0.408,-17.063],[-8.628,-14.916],[-14.868,7.516],[7.869,13.674],[14.112,-8.755]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[4.075,0],[4.48,7.762],[-11.659,6.548],[-6.632,-11.498],[11.656,-6.545]],"o":[[-8.468,0],[-6.635,-11.498],[11.66,-6.545],[6.635,11.499],[-3.788,2.129]],"v":[[-0.338,23.374],[-21.522,11.252],[-12.413,-21.476],[20.763,-12.491],[11.657,20.234]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 3","ix":3,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-48.834,27.419],[-0.345,55.425],[48.49,28.006],[48.835,-27.419],[0.345,-55.425],[-48.489,-28.009]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 4","ix":4,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-0.399,64.145],[-56.517,31.729],[-56.118,-32.413],[0.399,-64.145],[56.517,-31.732],[56.115,32.413]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[56.767,64.395],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[9.84,-5.524],[5.601,9.707],[-9.84,5.524],[-5.6,-9.707]],"o":[[-9.84,5.524],[-5.6,-9.707],[9.841,-5.524],[5.6,9.706]],"v":[[8.488,16.955],[-19.47,9.383],[-11.794,-18.195],[16.164,-10.622]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.372,-59.786],[-52.304,-30.211],[-52.674,29.575],[-0.371,59.786],[52.304,30.21],[52.674,-29.578]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0314,0.4863,0.4863],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[58.042,64.395],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":3},{"ty":4,"nm":"HookRight Outlines","mn":"","sr":1,"st":0,"op":300.00001221925,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[61.752,15.63,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[34.698,158.826,0],"ix":2},"r":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-8],"t":30},{"o":{"x":0.167,"y":0},"i":{"x":0.667,"y":1},"s":[-8],"t":36},{"o":{"x":0.167,"y":0},"i":{"x":0.833,"y":1},"s":[-8],"t":55},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":61.0000024845809}],"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-30.592,0.655],[11.728,50.746],[29.598,36.048],[-0.476,0.449],[29.734,-36.305],[11.667,-50.759]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[10.747,61.379],[-40.502,0.717],[10.544,-61.379],[40.502,-37.415],[9.434,0.384],[40.378,37.017]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[40.752,61.63],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[11.336,55.503],[-34.978,0.684],[11.206,-55.503],[34.978,-36.486],[4.647,0.418],[34.848,36.171]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0314,0.4863,0.4863],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[37.588,59.722],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":4,"parent":6},{"ty":4,"nm":"Shape Layer 1","mn":"","sr":1,"st":0,"op":300.00001221925,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[540,542.5,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[177,-209.5],[-61,-209.5],[-65,-156.5],[-68,-92.5],[162,-92.5],[170,-147.5]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0314,0.4863,0.4863],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":5},{"ty":4,"nm":"Hooker Clamp","mn":"","sr":1,"st":0,"op":300.00001221925,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":2,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[62.758,85.82,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[624.559,534.242,0],"t":30,"ti":[0,-8.5,0],"to":[0,9.333,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[624.559,590.242,0],"t":52,"ti":[0,9.333,0],"to":[0,8.5,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[624.559,585.242,0],"t":73,"ti":[0,8.5,0],"to":[0,-9.333,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[624.559,534.242,0],"t":90.0000036657751}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[3.827,31.46],[-3.827,31.46],[-3.642,-128.71],[4.012,-128.71]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[98.237,31.71],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[3.827,31.46],[-3.827,31.46],[-3.642,-129.21],[4.012,-129.21]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[29.352,31.71],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 3","ix":3,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[3.827,26.425],[-3.827,26.425],[-3.827,-26.425],[3.827,-26.425]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[65.071,129.863],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 4","ix":4,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16.585,3.775],[-16.585,3.775],[-16.585,-3.775],[16.585,-3.775]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[65.071,167.616],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 5","ix":5,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[3.827,23.91],[-3.827,23.91],[-3.827,-23.91],[3.827,-23.91]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[103.341,127.347],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 6","ix":6,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[3.827,21.392],[-3.827,21.392],[-3.853,-25.892],[3.801,-25.892]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[24.248,128.527],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 7","ix":7,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.94,-21.136],[0,0],[21.924,0],[0,0]],"o":[[0,0],[-1.94,-21.136],[0,0],[-21.924,0]],"v":[[-54.683,18.875],[54.684,18.875],[12.756,-18.875],[-12.756,-18.875]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[-27.436,0],[0,0],[0,-27.06]],"o":[[0,0],[0,0],[0,-27.06],[0,0],[27.433,0],[0,0]],"v":[[62.508,26.425],[-62.508,26.425],[-62.508,22.652],[-12.756,-26.425],[12.756,-26.425],[62.508,22.652]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[62.758,79.53],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 8","ix":8,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[40.822,-28.942],[40.822,11.576],[17.495,28.942],[-17.496,28.942],[-40.822,11.576],[-40.822,-28.942]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0314,0.4863,0.4863],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[63.795,134.897],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 9","ix":9,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,-25.019],[0,0],[-25.363,0],[0,0]],"o":[[0,0],[0,-25.019],[0,0],[25.364,0]],"v":[[58.681,22.65],[-58.681,22.65],[-12.758,-22.65],[12.757,-22.65]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0314,0.4863,0.4863],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[63.795,83.305],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":6},{"ty":4,"nm":"Gear Outlines","mn":"","sr":1,"st":0,"op":300.00001221925,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[68.824,65.077,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[456.50000000000006,570.5,0],"ix":2},"r":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":30},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[360],"t":90.0000036657751}],"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[9.144,0],[0,-9.02],[-9.144,0],[0,9.02]],"o":[[-9.144,0],[0,9.02],[9.144,0],[0,-9.02]],"v":[[0,-16.36],[-16.583,-0.002],[0,16.36],[16.583,-0.002]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[13.365,0],[0,13.183],[-13.365,0],[0,-13.184]],"o":[[-13.365,0],[0,-13.184],[13.365,0],[0,13.183]],"v":[[0,23.91],[-24.237,-0.002],[0,-23.91],[24.237,-0.002]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[69.125,65.24],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-2.265,2.649],[0,0],[0,0],[0,0],[0,0],[0,0],[0.297,3.437],[0,0],[0,0],[0,0],[0,0],[0,0],[2.68,2.226],[0,0],[0,0],[0,0],[0,0],[0,0],[3.471,-0.29],[0,0],[0,0],[0,0],[0,0],[0,0],[2.281,-2.668],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.288,-3.458],[0,0],[0,0],[0,0],[0,0],[0,0],[-2.67,-2.22],[0,0],[0,0],[0,0],[0,0],[0,0],[-3.487,0.29]],"o":[[0,0],[0,0],[0,0],[0,0],[2.797,-2.078],[0,0],[0,0],[0,0],[0,0],[0,0],[0.5,-3.43],[0,0],[0,0],[0,0],[0,0],[0,0],[-2.117,-2.772],[0,0],[0,0],[0,0],[0,0],[0,0],[-3.5,-0.487],[0,0],[0,0],[0,0],[0,0],[0,0],[-2.788,2.066],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.491,3.427],[0,0],[0,0],[0,0],[0,0],[0,0],[2.129,2.781],[0,0],[0,0],[0,0],[0,0],[0,0],[3.477,0.49],[0,0]],"v":[[5.911,46.27],[16.055,57.994],[27.227,53.787],[26.876,38.368],[28.446,37.204],[36.077,30.079],[37.343,28.596],[52.932,29.809],[57.812,19.029],[46.494,8.378],[46.775,6.451],[47.079,-3.898],[46.911,-5.833],[58.799,-15.842],[54.524,-26.872],[38.906,-26.514],[37.723,-28.058],[30.493,-35.59],[28.99,-36.842],[30.224,-52.217],[19.305,-57.032],[8.509,-45.877],[6.563,-46.148],[-3.942,-46.439],[-5.901,-46.273],[-16.054,-57.999],[-27.23,-53.79],[-26.863,-38.374],[-28.435,-37.209],[-36.07,-30.081],[-37.336,-28.601],[-52.922,-29.812],[-57.807,-19.032],[-46.505,-8.383],[-46.777,-6.467],[-47.081,3.911],[-46.92,5.839],[-58.798,15.839],[-54.526,26.87],[-38.911,26.511],[-37.728,28.053],[-30.495,35.59],[-28.989,36.842],[-30.22,52.206],[-19.297,57.025],[-8.495,45.872],[-6.546,46.143],[3.949,46.439]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[2.743,0.284],[0,0],[0,0],[0,0],[1.746,2.116],[0,0],[0,0],[0,0],[-0.285,2.703],[0,0],[0,0],[0,0],[-2.133,1.708],[0,0],[0,0],[0,0],[-2.753,-0.281],[0,0],[0,0],[0,0],[-1.74,-2.113],[0,0],[0,0],[0,0],[0.288,-2.71],[0,0],[0,0],[0,0],[2.138,-1.71],[0,0]],"o":[[0,0],[-2.75,0.131],[0,0],[0,0],[0,0],[-2.034,-1.822],[0,0],[0,0],[0,0],[-0.127,-2.719],[0,0],[0,0],[0,0],[1.86,-2.023],[0,0],[0,0],[0,0],[2.743,-0.118],[0,0],[0,0],[0,0],[2.041,1.822],[0,0],[0,0],[0,0],[0.133,2.713],[0,0],[0,0],[0,0],[-1.854,2.013],[0,0],[0,0]],"v":[[13.739,66.951],[2.614,54.093],[-5.647,53.861],[-17.494,66.096],[-38.275,56.926],[-36.928,40.068],[-42.61,34.149],[-59.747,34.542],[-67.876,13.559],[-54.836,2.579],[-54.599,-5.571],[-66.999,-17.253],[-57.706,-37.756],[-40.607,-36.429],[-34.605,-42.035],[-35.01,-58.939],[-13.741,-66.951],[-2.607,-54.095],[5.655,-53.864],[17.498,-66.102],[38.283,-56.936],[36.925,-40.068],[42.608,-34.152],[59.748,-34.548],[67.876,-13.564],[54.837,-2.579],[54.6,5.568],[67.003,17.245],[57.717,37.757],[40.618,36.424],[34.616,42.025],[35.002,58.939]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[68.126,67.202],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":7},{"ty":4,"nm":"Hook Board Outlines","mn":"","sr":1,"st":0,"op":300.00001221925,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[421.317,391.334,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[541.488,520.669,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[7.888,0],[6.667,-1.723],[10.679,-17.999],[-5.36,-20.166],[-18.247,-10.525],[-20.456,5.284],[2.25,36.801],[0,0],[0,0],[0,0],[0,0]],"o":[[-6.724,0],[-20.456,5.281],[-10.679,18.003],[5.345,20.181],[18.241,10.521],[36.855,-9.529],[0,0],[0,0],[0,0],[0,0],[-7.609,-2.347]],"v":[[-1.115,-77.154],[-21.262,-74.573],[-69.546,-38.468],[-77.794,20.718],[-41.208,68.334],[18.797,76.461],[77.79,-3.567],[35.581,29.491],[-16.089,12.144],[-19.215,-41.136],[22.225,-73.627]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[7.379,0],[13.47,7.771],[5.867,22.129],[-11.714,19.744],[-22.434,5.796],[-17.457,-7.007],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[42.858,-11.083]],"o":[[-15.207,0],[-20.012,-11.545],[-5.875,-22.116],[11.713,-19.741],[18.282,-4.716],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[6.411,43.144],[-7.331,1.891]],"v":[[-1.404,86.595],[-45.072,74.851],[-85.201,22.631],[-76.151,-42.282],[-23.201,-81.879],[31.427,-78.387],[37.49,-75.949],[-11.344,-37.659],[-8.747,6.632],[34.094,21.018],[83.701,-17.837],[84.665,-11.352],[20.735,83.768]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[667.891,86.845],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[-14.615,9.966],[0,0],[0,0],[0,0],[-6.382,-12.185]],"o":[[-8.144,-15.549],[0,0],[0,0],[0,0],[-11.305,7.719],[0,0]],"v":[[-59.58,56.762],[-48.227,12.005],[63.677,-56.762],[67.724,-50.355],[-44.029,18.313],[-52.78,53.298]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[522.422,172.096],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 3","ix":3,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-36.781,36.503],[-41.799,30.801],[36.781,-36.503],[41.799,-30.804]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[573.521,186.324],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 4","ix":4,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54.854,3.775],[-54.854,3.775],[-54.854,-3.775],[54.854,-3.775]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[177.572,194.761],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 5","ix":5,"cix":2,"np":10,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-66.337,3.775],[-76.54,3.775],[-76.54,-3.775],[-66.337,-3.775]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-45.924,3.775],[-56.128,3.775],[-56.128,-3.775],[-45.924,-3.775]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 3","ix":3,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-25.517,3.775],[-35.721,3.775],[-35.721,-3.775],[-25.517,-3.775]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 4","ix":4,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-5.105,3.775],[-15.31,3.775],[-15.31,-3.775],[-5.105,-3.775]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 5","ix":5,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[15.306,3.775],[5.102,3.775],[5.102,-3.775],[15.306,-3.775]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 6","ix":6,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[35.717,3.775],[25.513,3.775],[25.513,-3.775],[35.717,-3.775]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 7","ix":7,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.129,3.775],[45.925,3.775],[45.925,-3.775],[56.129,-3.775]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 8","ix":8,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[76.54,3.775],[66.336,3.775],[66.336,-3.775],[76.54,-3.775]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[176.297,141.911],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 6","ix":6,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[5.357,0],[0.183,0.003],[0,11.233],[0,0],[0,0],[0,0],[-6.847,-0.178],[-2.487,2.391],[0,3.42],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[3.977,-3.82]],"o":[[-0.18,0],[-10.957,-0.281],[0,0],[0,0],[0,0],[0,7.179],[3.452,0.081],[2.483,-2.387],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,5.477],[-3.845,3.699]],"v":[[-137.77,132.874],[-138.311,132.868],[-158.185,111.984],[-158.185,-49.822],[-150.531,-49.822],[-150.531,111.984],[-138.113,125.321],[-128.87,121.75],[-125.016,112.74],[-125.016,-95.12],[32.363,-95.12],[69.459,-132.874],[158.185,-132.874],[158.185,76.017],[150.531,76.017],[150.531,-125.323],[72.701,-125.323],[35.606,-87.57],[-117.363,-87.57],[-117.363,112.74],[-123.529,127.156]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[158.436,160.272],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 7","ix":7,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[85.471,3.775],[-85.471,3.775],[-85.471,-3.775],[85.471,-3.775]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[108.685,287.88],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 8","ix":8,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.348,0],[2.613,-1.548],[1.076,-4.241],[-2.281,-3.743],[-4.297,-1.058],[-3.797,2.247],[-1.076,4.238],[2.281,3.743],[4.297,1.058]],"o":[[-2.971,0],[-3.797,2.247],[-1.072,4.239],[2.279,3.748],[4.297,1.061],[3.8,-2.247],[1.072,-4.239],[-2.278,-3.748],[-1.342,-0.331]],"v":[[-0.028,-15.951],[-8.533,-13.61],[-16.087,-3.551],[-14.214,8.829],[-4.016,16.282],[8.533,14.436],[16.09,4.377],[14.217,-8.001],[4.019,-15.454]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[4.341,0],[1.962,0.484],[3.332,5.478],[-1.569,6.192],[-5.55,3.283],[-6.284,-1.551],[-3.329,-5.478],[1.566,-6.193],[5.553,-3.283]],"o":[[-1.971,0],[-6.281,-1.548],[-3.329,-5.474],[1.57,-6.195],[5.549,-3.287],[6.281,1.548],[3.332,5.471],[-1.569,6.195],[-3.816,2.26]],"v":[[0.045,24.33],[-5.873,23.606],[-20.778,12.713],[-23.512,-5.379],[-12.473,-20.082],[5.876,-22.78],[20.778,-11.887],[23.515,6.207],[12.473,20.91]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[481.184,78.579],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 9","ix":9,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-31.873,56.664],[-65.685,1.102],[-33.81,-55.562],[31.873,-56.664],[65.685,-1.102],[47.922,30.475],[41.229,26.814],[56.847,-0.952],[27.586,-49.039],[-29.26,-48.084],[-56.848,0.955],[-27.586,49.039],[8.803,48.421],[8.937,55.971]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[481.245,78.991],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 15","ix":15,"cix":2,"np":5,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[6.705,0],[1.399,-0.184],[4.986,-6.454],[-1.098,-8.04],[-16.849,2.209],[-4.986,6.448],[1.101,8.043],[6.544,4.922]],"o":[[-1.386,0],[-8.15,1.083],[-4.99,6.452],[2.262,16.599],[8.153,-1.087],[4.99,-6.451],[-1.098,-8.043],[-5.423,-4.076]],"v":[[20.902,-40.348],[16.722,-40.07],[-3.651,-28.385],[-9.682,-5.91],[24.962,20.146],[45.338,8.46],[51.369,-14.018],[39.519,-34.121]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.702,0],[2.591,19.013],[-6.227,8.058],[-10.176,1.351],[-8.173,-6.143],[-1.37,-10.041],[6.23,-8.056],[10.179,-1.352]],"o":[[-18.953,0],[-1.374,-10.038],[6.227,-8.056],[10.189,-1.361],[8.17,6.142],[1.377,10.043],[-6.227,8.055],[-1.733,0.231]],"v":[[20.826,27.967],[-17.266,-4.901],[-9.739,-32.963],[15.7,-47.551],[44.161,-40.123],[58.953,-15.029],[51.426,13.039],[25.983,27.627]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 3","ix":3,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[4.265,1.513],[0,0],[0,0],[0,0],[1.911,4.014],[0,0],[0,0],[0,0],[-1.541,4.217],[0,0],[0,0],[0,0],[-4.097,1.907],[0,0],[0,0],[0,0],[0,0],[4.221,-3.099],[0,0],[0,0],[0,0],[0,0],[0,0],[0.763,-5.115],[0,0],[0,0],[0,0],[0,0],[0,0],[-3.171,-4.214],[0,0],[0,0],[0,0],[0,0],[0,0],[-5.211,-0.768],[0,0],[0,0],[0,0],[0,0],[0,0],[-4.202,3.086],[0,0],[0,0],[0,0],[0,0],[4.113,-1.917],[0,0]],"o":[[0,0],[-4.452,-0.771],[0,0],[0,0],[0,0],[-2.623,-3.671],[0,0],[0,0],[0,0],[0.769,-4.376],[0,0],[0,0],[0,0],[3.693,-2.568],[0,0],[0,0],[0,0],[0,0],[-4.838,2.1],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.921,4.872],[0,0],[0,0],[0,0],[0,0],[0,0],[2.098,4.722],[0,0],[0,0],[0,0],[0,0],[0,0],[4.923,1.888],[0,0],[0,0],[0,0],[0,0],[0,0],[4.857,-2.116],[0,0],[0,0],[0,0],[0,0],[-3.679,2.557],[0,0],[0,0]],"v":[[18.687,88.12],[7.363,66.435],[-5.746,63.002],[-26.468,76.319],[-50.986,57.877],[-43.449,34.637],[-50.271,23.073],[-74.47,18.048],[-78.583,-12.089],[-56.602,-23.269],[-53.128,-36.197],[-66.639,-56.643],[-47.945,-80.826],[-24.386,-73.379],[-12.666,-80.111],[-10.954,-88.12],[-3.465,-86.565],[-5.977,-74.793],[-7.774,-74.012],[-21.424,-66.175],[-23.003,-65.014],[-45.133,-72.009],[-57.276,-56.303],[-44.588,-37.096],[-45.3,-35.289],[-49.344,-20.239],[-49.629,-18.319],[-70.274,-7.817],[-67.604,11.756],[-44.87,16.478],[-44.082,18.257],[-36.143,31.722],[-34.973,33.276],[-42.051,55.102],[-26.12,67.084],[-6.657,54.578],[-4.828,55.277],[10.448,59.281],[12.387,59.568],[23.019,79.927],[42.876,77.284],[47.664,54.884],[49.455,54.097],[63.111,46.254],[64.69,45.093],[78.583,49.481],[76.251,56.672],[66.072,53.46],[54.35,60.193],[49.252,84.053]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[695.915,413.697],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 16","ix":16,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[5.018,0],[0,-4.95],[-5.018,0],[0,4.947]],"o":[[-5.018,0],[0,4.947],[5.018,0],[0,-4.95]],"v":[[0,-8.978],[-9.103,0.001],[0,8.978],[9.103,0.001]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[340.659,287.259],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 17","ix":17,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[5.018,0],[0,-4.95],[-5.018,0],[0,4.947]],"o":[[-5.018,0],[0,4.947],[5.018,0],[0,-4.95]],"v":[[0,-8.978],[-9.103,0.001],[0,8.978],[9.103,0.001]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[308.297,287.259],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 18","ix":18,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[5.018,0],[0,-4.95],[-5.018,0],[0,4.947]],"o":[[-5.018,0],[0,4.947],[5.018,0],[0,-4.95]],"v":[[0,-8.978],[-9.103,0.001],[0,8.978],[9.103,0.001]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[275.936,287.259],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 19","ix":19,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[15.757,0],[0,0],[0,-15.54]],"o":[[0,0],[0,0],[0,-15.54],[0,0],[-15.757,0],[0,0]],"v":[[-271.722,27.684],[271.722,27.684],[271.722,0.503],[243.148,-27.685],[-243.147,-27.685],[-271.722,0.503]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[-19.978,0],[0,0],[0,-19.703]],"o":[[0,0],[0,0],[0,-19.703],[0,0],[19.978,0],[0,0]],"v":[[279.376,35.234],[-279.376,35.234],[-279.376,0.503],[-243.147,-35.234],[243.148,-35.234],[279.376,0.503]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[501.596,286.622],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 20","ix":20,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[32.644,0],[0,0],[0,0],[0,0],[0,-36.367]],"o":[[0,0],[0,0],[0,-32.204],[0,0],[0,0],[0,0],[36.865,0],[0,0]],"v":[[109.71,81.793],[102.056,81.793],[102.056,-15.845],[42.853,-74.243],[-109.71,-74.243],[-109.71,-81.793],[42.853,-81.793],[109.71,-15.845]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[712.085,297.948],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 21","ix":21,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[3.827,12.585],[-3.827,12.585],[-3.827,-12.585],[3.827,-12.585]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[817.967,425.041],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 22","ix":22,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[279.378,232.798],[-279.378,232.798],[-279.378,137.161],[-271.72,137.161],[-271.72,225.248],[271.721,225.248],[271.594,-225.248],[-271.72,-225.248],[-271.72,49.077],[-279.378,49.077],[-279.378,-232.798],[279.248,-232.798]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[501.596,547.104],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 23","ix":23,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[14.688,0],[0,0],[0,-14.488],[0,0],[0,0],[0,0]],"o":[[0,0],[-14.688,0],[0,0],[0,0],[0,0],[0,-14.488]],"v":[[248.955,-31.46],[-248.958,-31.46],[-275.549,-5.226],[-275.549,31.46],[275.549,31.46],[275.549,-5.226]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0314,0.4863,0.4863],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[505.425,287.881],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 24","ix":24,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[4.582,12.997],[-10.844,-8.654],[-4.585,-12.997],[10.844,8.656]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[160.94,691.431],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 25","ix":25,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-3.267,6.88],[-7.668,0.701],[3.267,-6.88],[7.668,-0.701]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[124.207,710.553],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 26","ix":26,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-97.601,72.875],[-73.94,29.342],[73.491,-72.875],[77.892,-66.695],[-68.052,34.488],[-83.666,63.215],[-50.975,58.455],[93.2,-41.505],[97.601,-35.325],[-48.061,65.662]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[216.51,641.349],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 27","ix":27,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[-0.265,1.495],[0.885,1.242],[0,0],[2.598,-1.804]],"o":[[0,0],[0,0],[1.259,-0.874],[0.266,-1.495],[0,0],[-1.829,-2.566],[0,0]],"v":[[-15.141,-7.431],[1.939,16.536],[15.285,7.282],[17.648,3.608],[16.686,-0.636],[6.238,-15.302],[-1.792,-16.685]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[-4.255,-5.971],[0,0],[0.617,-3.483],[2.933,-2.032]],"o":[[0,0],[0,0],[6.05,-4.195],[0,0],[2.06,2.89],[-0.617,3.483],[0,0]],"v":[[0.074,27.057],[-25.805,-9.263],[-6.193,-22.862],[12.5,-19.644],[22.948,-4.978],[25.188,4.907],[19.686,13.459]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[313.16,579.575],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 28","ix":28,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-36.903,41.111],[-44.06,38.436],[-32.938,9.483],[-4.867,18.334],[-6.23,-41.111],[44.06,2.725],[38.991,8.381],[1.816,-24.02],[3.031,28.749],[-28.354,18.858]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[798.323,149.066],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 29","ix":29,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-14.94,-13.663],[-4.632,8.969],[12.324,-10.273]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-6.708,22.836],[-27.509,-22.836],[27.508,-15.994]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0314,0.4863,0.4863],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[377.434,187.496],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 30","ix":30,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[6.869,0],[0,-6.773],[-6.869,0],[0,6.776]],"o":[[-6.869,0],[0,6.776],[6.869,0],[0,-6.773]],"v":[[0,-12.287],[-12.454,-0.001],[0,12.287],[12.454,-0.001]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[11.422,0],[0,11.267],[-11.422,0],[0,-11.264]],"o":[[-11.422,0],[0,-11.264],[11.422,0],[0,11.267]],"v":[[0,20.433],[-20.712,-0.001],[0,-20.433],[20.712,-0.001]]},"ix":2}},{"ty":"mm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Merge","nm":"Merge Paths 1","mm":1},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0314,0.4863,0.4863],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[74.24,369.675],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 31","ix":31,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[29.34,3.775],[-29.34,3.775],[-29.34,-3.775],[29.34,-3.775]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[72.966,713.21],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 32","ix":32,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[29.34,3.775],[-29.34,3.775],[-29.34,-3.775],[29.34,-3.775]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[72.966,670.425],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 33","ix":33,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[51.027,3.775],[-51.027,3.775],[-51.027,-3.775],[51.027,-3.775]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[94.652,627.641],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 34","ix":34,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[76.541,41.526],[-76.541,41.526],[-76.541,-41.527],[76.541,-41.527]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0314,0.4863,0.4863],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[120.166,542.071],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 35","ix":35,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[94.4,158.555],[-94.4,158.555],[-94.4,-158.555],[94.4,-158.555],[94.4,-151.005],[-86.747,-151.005],[-86.747,151.004],[94.4,151.004]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[102.306,623.864],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":8}],"ddd":0,"h":1085,"w":1080,"meta":{"a":"","k":"","d":"","g":"@lottiefiles/toolkit-js 0.22.1","tc":"#000000"},"v":"5.5.5","fr":30,"op":105,"ip":30.0000012219251,"assets":[]} \ No newline at end of file diff --git a/assets/lottie/nodatafound.json b/assets/lottie/nodatafound.json new file mode 100644 index 0000000..9917d1d --- /dev/null +++ b/assets/lottie/nodatafound.json @@ -0,0 +1 @@ +{"v":"5.9.0","fr":60,"ip":0,"op":316,"w":642,"h":460,"nm":"NEW sin movs 2","ddd":0,"assets":[{"id":"comp_0","nm":"NEW sin movs","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"escalador papel","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[321,161.5,0],"ix":2,"l":2},"a":{"a":0,"k":[60,60,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":421,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"lupa Outlines 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":34,"s":[64]},{"i":{"x":[0.437],"y":[0.995]},"o":{"x":[0.7],"y":[0]},"t":92,"s":[23]},{"i":{"x":[0.358],"y":[1]},"o":{"x":[0.544],"y":[0]},"t":133,"s":[-21]},{"i":{"x":[0.326],"y":[0.985]},"o":{"x":[0.595],"y":[0]},"t":183,"s":[16]},{"t":220,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.222,"y":1},"o":{"x":0.167,"y":0.1},"t":34,"s":[240.252,377.045,0],"to":[13.917,-18.667,0],"ti":[-5.248,0.545,0]},{"i":{"x":0.453,"y":0.997},"o":{"x":0.785,"y":0},"t":88,"s":[323.752,265.045,0],"to":[5.248,-0.545,0],"ti":[-24.248,-17.455,0]},{"i":{"x":0.156,"y":1},"o":{"x":0.423,"y":0.339},"t":133,"s":[334.252,403.045,0],"to":[24.248,17.455,0],"ti":[-27.5,-2,0]},{"t":183.03515625,"s":[499.252,415.045,0]}],"ix":2,"l":2},"a":{"a":0,"k":[47.206,48.054,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.15,0.15,0.15],"y":[0,0,0]},"t":34,"s":[0,0,100]},{"i":{"x":[0.53,0.53,0.53],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":60,"s":[240,240,100]},{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.42,0.42,0.42],"y":[0,0,0]},"t":133,"s":[240,240,100]},{"t":183,"s":[400,400,100]}],"ix":6,"l":2}},"ao":0,"ip":34,"op":12647,"st":18,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"LUPA rotacion 3D","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[26.162,29.375,0],"ix":2,"l":2},"a":{"a":0,"k":[48.528,49.656,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.25,"y":1},"o":{"x":0.167,"y":0},"t":202,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[3.438,-0.004],[0.027,3.406],[0.01,3.421],[0.056,3.461],[-3.375,0.031],[-3.421,-0.01],[-3.397,-0.021],[0.014,-3.432],[-0.01,-3.421],[0.024,-3.444],[3.454,-0.013],[3.421,0.011]],"c":true}]},{"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[12.204,8.794],[8.792,12.204],[0.01,3.422],[-8.706,12.137],[-12.137,8.707],[-3.421,-0.01],[-12.204,-8.793],[-8.792,-12.204],[-0.01,-3.421],[8.706,-12.137],[12.137,-8.706],[3.421,0.011]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.031372550875,0.486274510622,0.486274510622,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[46.174,44.297],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":202,"s":[0]},{"t":205,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0},"t":35,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"i":{"x":0.765,"y":1},"o":{"x":0.8,"y":0},"t":60,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.21,"y":0},"t":84,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0},"t":110,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.765,"y":1},"o":{"x":0.167,"y":0},"t":126,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.21,"y":0},"t":147,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"t":173,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.031372550875,0.486274510622,0.486274510622,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":35,"s":[5.6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.8],"y":[0]},"t":60,"s":[3.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":84,"s":[5.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":110,"s":[3.6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0]},"t":126,"s":[3.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":147,"s":[5.6]},{"t":173,"s":[3.6]}],"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[45.074,45],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-42.5,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.883,-1.88],[0,0],[1.874,-1.878],[0,0],[1.884,1.881],[0,0],[-1.874,1.877]],"o":[[1.885,-1.879],[0,0],[1.877,1.874],[0,0],[-1.885,1.878],[0,0],[-1.877,-1.875],[0,0]],"v":[[-15.301,-15.277],[-8.483,-15.273],[15.305,8.476],[15.31,15.269],[15.302,15.277],[8.483,15.273],[-15.305,-8.475],[-15.31,-15.268]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.031372550875,0.486274510622,0.486274510622,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[79.622,81.906],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":34,"op":421,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"line 1 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[299.82,224.817,0],"ix":2,"l":2},"a":{"a":0,"k":[36.006,1.8,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.8,1.8],[70.211,1.8]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.031372550875,0.486274510622,0.486274510622,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.15],"y":[0]},"t":17,"s":[0]},{"t":39,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":17,"op":421,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"line 2 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[292.734,278.817,0],"ix":2,"l":2},"a":{"a":0,"k":[32.463,1.8,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.8,1.8],[63.125,1.8]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.031372550875,0.486274510622,0.486274510622,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.15],"y":[0]},"t":24,"s":[0]},{"t":46,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":24,"op":428,"st":7,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"line 3 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[258.464,329.218,0],"ix":2,"l":2},"a":{"a":0,"k":[15.327,1.8,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.8,1.8],[28.855,1.8]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.031372550875,0.486274510622,0.486274510622,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.15],"y":[0]},"t":31,"s":[0]},{"t":53,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":31,"op":435,"st":14,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"line 4 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[258.464,379.618,0],"ix":2,"l":2},"a":{"a":0,"k":[15.327,1.8,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.8,1.8],[28.855,1.8]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.031372550875,0.486274510622,0.486274510622,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.15],"y":[0]},"t":38,"s":[0]},{"t":60,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":38,"op":442,"st":21,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Sombra lupa","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[321,321,0],"ix":2,"l":2},"a":{"a":0,"k":[321,321,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[-7.5,3.5],[0,0],[0,0],[0,0],[0,0],[-4,0],[0,-2.5],[-29.5,0],[0,15],[0,5]],"o":[[-13,0],[0,0],[0,0],[0,0],[0,0],[4,0],[0,2.5],[29.5,0],[0,-15],[0,-5]],"v":[[412.5,169.5],[231.5,169.5],[202,190.5],[191,254],[191,428.5],[329,428.5],[333.5,434.5],[365.5,478.5],[399,436.5],[399,193]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"w":642,"h":642,"ip":0,"op":421,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"papel bot Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-8.861,353.102,0],"ix":2,"l":2},"a":{"a":0,"k":[57.389,18.529,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":1,"k":[{"i":{"x":0.35,"y":1},"o":{"x":0.167,"y":0},"t":25,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[114.817,36.647],[1.381,36.647],[1.32,36.478],[114.756,36.478]],"c":true}]},{"t":45,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[114.756,5.022],[1.32,5.022],[1.32,36.478],[114.756,36.478]],"c":true}]}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0.013,-9.145],[0,0],[0,0],[7.994,-2.105],[1.814,0.473],[-0.022,8.366],[0,0],[1.528,0.009],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-1.541],[0,0],[-13.061,-0.011],[0,0],[0,0],[0,0],[-0.609,0.093],[-1.049,0.261],[-0.013,11.099],[0,0],[0,0],[-5.938,0.542],[-0.345,-0.019],[-0.011,-6.47],[0,0],[0,0],[0,1.541],[0,0],[9.485,0.042]],"o":[[0,0],[-9.048,0.552],[0,0],[0,0],[-0.017,8.335],[-1.815,0.473],[-8.019,-2.135],[0,0],[0.011,-1.541],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.527,0],[0,0],[-0.012,13.175],[0,0],[0,0],[0,0],[0.609,0],[1.076,-0.099],[10.716,-2.516],[0,0],[0,0],[0.079,-6.014],[0.344,-0.019],[6.414,0.01],[0,0],[0,0],[1.528,0],[0,0],[0.04,-9.569],[0,0]],"v":[[89.342,-145.931],[88.309,-145.931],[72.188,-128.68],[72.188,-128.419],[72.188,-6.1],[58.594,11.617],[53.059,11.617],[39.481,-6.211],[39.481,-9.933],[36.734,-12.743],[19.195,-12.743],[5.094,-12.743],[-16.645,-12.743],[-39.406,-12.743],[-53.01,-12.743],[-55.777,-9.951],[-55.777,-6.006],[-32.146,17.869],[-32.111,17.869],[-2.276,17.869],[54.109,17.869],[56.268,17.776],[59.46,17.237],[77.777,-6.006],[77.777,-128.586],[77.777,-128.717],[88.365,-140.254],[89.398,-140.254],[101.02,-128.531],[101.02,-115.262],[103.787,-115.262],[106.555,-118.053],[106.555,-128.531],[89.454,-145.931]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[58.74,18.119],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":25,"op":421,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Papel front Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[42.09,219.148,0],"ix":2,"l":2},"a":{"a":0,"k":[74.219,89.605,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":1,"k":[{"i":{"x":0.99,"y":1},"o":{"x":0.28,"y":0},"t":10,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[152.276,7.781],[-1.326,7.781],[-1.326,9.031],[152.276,9.031]],"c":true}]},{"t":25,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[152.276,7.781],[-1.326,7.781],[-1.326,175.031],[152.276,175.031]],"c":true}]}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0.012,-9.145],[0,0],[0,0],[7.994,-2.106],[0.894,-0.009],[0,0],[0,0],[0,0],[0,0],[-5.953,-0.005],[0,0],[0,0],[0,0],[-0.61,0.093],[-1.049,0.26],[-0.014,11.099],[0,0],[0,0],[-5.937,0.544],[-0.326,-0.016]],"o":[[0,0],[-9.048,0.551],[0,0],[0,0],[-0.017,8.335],[-0.874,0.227],[0,0],[0,0],[0,0],[0,0],[4.154,3.667],[0,0],[0,0],[0,0],[0.608,0],[1.075,-0.099],[10.716,-2.517],[0,0],[0,0],[0.079,-6.015],[0.328,-0.018],[0,0]],"v":[[68.364,-81.9],[67.331,-81.9],[51.208,-64.649],[51.208,-64.388],[51.208,57.931],[37.614,75.648],[34.951,75.996],[34.951,75.999],[34.882,75.999],[34.811,75.999],[-68.667,75.999],[-53.126,81.9],[-53.089,81.9],[-23.254,81.9],[33.132,81.9],[35.29,81.807],[38.48,81.268],[56.799,58.024],[56.799,-64.556],[56.799,-64.686],[67.385,-76.224],[68.667,-76.224]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[71.074,92.141],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-57.614,-15.262],[-57.614,-11.587]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,3.997],[0,8.289]],"o":[[5.953,0],[0,-32.687],[0,0]],"v":[[-65.219,79.059],[-58.189,68.737],[-58.135,4.655]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-13.063,0.021],[0,0]],"o":[[0,0],[0.021,-13.171],[0,0],[0,0]],"v":[[-58.135,-24.607],[-58.135,-55.195],[-34.467,-79.059],[65.219,-79.059]],"c":false},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[74.219,91.151],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.02,-13.17],[0,0],[0,0]],"o":[[-13.063,0.021],[0,0],[0,0],[0,0]],"v":[[-6.133,-52.006],[-29.801,-28.142],[-29.801,52.08],[29.801,-52.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176474094,0.941176474094,0.941176474094,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[45.83,62.33],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0.02,-13.17],[0,0],[-1.527,0],[0,0],[-0.009,13.153],[0,0],[0,0],[-6.363,0.009],[-0.025,0.001]],"o":[[0,0],[0,0],[-13.062,0.02],[0,0],[0,1.54],[0,0],[13.045,-0.041],[0,0],[0,0],[0.081,-6.417],[0.026,0],[0,0]],"v":[[61.639,-76.59],[61.639,-81.84],[-38.048,-81.84],[-61.714,-57.977],[-61.714,79.05],[-58.947,81.84],[26.406,81.84],[50.018,57.977],[50.018,-64.542],[50.018,-64.673],[61.639,-76.581],[61.714,-76.59]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[77.799,92.164],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":10,"op":421,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Papel top Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[89.026,94.609,0],"ix":2,"l":2},"a":{"a":0,"k":[69.157,27.261,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[133.251,43.755],[8.394,43.755],[8.394,45.207],[133.251,45.207]],"c":true}]},{"t":10,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[133.324,9.505],[8.466,9.505],[8.394,45.207],[133.251,45.207]],"c":true}]}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.544],[-1.545,0],[-0.018,-0.001],[-0.01,-6.471],[0,0],[0,0],[0,1.541],[0,0],[9.485,0.041],[0,0],[0,0],[0.251,-0.027],[0,0]],"o":[[0,1.544],[97.877,0],[6.414,0.011],[0,0],[0,0],[1.527,0],[0,0],[0.041,-9.569],[0,0],[0,0],[-0.254,0.015],[0,0],[-1.544,0]],"v":[[-58.955,-12.455],[-56.155,-9.658],[41.758,-9.658],[53.379,2.066],[53.379,15.334],[56.147,15.334],[58.914,12.543],[58.914,2.066],[41.814,-15.334],[41.703,-15.334],[40.67,-15.334],[39.918,-15.251],[-56.159,-15.251]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[69.205,25.502],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.67,0],[0,-9.44],[0,0],[-1.527,0],[0,0],[0,1.632],[0,0],[9.475,0]],"o":[[16,0],[0,0],[0,1.632],[0,0],[1.528,0],[0,0],[0,-10.128],[-0.642,0]],"v":[[-58.571,-15.544],[-40.571,3.353],[-40.571,13.652],[-37.803,16.61],[56.472,15.79],[59.24,12.833],[59.24,1.73],[42.083,-16.61]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[68.824,26.86],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":421,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"circulito Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[516.83,232.086,0],"ix":2,"l":2},"a":{"a":0,"k":[7.194,7.198,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.434,0.434,0.67],"y":[1,1,1]},"o":{"x":[0.233,0.233,0.33],"y":[0,0,0]},"t":232,"s":[0,0,100]},{"i":{"x":[0.196,0.196,0.67],"y":[1,1,1]},"o":{"x":[0.172,0.172,0.33],"y":[0,0,0]},"t":252,"s":[240,240,100]},{"t":284,"s":[200,200,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.643,0],[0,1.652],[1.642,0],[0,-1.652]],"o":[[1.642,0],[0,-1.652],[-1.643,0],[0,1.652]],"v":[[0,2.988],[2.976,0],[0,-2.988],[-2.977,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[3.837,0],[0,3.836],[-3.837,0],[0,-3.836]],"o":[[-3.837,0],[0,-3.836],[3.837,0],[0,3.836]],"v":[[0,6.948],[-6.944,0],[0,-6.948],[6.944,0]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[7.194,7.198],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":232,"op":566,"st":145,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"x 2 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[96.613,416.097,0],"ix":2,"l":2},"a":{"a":0,"k":[6.515,6.515,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.434,0.434,0.67],"y":[1,1,1]},"o":{"x":[0.233,0.233,0.33],"y":[0,0,0]},"t":252,"s":[0,0,100]},{"i":{"x":[0.196,0.196,0.67],"y":[1,1,1]},"o":{"x":[0.172,0.172,0.33],"y":[0,0,0]},"t":272,"s":[240,240,100]},{"t":304,"s":[200,200,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[6.258,3.885],[3.885,6.258],[0.001,2.372],[-3.891,6.264],[-6.264,3.891],[-2.374,-0.001],[-6.258,-3.885],[-3.885,-6.26],[-0.001,-2.374],[3.891,-6.264],[6.264,-3.891],[2.374,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.901960790157,0.901960790157,0.901960790157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.515,6.515],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":252,"op":441,"st":20,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"bg Outlines","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":7,"s":[0]},{"t":49,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[318.455,325.042,0],"ix":2,"l":2},"a":{"a":0,"k":[119.513,102.602,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-34.357,-7.157],[-2.441,47.062],[17.957,18.605],[34.584,38.554],[38.389,-25.44],[-24.266,-40.899]],"o":[[79.337,8.029],[3.66,-70.593],[-17.958,-18.605],[-23.057,-25.703],[-30.965,25.202],[24.266,40.898]],"v":[[-7.063,94.323],[115.603,35.774],[39.645,-45.798],[7.22,-76.517],[-84.949,-76.912],[-94.997,22.24]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.898039221764,0.949019610882,0.949019610882,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[119.513,102.602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":421,"st":0,"bm":0}]},{"id":"comp_1","nm":"Sombra lupa","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"parent sombra","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":34,"s":[64]},{"i":{"x":[0.437],"y":[0.995]},"o":{"x":[0.7],"y":[0]},"t":92,"s":[23]},{"i":{"x":[0.358],"y":[1]},"o":{"x":[0.544],"y":[0]},"t":133,"s":[-21]},{"i":{"x":[0.326],"y":[0.985]},"o":{"x":[0.595],"y":[0]},"t":183,"s":[16]},{"t":220,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.222,"y":1},"o":{"x":0.167,"y":0.1},"t":34,"s":[240.252,377.045,0],"to":[13.917,-18.667,0],"ti":[-5.248,0.545,0]},{"i":{"x":0.453,"y":0.997},"o":{"x":0.785,"y":0},"t":88,"s":[323.752,265.045,0],"to":[5.248,-0.545,0],"ti":[-24.248,-17.455,0]},{"i":{"x":0.156,"y":1},"o":{"x":0.423,"y":0.339},"t":133,"s":[334.252,403.045,0],"to":[24.248,17.455,0],"ti":[-27.5,-2,0]},{"t":183.03515625,"s":[499.252,415.045,0]}],"ix":2,"l":2},"a":{"a":0,"k":[47.206,48.054,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.15,0.15,0.15],"y":[0,0,0]},"t":34,"s":[0,0,100]},{"i":{"x":[0.53,0.53,0.53],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":60,"s":[240,240,100]},{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.42,0.42,0.42],"y":[0,0,0]},"t":133,"s":[240,240,100]},{"t":183,"s":[400,400,100]}],"ix":6,"l":2}},"ao":0,"ip":34,"op":12647,"st":18,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"LUPA sombra","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[26.162,29.375,0],"ix":2,"l":2},"a":{"a":1,"k":[{"i":{"x":0.67,"y":1},"o":{"x":0.33,"y":0},"t":133,"s":[59.528,35.656,0],"to":[-0.683,1.317,0],"ti":[0.683,-1.317,0]},{"t":183,"s":[55.428,43.556,0]}],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0},"t":35,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"i":{"x":0.765,"y":1},"o":{"x":0.8,"y":0},"t":60,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.21,"y":0},"t":84,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0},"t":110,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.765,"y":1},"o":{"x":0.167,"y":0},"t":126,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.21,"y":0},"t":147,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"t":173,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.898039221764,0.949019610882,0.949019610882,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":35,"s":[5.6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.8],"y":[0]},"t":60,"s":[3.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":84,"s":[5.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":110,"s":[3.6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0]},"t":126,"s":[3.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":147,"s":[5.6]},{"t":173,"s":[3.6]}],"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.898039221764,0.949019610882,0.949019610882,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[45.074,45],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-42.5,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.883,-1.88],[0,0],[1.874,-1.878],[0,0],[1.884,1.881],[0,0],[-1.874,1.877]],"o":[[1.885,-1.879],[0,0],[1.877,1.874],[0,0],[-1.885,1.878],[0,0],[-1.877,-1.875],[0,0]],"v":[[-15.301,-15.277],[-8.483,-15.273],[15.305,8.476],[15.31,15.269],[15.302,15.277],[8.483,15.273],[-15.305,-8.475],[-15.31,-15.268]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.898039221764,0.949019610882,0.949019610882,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[79.622,81.906],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":34,"op":421,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"NEW sin movs","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[321,230,0],"ix":2,"l":2},"a":{"a":0,"k":[321,321,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":642,"h":642,"ip":0,"op":316,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/assets/lottie/onbo_a.json b/assets/lottie/onbo_a.json new file mode 100644 index 0000000..60f8db7 --- /dev/null +++ b/assets/lottie/onbo_a.json @@ -0,0 +1 @@ +{"v":"5.9.0","fr":60,"ip":0,"op":120,"w":350,"h":300,"nm":"onbo_a","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"surface1710","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40.5,51.25,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[133.33,133.47,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.3,5.67],[-5.65,0.6],[-5.2,4.74],[-6.71,-2.13],[-1.51,-6.88],[-3.99,-1.6],[-0.64,-4.26],[0,-5.82],[6.05,-0.07]],"o":[[-5.68,-0.01],[-0.31,-5.67],[-1.51,-6.88],[5.2,-4.74],[6.71,2.14],[3.4,-2.64],[3.99,1.6],[5.81,0.34],[-0.07,6.05],[0,0]],"v":[[40.65,57.81],[29.99,47.69],[39.51,36.5],[45.48,17.72],[64.73,13.5],[78.01,28.06],[89.94,26.38],[97.41,35.83],[107.77,46.81],[96.77,57.81]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-18.82,-7.88],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.99,-1.58]],"o":[[-0.08,1.86],[0,0]],"v":[[1041.93,371.58],[1043.32,376.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-652.75,-232.82],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.75,-2.15]],"o":[[1.97,1.15],[0,0]],"v":[[1127.29,319.6],[1131.5,324.7]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-706.23,-200.26],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.72,-2.42]],"o":[[0.08,2.98],[0,0]],"v":[[1310.6,375.17],[1308.06,383.49]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-819.47,-235.07],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[13.41,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[37.76,35.59],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[5.85,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[28.48,35.59],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,6.03],[-7.06,0],[-0.22,-0.02],[-5.15,0],[0,0],[-1.4,-0.84],[-6.44,0.02],[0.1,-9.57],[0.02,-0.35],[-2.11,-2.1],[0.03,-2.98],[2.15,-2.07],[2.98,0.09],[0,0]],"o":[[0,0],[-6.72,-0.33],[0,-6.03],[0.23,0],[0.1,-5.46],[0,0],[1.63,0],[2.9,-5.75],[9.57,0.09],[0,0.36],[2.98,-0.04],[2.11,2.1],[-0.02,2.98],[-2.15,2.07],[0,0],[0,0]],"v":[[972.34,303.03],[972.33,303.03],[960.28,291.74],[973.06,280.43],[973.74,280.45],[983.17,270.6],[983.17,270.59],[987.79,271.88],[1003,262.47],[1020.15,279.97],[1020.11,281.04],[1028.08,284.27],[1031.35,292.22],[1027.95,300.12],[1019.93,303.21],[1019.7,303.21]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-601.6,-164.47],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[19.52,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[84.93,123.2],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[18.34,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[61.34,123.2],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[18.35,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[61.35,149.58],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[18.35,0],[18.35,24.46],[0,24.46]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[61.35,142.28],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[18.34,0],[18.34,24.46],[0,24.46]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[61.34,115.76],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[19.52,0],[19.52,24.46],[0,24.46]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[84.93,115.76],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.25,-0.24],[0,-0.35],[0,0],[0,0],[0,0],[-0.24,0.25],[-0.35,0]],"o":[[0,0],[0.35,0],[0.24,0.25],[0,0],[0,0],[0,0],[0,-0.35],[0.25,-0.24],[0,0]],"v":[[689.1,1155.98],[705.99,1155.98],[706.92,1156.36],[707.31,1157.3],[707.31,1199.54],[687.78,1199.54],[687.78,1157.3],[688.17,1156.36],[689.1,1155.98]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-430.9,-724.2],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0.24,0.24],[0.35,0],[0,0],[0,-0.73]],"o":[[0,0],[0,0],[0,-0.35],[-0.25,-0.25],[0,0],[-0.73,0],[0,0]],"v":[[806.29,684.88],[818.18,693.81],[818.18,672.68],[817.79,671.75],[816.86,671.36],[807.61,671.36],[806.29,672.68]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-505.14,-420.62],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0.47,-0.35],[0,0],[0.45,0.22],[0,0.49],[0,0],[-0.33,0.25],[0,0],[-0.47,-0.35],[0,0],[0,-0.41],[0,0],[0.44,-0.22],[0.4,0.3]],"o":[[0,0],[-0.47,-0.35],[0,0],[-0.4,0.3],[-0.44,-0.22],[0,0],[0.01,-0.41],[0,0],[0.47,-0.35],[0,0],[0.33,0.25],[0,0],[0,0.49],[-0.45,0.22],[0,0]],"v":[[452.14,687.45],[406.23,653.05],[404.65,653.05],[358.73,687.45],[357.35,687.58],[356.63,686.4],[356.63,677.55],[357.16,676.5],[404.65,640.91],[406.22,640.91],[453.71,676.5],[454.25,677.55],[454.25,686.4],[453.52,687.58],[452.14,687.45]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-223.45,-401.37],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-0.22,0.48],[0.34,0.4],[0,0]],"o":[[0,0],[0,0],[0,0],[0.53,0],[0.22,-0.48],[0,0],[0,0]],"v":[[944.04,1004.83],[903.34,1004.83],[903.34,1024.06],[957.49,1024.06],[958.72,1023.27],[958.53,1021.81],[944.05,1004.83]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-565.94,-629.52],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[483.11,744.41],[441.67,712.36],[400.23,744.41],[400.23,829],[483.11,829]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-250.77,-446.3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.02,-6.89],[-0.04,-0.46],[0,0],[0,0]],"o":[[0,0],[-6.29,2.81],[0,0.47],[0,0],[0,0],[0,0]],"v":[[954.76,1121.55],[954.76,1153.41],[944.45,1169.36],[944.5,1170.78],[903.34,1170.78],[903.34,1121.55]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-565.94,-702.63],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.24,-0.25],[0,-0.35],[0,0],[0,0],[0,0],[-0.25,0.24],[-0.35,0]],"o":[[0,0],[0.35,0],[0.24,0.24],[0,0],[0,0],[0,0],[0,-0.35],[0.24,-0.25],[0,0]],"v":[[968.16,1236.93],[996.17,1236.93],[997.1,1237.32],[997.49,1238.25],[997.49,1267.16],[966.84,1267.16],[966.84,1238.25],[967.23,1237.32],[968.16,1236.93]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-605.72,-774.91],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.06,1.91],[1.47,1.21],[1.89,-0.32],[0.99,-1.62]],"o":[[1.4,-1.3],[-0.06,-1.91],[-1.48,-1.21],[-1.88,0.32],[0,0]],"v":[[1363.29,1362.41],[1365.41,1357.37],[1363,1352.46],[1357.72,1351.06],[1353.19,1354.12]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-847.74,-846.35],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0.47],[0,0],[-6.29,2.81],[-2.38,-0.01],[-3.22,-3.27],[0.03,-4.59],[0.04,-0.47],[0,0]],"o":[[0,0],[-0.04,-0.47],[0,0],[-0.02,-6.89],[2.17,-0.96],[4.59,0.03],[3.22,3.27],[0,0.48],[0,0],[0,0]],"v":[[1163.19,1324.93],[1152.94,1324.93],[1152.88,1323.51],[1152.88,1323.5],[1163.19,1307.56],[1170.08,1306.1],[1182.28,1311.24],[1187.28,1323.5],[1187.22,1324.92],[1163.19,1324.92]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-722.26,-818.24],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[3.2,7.68],[0.28,0],[0.11,-0.26],[0,-8.83],[0,0],[-3.39,-3.41],[-4.8,0],[-3.39,3.41],[0.03,4.8]],"o":[[0,-8.83],[-0.1,-0.26],[-0.28,0],[-3.2,7.68],[0,0],[-0.04,4.8],[3.38,3.41],[4.8,0],[3.38,-3.41],[0,0]],"v":[[1400.88,861.21],[1383.5,810.12],[1382.86,809.7],[1382.23,810.12],[1364.85,861.21],[1364.85,861.21],[1370.08,874.03],[1382.86,879.35],[1395.65,874.03],[1400.88,861.21]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-855.05,-507.28],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[11.59,0],[0,11.59],[-11.6,0],[0,-11.6]],"o":[[0,11.59],[-11.6,0],[0,-11.6],[11.59,0],[0,0]],"v":[[41.99,21],[21,41.99],[0,21],[21,0],[41.99,21]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[7.69,121.26],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[11.27,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[134.11,62.94],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[15.7,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[125.08,167.62],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[15.7,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[125.08,160.57],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[10.17,0],[0,10.17]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[182.1,137.04],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[9.72,10]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[174.81,131.07],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":29,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,59.18]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[182.1,131.07],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":30,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[8.33,8.49]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[17.19,139.08],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":31,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[9.58,0],[0,9.57]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[23.44,133.78],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":32,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,55.57]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[23.44,133.78],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":33,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[265,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[1.5,175.45],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":34,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"surface1710","np":34,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.27],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"t":66,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"undefined","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":10,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[175.25,146.125,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-8.25],[8.24,0],[0,0],[0,-8.25],[0,0],[-8.24,0],[0,0],[-0.28,-8.21],[8.24,-0.16],[0,0],[3.1,-2.83],[36.03,39.26],[4.32,12.63],[5.9,-0.13],[0,0],[0.27,8.22],[-2.87,2.86],[-3.91,0.11],[0,0],[-1.55,0],[0,0],[0,8.24],[0,0],[6.39,1.69],[0,0],[1.26,0.34],[-2.12,7.96],[-6.76,-0.01],[0,0],[-2.55,2.24],[-35.23,-40],[-4.06,-12.68],[-3.36,-0.01],[0,0]],"o":[[0,8.25],[0,0],[-8.24,0],[0,0],[0,8.24],[0,0],[8.21,-0.06],[0.16,8.25],[0,0],[-4.2,0],[-39.26,36.04],[-9.02,-9.83],[-1.97,-5.56],[0,0],[-8.23,0.07],[-0.08,-4.05],[2.65,-2.88],[0,0],[3.36,0],[0,0],[8.25,0],[0,0],[0.01,-6.92],[0,0],[-1.3,0],[-7.97,-2.12],[1.74,-6.55],[0,0],[3.39,0.04],[40,-35.22],[8.8,9.99],[1,3.21],[0,0],[8.24,0]],"v":[[137.146,-9.037],[122.216,5.893],[79.226,5.893],[64.296,20.823],[64.296,21.903],[79.226,36.823],[91.846,36.823],[107.106,51.463],[92.466,66.683],[77.346,66.683],[65.996,71.083],[-70.344,65.233],[-90.564,31.183],[-103.754,22.083],[-121.874,22.083],[-137.144,7.423],[-132.774,-3.397],[-122.514,-8.077],[-103.874,-8.537],[-95.324,-8.697],[-81.284,-8.697],[-66.354,-23.627],[-66.354,-24.707],[-77.454,-39.137],[-90.734,-39.137],[-94.584,-39.637],[-105.174,-57.897],[-90.744,-68.987],[-72.224,-68.987],[-63.004,-72.407],[73.206,-63.757],[92.706,-29.367],[100.036,-23.967],[122.216,-23.967]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2.627,42.709],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 35","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[8.149,-0.067],[0,0],[-0.068,8.245],[-8.149,0.067],[0,0],[0.068,-8.245]],"o":[[0,0],[-8.245,-0.068],[0.067,-8.149],[0,0],[8.245,0.068],[-0.067,8.149]],"v":[[6.475,14.929],[-6.475,14.929],[-21.281,-0.122],[-6.475,-14.928],[6.475,-14.928],[21.281,0.123]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[143.91,94.466],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 36","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[8.15,-0.067],[0,0],[-0.068,8.245],[-8.15,0.067],[0,0],[0.067,-8.244]],"o":[[0,0],[-8.245,-0.067],[0.066,-8.149],[0,0],[8.245,0.067],[-0.067,8.15]],"v":[[9.173,14.929],[-9.173,14.929],[-23.979,-0.122],[-9.173,-14.928],[9.173,-14.928],[23.98,0.122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-141.711,-11.357],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 37","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/assets/lottie/onbo_b.json b/assets/lottie/onbo_b.json new file mode 100644 index 0000000..7f05c3e --- /dev/null +++ b/assets/lottie/onbo_b.json @@ -0,0 +1 @@ +{"v":"5.9.0","fr":60,"ip":0,"op":120,"w":350,"h":300,"nm":"onbo_b","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"surface2150","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40.5,7,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[133.33,133.49,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.65,-1.18]],"o":[[1.04,0.85],[0,0]],"v":[[1252.99,102.71],[1255.54,105.79]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-788.62,-64.65],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.86,-1.46]],"o":[[-0.18,1.69],[0,0]],"v":[[469.64,326.14],[468.05,330.92]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-294.59,-205.28],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.38,-1.92]],"o":[[-2.17,0.93],[0,0]],"v":[[340.5,253.28],[335.08,257.65]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-210.9,-159.42],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.16,-2.79]],"o":[[0,0],[0,0]],"v":[[108.12,322.13],[110.34,332.31]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-68.06,-202.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,5.87],[-5.86,0],[-0.2,-0.02],[0,0],[-3.76,1.74],[-3.16,-2.68],[-6.44,1.98],[-5.06,-4.44],[1.12,-6.64],[0.23,-5.69],[5.7,0]],"o":[[-5.86,0],[0,-5.87],[0.21,0],[0,0],[-0.01,-4.14],[3.76,-1.74],[1.7,-6.52],[6.44,-1.97],[5.06,4.44],[5.68,0.47],[-0.24,5.69],[0,0]],"v":[[1084.8,56.35],[1074.18,45.73],[1084.8,35.1],[1085.42,35.12],[1085.43,35.1],[1091.59,25.45],[1102.93,27],[1116.03,13.34],[1134.53,17.31],[1140.88,35.14],[1150.61,46.17],[1139.99,56.35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-676.08,-7.88],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.5,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[13.55,60.93],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,8.15],[-8.08,0.56],[0,0.23],[0,0],[-5.17,4.22],[-6.54,-1.33],[-3.11,-5.9],[-3.57,-2.24],[-0.01,-4.21],[0.02,-0.23],[-0.48,0],[0,-7.97],[8.64,0]],"o":[[-8.64,0],[0,-8.15],[-0.01,-0.23],[0,0],[0,-6.67],[5.17,-4.23],[6.55,1.32],[3.8,-1.83],[3.57,2.24],[0,0.23],[0.47,-0.04],[8.64,0],[0,7.97],[0,0]],"v":[[33.85,231.91],[18.21,217.17],[32.67,202.47],[32.66,201.79],[32.66,201.78],[40.82,184.56],[59.32,179.98],[74.59,191.39],[86.42,192.05],[92.16,202.42],[92.14,203.11],[93.57,203.05],[109.2,217.49],[93.57,231.92]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-11.47,-113],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[14.67,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[118.77,163.84],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[14.67,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[97.53,192.6],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[14.67,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[76.84,192.62],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[14.67,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[97.53,163.84],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[14.67,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[76.84,163.86],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[14.67,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[118.77,135.1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[14.67,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[97.53,135.1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[14.67,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[76.84,135.12],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[14.67,0],[14.67,26.89],[0,26.89]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[118.77,156.44],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.24,-0.24],[0,-0.34],[0,0],[0,0],[0,0],[-0.24,0.24],[-0.34,0]],"o":[[0,0],[0.34,-0.01],[0.24,0.24],[0,0],[0,0],[0,0],[0,-0.34],[0.24,-0.24],[0,0]],"v":[[968.86,1537.25],[987.14,1537.25],[988.05,1537.63],[988.43,1538.53],[988.43,1573.71],[967.57,1573.71],[967.57,1538.53],[967.95,1537.63],[968.86,1537.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-608.98,-967.53],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[14.67,0],[14.67,26.9],[0,26.9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[97.55,184.98],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[14.67,0],[14.67,26.9],[0,26.9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[76.84,184.98],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[14.67,0],[14.67,26.89],[0,26.89]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[97.55,156.44],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[14.67,0],[14.67,26.89],[0,26.89]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[76.84,156.44],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[14.67,0],[14.67,26.9],[0,26.9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[118.77,127.91],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[14.67,0],[14.67,26.9],[0,26.9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[97.55,127.91],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[14.67,0],[14.67,26.9],[0,26.9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[76.84,127.91],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,13.95]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[68.1,101.53],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,14.1]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[139.94,101.42],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[107.74,0],[107.74,10.07],[0,10.07]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[63.62,93.87],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[107.74,0],[107.74,10.07],[0,10.07]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[63.62,112],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,0],[95.8,0],[95.8,124.27],[0,124.27]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[68.1,119.55],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":29,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[6.79,-2.81],[5.2,5.2],[-2.81,6.79],[-7.35,0],[-0.46,-0.04],[0,0],[0,-9.49]],"o":[[0,7.35],[-6.79,2.81],[-5.2,-5.2],[2.81,-6.79],[0.47,0],[0,0],[9.46,0.74],[0,0]],"v":[[48.86,1398.46],[37.64,1415.25],[17.82,1411.31],[13.89,1391.5],[30.68,1380.28],[32.09,1380.33],[32.09,1380.33],[48.86,1398.46]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-7.88,-868.73],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":30,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[7.71,-5.14],[8.55,3.57],[3.14,3.93],[5.02,0.4],[-4.72,6.16],[-7.73,0.68],[-5.72,-5.24],[0,-7.77]],"o":[[-0.01,9.27],[-7.72,5.14],[0.72,-4.98],[-3.14,-3.93],[-1.34,-7.65],[4.73,-6.16],[7.73,-0.67],[5.72,5.24],[0,0]],"v":[[187.13,1206.3],[174.78,1229.35],[148.74,1231.85],[144.95,1217.88],[132.17,1211.1],[137.47,1189.44],[157.03,1178.72],[178.14,1185.89],[187.13,1206.3]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-82.93,-741.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":31,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[15.29,0],[0,15.29],[-15.29,0],[0,-15.29]],"o":[[0,15.29],[-15.29,0],[0,-15.29],[15.29,0],[0,0]],"v":[[55.38,27.69],[27.69,55.38],[0,27.69],[27.69,0],[55.38,27.69]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[148.31,142.63],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":32,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[17.98,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[145.02,34.39],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":33,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[14.17,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.66,60.54],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":34,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[6.53,5.79]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[9.99,183.01],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":35,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[7.46,0],[0,7.46]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[14.94,178.07],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":36,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[10.45,10.45]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[28.8,166.46],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":37,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[11,0],[0,11]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[36.73,159.52],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":38,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,45.95]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[14.89,178.29],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":39,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,71.49]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[36.65,159.14],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":40,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[11.22,11.22]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[160.66,166.53],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":41,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[11.36,0],[0,11.37]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[169.24,159.14],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":42,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,71.49]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[169.07,159.14],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":43,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[256.32,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[8.01,212.76],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":44,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"surface2150","np":44,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.27],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"t":66,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"undefined","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":10,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[175.25,146.125,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-8.25],[8.24,0],[0,0],[0,-8.25],[0,0],[-8.24,0],[0,0],[-0.28,-8.21],[8.24,-0.16],[0,0],[3.1,-2.83],[36.03,39.26],[4.32,12.63],[5.9,-0.13],[0,0],[0.27,8.22],[-2.87,2.86],[-3.91,0.11],[0,0],[-1.55,0],[0,0],[0,8.24],[0,0],[6.39,1.69],[0,0],[1.26,0.34],[-2.12,7.96],[-6.76,-0.01],[0,0],[-2.55,2.24],[-35.23,-40],[-4.06,-12.68],[-3.36,-0.01],[0,0]],"o":[[0,8.25],[0,0],[-8.24,0],[0,0],[0,8.24],[0,0],[8.21,-0.06],[0.16,8.25],[0,0],[-4.2,0],[-39.26,36.04],[-9.02,-9.83],[-1.97,-5.56],[0,0],[-8.23,0.07],[-0.08,-4.05],[2.65,-2.88],[0,0],[3.36,0],[0,0],[8.25,0],[0,0],[0.01,-6.92],[0,0],[-1.3,0],[-7.97,-2.12],[1.74,-6.55],[0,0],[3.39,0.04],[40,-35.22],[8.8,9.99],[1,3.21],[0,0],[8.24,0]],"v":[[137.146,-9.037],[122.216,5.893],[79.226,5.893],[64.296,20.823],[64.296,21.903],[79.226,36.823],[91.846,36.823],[107.106,51.463],[92.466,66.683],[77.346,66.683],[65.996,71.083],[-70.344,65.233],[-90.564,31.183],[-103.754,22.083],[-121.874,22.083],[-137.144,7.423],[-132.774,-3.397],[-122.514,-8.077],[-103.874,-8.537],[-95.324,-8.697],[-81.284,-8.697],[-66.354,-23.627],[-66.354,-24.707],[-77.454,-39.137],[-90.734,-39.137],[-94.584,-39.637],[-105.174,-57.897],[-90.744,-68.987],[-72.224,-68.987],[-63.004,-72.407],[73.206,-63.757],[92.706,-29.367],[100.036,-23.967],[122.216,-23.967]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2.627,42.709],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 35","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[8.149,-0.067],[0,0],[-0.068,8.245],[-8.149,0.067],[0,0],[0.068,-8.245]],"o":[[0,0],[-8.245,-0.068],[0.067,-8.149],[0,0],[8.245,0.068],[-0.067,8.149]],"v":[[6.475,14.929],[-6.475,14.929],[-21.281,-0.122],[-6.475,-14.928],[6.475,-14.928],[21.281,0.123]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[143.91,94.466],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 36","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[8.15,-0.067],[0,0],[-0.068,8.245],[-8.15,0.067],[0,0],[0.067,-8.244]],"o":[[0,0],[-8.245,-0.067],[0.066,-8.149],[0,0],[8.245,0.067],[-0.067,8.15]],"v":[[9.173,14.929],[-9.173,14.929],[-23.979,-0.122],[-9.173,-14.928],[9.173,-14.928],[23.98,0.122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-141.711,-11.357],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 37","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/assets/lottie/onbo_c.json b/assets/lottie/onbo_c.json new file mode 100644 index 0000000..92e7d0d --- /dev/null +++ b/assets/lottie/onbo_c.json @@ -0,0 +1 @@ +{"v":"5.9.0","fr":60,"ip":0,"op":120,"w":350,"h":300,"nm":"onbo_c","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"surface14015","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40.5,7.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[133.33,133.27,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.54,-1.54],[2.18,0],[0,0],[0,0],[1.55,1.49],[0,2.16],[-1.55,1.5],[-2.16,-0.08],[-1.06,-0.46],[-5.54,-0.28],[-0.83,-5.49],[-3.06,-1.28],[0,-3.32]],"o":[[0,2.18],[-1.54,1.54],[0,0],[0,0],[-2.16,0.08],[-1.55,-1.5],[0,-2.15],[1.55,-1.49],[1.16,-0.01],[1.36,-5.38],[5.54,0.28],[2.35,-2.34],[3.06,1.27],[0,0]],"v":[[1381.52,217.99],[1379.12,223.8],[1373.31,226.21],[1341.33,226.21],[1341.34,226.21],[1335.54,223.99],[1333.11,218.27],[1335.54,212.56],[1341.34,210.34],[1344.7,211.03],[1356.57,202.26],[1367.52,212.17],[1376.47,210.42],[1381.52,217.99]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-847.39,-128.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.2,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[7.62,76.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[4.62,0],[0,0],[0,4.61],[-4.62,0],[-0.15,-0.01],[-2.26,3.76],[-4.3,0.87],[-3.54,-2.59],[-0.48,-4.36],[-3.54,-1.36],[-0.05,-3.79],[-0.15,0],[-1.57,-1.57],[0,-2.22]],"o":[[0,4.61],[0,0],[-4.62,0],[0,-4.62],[0.15,0],[-1.25,-4.2],[2.26,-3.76],[4.3,-0.86],[3.54,2.58],[2.57,-2.78],[3.54,1.35],[0.15,-0.01],[2.21,0],[1.57,1.56],[0,0]],"v":[[76.65,453.74],[68.29,462.09],[20.86,462.09],[12.5,453.74],[20.86,445.38],[21.3,445.39],[22.89,432.89],[33.18,425.63],[45.48,428.33],[51.8,439.23],[61.91,436.87],[67.84,445.39],[68.29,445.38],[74.2,447.83],[76.65,453.74]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-7.88,-270.32],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.65,3.96],[0,0],[0.12,0],[0.05,-0.12],[0,-4.97],[-5.53,0],[0,5.53]],"o":[[0,-4.97],[0,0],[-0.05,-0.12],[-0.13,0],[-1.65,3.96],[0,5.53],[5.53,0],[0,0]],"v":[[1606.35,1510.27],[1596.62,1481.62],[1596.63,1481.62],[1596.34,1481.42],[1596.05,1481.62],[1586.32,1510.27],[1596.34,1520.29],[1606.36,1510.27]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-1008.35,-941.67],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[4.07,-2.02],[3.61,2.74],[4.04,2.17],[4.72,3.8],[-2.96,1.01],[-2.3,3.07],[-3.83,-0.07],[-2.18,-3.15],[1.27,-3.62],[0,-4.84]],"o":[[0,4.54],[-4.06,2.01],[-3.71,2.7],[1.69,-5.82],[1.41,-2.79],[-1.14,-3.66],[2.3,-3.07],[3.83,0.07],[2.19,3.15],[4.47,1.84],[0,0]],"v":[[367.49,1433.73],[360.85,1444.43],[348.32,1443.23],[335.65,1444.09],[330.64,1428.2],[337.45,1422.28],[339.3,1411.57],[349.06,1406.8],[358.64,1411.92],[360.1,1422.7],[367.49,1433.73]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-210.12,-894.22],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[6.84,-1.55],[3.03,6.33],[-5.5,4.35],[-5.46,-4.4],[0.01,-4.55]],"o":[[0,7.01],[-6.84,1.55],[-3.03,-6.32],[5.49,-4.35],[3.54,2.85],[0,0]],"v":[[200.38,1540.59],[188.69,1555.23],[171.82,1547.07],[176.04,1528.82],[194.78,1528.9],[200.38,1540.59]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-108.22,-969.74],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[5.42,3.58]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[26.48,188.38],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[6.44,0],[0,6.44]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.55,183.78],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,36.4]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.55,184.7],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[4.91,2.96]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[47.72,181.48],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[7.26,0],[0,3.99]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[51.4,176.42],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,52.14]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[51.4,172.89],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[4.21,4.21]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[185.73,191.63],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[3.88,0],[0,3.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[188.88,188.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,36.87]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[188.9,184.7],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,18.06]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[98.02,1.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,200.92]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[112.64,50.37],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,200.92]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[102.96,50.37],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,200.92]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[93.28,50.37],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,0],[0,200.92]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[83.6,50.37],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[50.77,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.35,103.83],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[50.77,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.35,113.49],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[50.77,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.35,123.15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[50.77,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.35,132.8],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":24,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[50.77,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.35,142.46],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":25,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[50.77,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.35,152.11],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":26,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[50.77,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.35,161.77],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":27,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[50.77,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.35,171.42],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":28,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[50.77,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.35,181.08],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":29,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[50.77,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.35,190.74],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":30,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[50.77,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[138.35,200.39],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":31,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[246.55,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[15.34,212.35],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":32,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.1,-0.58],[0,0],[0,0],[0,0],[0,0],[-0.59,0]],"o":[[0,0],[0.59,0],[0,0],[0,0],[0,0],[0,0],[0.1,-0.58],[0,0]],"v":[[734.55,131.01],[764.49,131.01],[765.69,132.01],[768.84,148.88],[730.21,148.87],[733.36,132.01],[733.36,132.01],[734.55,131.01]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-464.12,-83.21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":33,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.02,-0.66],[0,0],[0,0],[0,0],[-0.66,0]],"o":[[0,0],[0.66,0],[0,0],[0,0],[0,0],[0.02,-0.66],[0,0]],"v":[[671.04,248.18],[724.92,248.18],[726.14,249.36],[726.56,262.15],[669.4,262.15],[669.82,249.36],[671.04,248.18]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-425.47,-157.7],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":34,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.22,-0.23],[0,-0.32],[0,0],[0,0],[0,0],[-0.23,0.23],[-0.32,0]],"o":[[0,0],[0.32,0],[0.23,0.23],[0,0],[0,0],[0,0],[0,-0.32],[0.23,-0.23],[0,0]],"v":[[622.02,339.85],[691.55,339.85],[692.41,340.21],[692.77,341.07],[692.77,571.1],[620.8,571.1],[620.8,341.07],[621.16,340.21],[622.02,339.85]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-394.57,-215.97],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":35,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.23,-0.23],[0.01,-0.33],[0,0],[0,0],[0,0],[-0.23,0.23],[-0.33,0]],"o":[[0,0],[0.33,0],[0.23,0.23],[0,0],[0,0],[0,0],[-0.01,-0.33],[0.23,-0.23],[0,0]],"v":[[1259.88,712.38],[1276.98,712.38],[1277.85,712.75],[1278.2,713.62],[1278.04,721.58],[1258.82,721.58],[1258.66,713.62],[1259.01,712.75],[1259.88,712.38]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-800.06,-452.79],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":36,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.23,-0.24],[0.02,-0.33],[0,0],[0,0],[0,0],[0,0],[-0.23,0.24],[-0.33,0]],"o":[[0,0],[0.33,0],[0.23,0.24],[0,0],[0,0],[0,0],[0,0],[-0.02,-0.33],[0.23,-0.24],[0,0]],"v":[[1160.27,772.75],[1207.73,772.75],[1208.62,773.13],[1208.95,774.03],[1208.56,781.95],[1159.45,781.95],[1159.06,774.03],[1159.05,774.03],[1159.39,773.13],[1160.27,772.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-736.74,-491.17],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":37,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.23,-0.23],[0,-0.32],[0,0],[0,0]],"o":[[0,0],[0.32,0],[0.23,0.23],[0,0],[0,0],[0,0]],"v":[[1093.05,833.12],[1160.39,833.12],[1161.26,833.48],[1161.61,834.34],[1161.61,989.19],[1093.05,989.19]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"undefined","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-694.78,-529.54],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[75,75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"undefined","np":1,"cix":2,"bm":0,"ix":38,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"surface14015","np":38,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.27],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"t":66,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"undefined","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":10,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[175.25,146.125,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-8.25],[8.24,0],[0,0],[0,-8.25],[0,0],[-8.24,0],[0,0],[-0.28,-8.21],[8.24,-0.16],[0,0],[3.1,-2.83],[36.03,39.26],[4.32,12.63],[5.9,-0.13],[0,0],[0.27,8.22],[-2.87,2.86],[-3.91,0.11],[0,0],[-1.55,0],[0,0],[0,8.24],[0,0],[6.39,1.69],[0,0],[1.26,0.34],[-2.12,7.96],[-6.76,-0.01],[0,0],[-2.55,2.24],[-35.23,-40],[-4.06,-12.68],[-3.36,-0.01],[0,0]],"o":[[0,8.25],[0,0],[-8.24,0],[0,0],[0,8.24],[0,0],[8.21,-0.06],[0.16,8.25],[0,0],[-4.2,0],[-39.26,36.04],[-9.02,-9.83],[-1.97,-5.56],[0,0],[-8.23,0.07],[-0.08,-4.05],[2.65,-2.88],[0,0],[3.36,0],[0,0],[8.25,0],[0,0],[0.01,-6.92],[0,0],[-1.3,0],[-7.97,-2.12],[1.74,-6.55],[0,0],[3.39,0.04],[40,-35.22],[8.8,9.99],[1,3.21],[0,0],[8.24,0]],"v":[[137.146,-9.037],[122.216,5.893],[79.226,5.893],[64.296,20.823],[64.296,21.903],[79.226,36.823],[91.846,36.823],[107.106,51.463],[92.466,66.683],[77.346,66.683],[65.996,71.083],[-70.344,65.233],[-90.564,31.183],[-103.754,22.083],[-121.874,22.083],[-137.144,7.423],[-132.774,-3.397],[-122.514,-8.077],[-103.874,-8.537],[-95.324,-8.697],[-81.284,-8.697],[-66.354,-23.627],[-66.354,-24.707],[-77.454,-39.137],[-90.734,-39.137],[-94.584,-39.637],[-105.174,-57.897],[-90.744,-68.987],[-72.224,-68.987],[-63.004,-72.407],[73.206,-63.757],[92.706,-29.367],[100.036,-23.967],[122.216,-23.967]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2.627,42.709],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 35","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[8.149,-0.067],[0,0],[-0.068,8.245],[-8.149,0.067],[0,0],[0.068,-8.245]],"o":[[0,0],[-8.245,-0.068],[0.067,-8.149],[0,0],[8.245,0.068],[-0.067,8.149]],"v":[[6.475,14.929],[-6.475,14.929],[-21.281,-0.122],[-6.475,-14.928],[6.475,-14.928],[21.281,0.123]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[143.91,94.466],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 36","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[8.15,-0.067],[0,0],[-0.068,8.245],[-8.15,0.067],[0,0],[0.067,-8.244]],"o":[[0,0],[-8.245,-0.067],[0.066,-8.149],[0,0],[8.245,0.067],[-0.067,8.15]],"v":[[9.173,14.929],[-9.173,14.929],[-23.979,-0.122],[-9.173,-14.928],[9.173,-14.928],[23.98,0.122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.03137254902,0.486274509804,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-141.711,-11.357],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 37","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/assets/map.png b/assets/map.png new file mode 100644 index 0000000..51805fc Binary files /dev/null and b/assets/map.png differ diff --git a/assets/riveAnimations/add.rev b/assets/riveAnimations/add.rev new file mode 100644 index 0000000..2b0e810 Binary files /dev/null and b/assets/riveAnimations/add.rev differ diff --git a/assets/riveAnimations/backupFIle.rev b/assets/riveAnimations/backupFIle.rev new file mode 100644 index 0000000..5c38b79 Binary files /dev/null and b/assets/riveAnimations/backupFIle.rev differ diff --git a/assets/riveAnimations/rive_animation.riv b/assets/riveAnimations/rive_animation.riv new file mode 100644 index 0000000..fc8ceea Binary files /dev/null and b/assets/riveAnimations/rive_animation.riv differ diff --git a/assets/svg/.DS_Store b/assets/svg/.DS_Store new file mode 100644 index 0000000..e6a1b14 Binary files /dev/null and b/assets/svg/.DS_Store differ diff --git a/assets/svg/Fallback/homeLogo.svg b/assets/svg/Fallback/homeLogo.svg new file mode 100644 index 0000000..27de827 --- /dev/null +++ b/assets/svg/Fallback/homeLogo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/Fallback/placeholder.svg b/assets/svg/Fallback/placeholder.svg new file mode 100644 index 0000000..57d9dd9 --- /dev/null +++ b/assets/svg/Fallback/placeholder.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/svg/Fallback/splash.svg b/assets/svg/Fallback/splash.svg new file mode 100644 index 0000000..8b9760f --- /dev/null +++ b/assets/svg/Fallback/splash.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/svg/Logo/company_logo.svg b/assets/svg/Logo/company_logo.svg new file mode 100644 index 0000000..64858a4 --- /dev/null +++ b/assets/svg/Logo/company_logo.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/svg/MultiColorSvg/.DS_Store b/assets/svg/MultiColorSvg/.DS_Store new file mode 100644 index 0000000..66214b6 Binary files /dev/null and b/assets/svg/MultiColorSvg/.DS_Store differ diff --git a/assets/svg/MultiColorSvg/delete_illustrator.svg b/assets/svg/MultiColorSvg/delete_illustrator.svg new file mode 100644 index 0000000..a581373 --- /dev/null +++ b/assets/svg/MultiColorSvg/delete_illustrator.svg @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/svg/MultiColorSvg/logout_illustrator.svg b/assets/svg/MultiColorSvg/logout_illustrator.svg new file mode 100644 index 0000000..1cac4ad --- /dev/null +++ b/assets/svg/MultiColorSvg/logout_illustrator.svg @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/svg/MultiColorSvg/no_chat_found.svg b/assets/svg/MultiColorSvg/no_chat_found.svg new file mode 100644 index 0000000..896cd49 --- /dev/null +++ b/assets/svg/MultiColorSvg/no_chat_found.svg @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/svg/MultiColorSvg/no_data_found_illustrator.svg b/assets/svg/MultiColorSvg/no_data_found_illustrator.svg new file mode 100644 index 0000000..5725fad --- /dev/null +++ b/assets/svg/MultiColorSvg/no_data_found_illustrator.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/svg/MultiColorSvg/no_internet_illustrator.svg b/assets/svg/MultiColorSvg/no_internet_illustrator.svg new file mode 100644 index 0000000..a7760a5 --- /dev/null +++ b/assets/svg/MultiColorSvg/no_internet_illustrator.svg @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/svg/MultiColorSvg/propertysubmited.svg b/assets/svg/MultiColorSvg/propertysubmited.svg new file mode 100644 index 0000000..7da882f --- /dev/null +++ b/assets/svg/MultiColorSvg/propertysubmited.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/svg/MultiColorSvg/something_went_wrong.svg b/assets/svg/MultiColorSvg/something_went_wrong.svg new file mode 100644 index 0000000..0f573f0 --- /dev/null +++ b/assets/svg/MultiColorSvg/something_went_wrong.svg @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/svg/about_us.svg b/assets/svg/about_us.svg new file mode 100644 index 0000000..7e91a06 --- /dev/null +++ b/assets/svg/about_us.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/area_convertor.svg b/assets/svg/area_convertor.svg new file mode 100644 index 0000000..4ae5424 --- /dev/null +++ b/assets/svg/area_convertor.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/svg/arrow_left.svg b/assets/svg/arrow_left.svg new file mode 100644 index 0000000..1583bb0 --- /dev/null +++ b/assets/svg/arrow_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/arrow_right.svg b/assets/svg/arrow_right.svg new file mode 100644 index 0000000..df6a829 --- /dev/null +++ b/assets/svg/arrow_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/article.svg b/assets/svg/article.svg new file mode 100644 index 0000000..2f4a9c1 --- /dev/null +++ b/assets/svg/article.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/svg/bin.svg b/assets/svg/bin.svg new file mode 100644 index 0000000..16dce90 --- /dev/null +++ b/assets/svg/bin.svg @@ -0,0 +1,87 @@ + + + + + + + + diff --git a/assets/svg/calender.svg b/assets/svg/calender.svg new file mode 100644 index 0000000..6960e9e --- /dev/null +++ b/assets/svg/calender.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/call.svg b/assets/svg/call.svg new file mode 100644 index 0000000..ba9e79a --- /dev/null +++ b/assets/svg/call.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/contact_us.svg b/assets/svg/contact_us.svg new file mode 100644 index 0000000..291ce66 --- /dev/null +++ b/assets/svg/contact_us.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/svg/dark_theme.svg b/assets/svg/dark_theme.svg new file mode 100644 index 0000000..092f755 --- /dev/null +++ b/assets/svg/dark_theme.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/assets/svg/defaultProfileIcon.svg b/assets/svg/defaultProfileIcon.svg new file mode 100644 index 0000000..e01132a --- /dev/null +++ b/assets/svg/defaultProfileIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/delete_account.svg b/assets/svg/delete_account.svg new file mode 100644 index 0000000..9a371fa --- /dev/null +++ b/assets/svg/delete_account.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/svg/down_arrow.svg b/assets/svg/down_arrow.svg new file mode 100644 index 0000000..5d3e248 --- /dev/null +++ b/assets/svg/down_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/edit.svg b/assets/svg/edit.svg new file mode 100644 index 0000000..48b9263 --- /dev/null +++ b/assets/svg/edit.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/svg/filter.svg b/assets/svg/filter.svg new file mode 100644 index 0000000..282a876 --- /dev/null +++ b/assets/svg/filter.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/svg/for_rent.svg b/assets/svg/for_rent.svg new file mode 100644 index 0000000..a40da0e --- /dev/null +++ b/assets/svg/for_rent.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/svg/for_sale.svg b/assets/svg/for_sale.svg new file mode 100644 index 0000000..1a71d16 --- /dev/null +++ b/assets/svg/for_sale.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/svg/header_curve.svg b/assets/svg/header_curve.svg new file mode 100644 index 0000000..da961f0 --- /dev/null +++ b/assets/svg/header_curve.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/assets/svg/home.svg b/assets/svg/home.svg new file mode 100644 index 0000000..23816d0 --- /dev/null +++ b/assets/svg/home.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/icon_arrow_left.svg b/assets/svg/icon_arrow_left.svg new file mode 100644 index 0000000..d245779 --- /dev/null +++ b/assets/svg/icon_arrow_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/inactive_chat.svg b/assets/svg/inactive_chat.svg new file mode 100644 index 0000000..4b99581 --- /dev/null +++ b/assets/svg/inactive_chat.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/svg/interested.svg b/assets/svg/interested.svg new file mode 100644 index 0000000..cf888e8 --- /dev/null +++ b/assets/svg/interested.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/svg/language.svg b/assets/svg/language.svg new file mode 100644 index 0000000..25fe7ab --- /dev/null +++ b/assets/svg/language.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/svg/like.svg b/assets/svg/like.svg new file mode 100644 index 0000000..8b1b07f --- /dev/null +++ b/assets/svg/like.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/like_fill.svg b/assets/svg/like_fill.svg new file mode 100644 index 0000000..9af98c6 --- /dev/null +++ b/assets/svg/like_fill.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/location.svg b/assets/svg/location.svg new file mode 100644 index 0000000..7eadfe0 --- /dev/null +++ b/assets/svg/location.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/svg/logout.svg b/assets/svg/logout.svg new file mode 100644 index 0000000..35280b7 --- /dev/null +++ b/assets/svg/logout.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/svg/magic.svg b/assets/svg/magic.svg new file mode 100644 index 0000000..3ea2f04 --- /dev/null +++ b/assets/svg/magic.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/svg/message.svg b/assets/svg/message.svg new file mode 100644 index 0000000..69ff070 --- /dev/null +++ b/assets/svg/message.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/assets/svg/notification.svg b/assets/svg/notification.svg new file mode 100644 index 0000000..5c79b3a --- /dev/null +++ b/assets/svg/notification.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/svg/other.svg b/assets/svg/other.svg new file mode 100644 index 0000000..3e42863 --- /dev/null +++ b/assets/svg/other.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/privacy.svg b/assets/svg/privacy.svg new file mode 100644 index 0000000..2015eb5 --- /dev/null +++ b/assets/svg/privacy.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/svg/privacypolicy.svg b/assets/svg/privacypolicy.svg new file mode 100644 index 0000000..2d4a648 --- /dev/null +++ b/assets/svg/privacypolicy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/profile.svg b/assets/svg/profile.svg new file mode 100644 index 0000000..4d67d56 --- /dev/null +++ b/assets/svg/profile.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/svg/promoted.svg b/assets/svg/promoted.svg new file mode 100644 index 0000000..3248dfc --- /dev/null +++ b/assets/svg/promoted.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/svg/properties.svg b/assets/svg/properties.svg new file mode 100644 index 0000000..e114c2b --- /dev/null +++ b/assets/svg/properties.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/svg/properties_icon.svg b/assets/svg/properties_icon.svg new file mode 100644 index 0000000..0d76eea --- /dev/null +++ b/assets/svg/properties_icon.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/svg/propertymap.svg b/assets/svg/propertymap.svg new file mode 100644 index 0000000..4af5587 --- /dev/null +++ b/assets/svg/propertymap.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/svg/rate_us.svg b/assets/svg/rate_us.svg new file mode 100644 index 0000000..e960cc7 --- /dev/null +++ b/assets/svg/rate_us.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/report.svg b/assets/svg/report.svg new file mode 100644 index 0000000..94f8d7f --- /dev/null +++ b/assets/svg/report.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/svg/report_dark.svg b/assets/svg/report_dark.svg new file mode 100644 index 0000000..3d2e5ff --- /dev/null +++ b/assets/svg/report_dark.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/svg/search.svg b/assets/svg/search.svg new file mode 100644 index 0000000..4463fbb --- /dev/null +++ b/assets/svg/search.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/svg/share.svg b/assets/svg/share.svg new file mode 100644 index 0000000..71af49b --- /dev/null +++ b/assets/svg/share.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/svg/subscription.svg b/assets/svg/subscription.svg new file mode 100644 index 0000000..08f7318 --- /dev/null +++ b/assets/svg/subscription.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/svg/t_c.svg b/assets/svg/t_c.svg new file mode 100644 index 0000000..f933006 --- /dev/null +++ b/assets/svg/t_c.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/svg/transaction.svg b/assets/svg/transaction.svg new file mode 100644 index 0000000..ec56806 --- /dev/null +++ b/assets/svg/transaction.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/svg/upcoming_projects_icon.svg b/assets/svg/upcoming_projects_icon.svg new file mode 100644 index 0000000..76ba2fc --- /dev/null +++ b/assets/svg/upcoming_projects_icon.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/svg/update.svg b/assets/svg/update.svg new file mode 100644 index 0000000..27e903f --- /dev/null +++ b/assets/svg/update.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/svg/v360.svg b/assets/svg/v360.svg new file mode 100644 index 0000000..eaac874 --- /dev/null +++ b/assets/svg/v360.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/svg/warning.svg b/assets/svg/warning.svg new file mode 100644 index 0000000..1334fb3 --- /dev/null +++ b/assets/svg/warning.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svg/whatsapp.svg b/assets/svg/whatsapp.svg new file mode 100644 index 0000000..808b16d --- /dev/null +++ b/assets/svg/whatsapp.svg @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..5c27c3e --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,2 @@ +extensions: + - provider: true \ No newline at end of file diff --git a/flutter_launcher_icons.yaml b/flutter_launcher_icons.yaml new file mode 100644 index 0000000..c2c3b96 --- /dev/null +++ b/flutter_launcher_icons.yaml @@ -0,0 +1,4 @@ +flutter_launcher_icons: + android: true + ios: true + image_path: "assets/AppIcon/icon.png" \ No newline at end of file diff --git a/lib/.DS_Store b/lib/.DS_Store new file mode 100644 index 0000000..97c9475 Binary files /dev/null and b/lib/.DS_Store differ diff --git a/lib/Ui/.DS_Store b/lib/Ui/.DS_Store new file mode 100644 index 0000000..288f616 Binary files /dev/null and b/lib/Ui/.DS_Store differ diff --git a/lib/Ui/Theme/theme.dart b/lib/Ui/Theme/theme.dart new file mode 100644 index 0000000..8032d5b --- /dev/null +++ b/lib/Ui/Theme/theme.dart @@ -0,0 +1,140 @@ +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; + +import '../../app/app.dart'; +import '../../utils/ui_utils.dart'; + +///Light Theme Colors +///This color format is different, isn't it? .You can use hex colors here also but you have to remove '#' symbol and add 0xff instead. +const Color primaryColor_ = Color(0xFFFAFAFA); +const Color secondaryColor_ = Color(0xFFFFFFFF); +const Color tertiaryColor_ = Color(0xFF087C7C); +const Color textColor = Color(0xFF4D5454); +Color lightTextColor = const Color(0xFF4D5454).withOpacity(0.5); +Color widgetsBorderColorLight = const Color(0xffEEEEEE).withOpacity(0.6); +Color senderChatColor = const Color.fromARGB(255, 233, 233, 233).darken(22); + +///Dark Theme Colors +Color primaryColorDark = const Color(0xff0C0C0C); +Color secondaryColorDark = const Color(0xff1C1C1C); +const Color tertiaryColorDark = Color(0xff53ADAE); +const Color textColorDarkTheme = Color(0xffFDFDFD); +Color lightTextColorDarkTheme = const Color(0xffFDFDFD).withOpacity(0.3); +Color widgetsBorderColorDark = const Color(0x1aFDFDFD); +Color darkSenderChatColor = + const Color.fromARGB(255, 233, 233, 233).darken(100); + +///Messages Color +const Color errorMessageColor = Color.fromARGB(255, 166, 4, 4); +const Color successMessageColor = Color.fromARGB(255, 12, 161, 161); +const Color warningMessageColor = Color(0xFFC2AF6F); + +//Button text color +const Color buttonTextColor = Colors.white; + +///Advance +//Theme settings +extension ColorPrefs on ColorScheme { + Color get primaryColor => _getColor( + brightness, + lightColor: appSettings.lightPrimary, + darkColor: appSettings.darkPrimary, + ); + Color get secondaryColor => _getColor( + brightness, + lightColor: appSettings.lightSecondary, + darkColor: appSettings.darkSecondary, + ); + Color get tertiaryColor => _getColor( + brightness, + lightColor: appSettings.lightTertiary, + darkColor: appSettings.darkTertiary, + ); + + Color get backgroundColor => _getColor( + brightness, + lightColor: appSettings.lightPrimary, + darkColor: appSettings.darkPrimary, + ); + + Color get buttonColor => buttonTextColor; + + Color get textColorDark => _getColor( + brightness, + lightColor: textColor, + darkColor: textColorDarkTheme, + ); + + Color get textLightColor => _getColor( + brightness, + lightColor: lightTextColor, + darkColor: lightTextColorDarkTheme, + ); + + Color get borderColor => _getColor(brightness, + lightColor: widgetsBorderColorLight, darkColor: widgetsBorderColorDark); + + Color get chatSenderColor => _getColor(brightness, + lightColor: senderChatColor, darkColor: darkSenderChatColor); + + ///This will set text color white if background is dark if background is light it will be dark + Color textAutoAdapt(Color backgroundColor) => + UiUtils.getAdaptiveTextColor(backgroundColor); + + Color get blackColor => Colors.black; + + Color get shimmerBaseColor => brightness == Brightness.light + ? const Color.fromARGB(255, 225, 225, 225) + : const Color.fromARGB(255, 150, 150, 150); + Color get shimmerHighlightColor => brightness == Brightness.light + ? Colors.grey.shade100 + : Colors.grey.shade300; + Color get shimmerContentColor => brightness == Brightness.light + ? Colors.white.withOpacity(0.85) + : Colors.white.withOpacity(0.7); +} + +// 10pt: Smaller +// 12pt: Small +// 16pt: Large +// 18pt: Larger +// 24pt: Extra large +extension TextThemeForFont on TextTheme { + Font get font => Font(); +} + +/// i made this to access font easyly from theme like, Theme.of(context).textTheme.font.small +/// So what is diffrence here?? in Theme.of(context).textTheme.small and Theme.of(context).textTheme.font.small +/// We use saperate class because There will be an exention on BuildContext in [Utils/Extensions/lib] folder so further explaination is there. you can check +class Font { + ///10 + double get smaller => 10; + + ///12 + double get small => 12; + + ///14 + double get normal => 14; + + ///16 + double get large => 16; + + ///18 + double get larger => 18; + + ///24 + double get extraLarge => 24; + + ///28 + double get xxLarge => 28; +} + +//This one is for check current theme and return data accordingly +Color _getColor(Brightness brightness, + {required Color lightColor, required Color darkColor}) { + if (Brightness.light == brightness) { + return lightColor; + } else { + return darkColor; + } +} diff --git a/lib/Ui/screens/.DS_Store b/lib/Ui/screens/.DS_Store new file mode 100644 index 0000000..98ca4dd Binary files /dev/null and b/lib/Ui/screens/.DS_Store differ diff --git a/lib/Ui/screens/Advertisement/create_advertisement_screen.dart b/lib/Ui/screens/Advertisement/create_advertisement_screen.dart new file mode 100644 index 0000000..dc9f12f --- /dev/null +++ b/lib/Ui/screens/Advertisement/create_advertisement_screen.dart @@ -0,0 +1,392 @@ +// ignore: unused_import +import 'dart:developer'; + +import 'package:ebroker/Ui/screens/home/Widgets/property_card_big.dart'; +import 'package:ebroker/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import 'package:ebroker/Ui/screens/widgets/promoted_widget.dart'; +import 'package:ebroker/app/routes.dart'; +import 'package:ebroker/data/Repositories/subscription_repository.dart'; +import 'package:ebroker/data/cubits/property/create_advertisement_cubit.dart'; +import 'package:ebroker/data/cubits/property/fetch_my_properties_cubit.dart'; +import 'package:ebroker/data/cubits/subscription/get_subsctiption_package_limits_cubit.dart'; +import 'package:ebroker/data/helper/widgets.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/helper_utils.dart'; +import 'package:ebroker/utils/imagePicker.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +enum AvertisementType { + home("HomeScreen"), + slider("Slider"), + list("ProductListing"); + + final String value; + const AvertisementType(this.value); +} + +class CreateAdvertisementScreen extends StatefulWidget { + final PropertyModel property; + const CreateAdvertisementScreen({ + super.key, + required this.property, + }); + static Route route(RouteSettings settings) { + Map? arguments = settings.arguments as Map?; + return BlurredRouter( + builder: (context) => MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => CreateAdvertisementCubit(), + ), + BlocProvider( + create: (context) => GetSubsctiptionPackageLimitsCubit(), + ), + ], + child: CreateAdvertisementScreen( + property: arguments?['model'], + ), + ), + ); + } + + @override + State createState() => + _CreateAdvertisementScreenState(); +} + +class _CreateAdvertisementScreenState extends State { + Map? selectedAdvertismentOption; + final PickImage _pickImage = PickImage(); + AvertisementType advertisementType = AvertisementType.home; + Widget getPreview() { + if (selectedAdvertismentOption?['id'] == 0 || + selectedAdvertismentOption == null) { + return Transform.scale( + scale: 0.8, child: PropertyCardBig(property: widget.property)); + } else if (selectedAdvertismentOption?['id'] == 1) { + return LayoutBuilder(builder: (context, c) { + return Container( + width: context.screenWidth - 100, + height: 150, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular( + 11, + ), + ), + child: Stack( + fit: StackFit.expand, + children: [ + _pickImage.listenChangesInUI( + (context, image) { + if (image != null) { + return Image.file( + _pickImage.pickedFile!, + fit: BoxFit.cover, + ); + } + + if (widget.property.titleImage == "") { + return UiUtils.getImage(widget.property.titleImage!); + } + return Image.network( + widget.property.titleImage ?? "", + fit: BoxFit.cover, + ); + }, + ), + const Positioned( + left: 10, + top: 10, + child: PromotedCard(type: PromoteCardType.icon), + ) + ], + ), + ); + }); + } else { + return Text(UiUtils.translate(context, "previewNotAvail")); + } + } + + Future _createAdvertisment() async { + if (selectedAdvertismentOption?['id'] == 0) { + advertisementType = AvertisementType.home; + } else if (selectedAdvertismentOption?['id'] == 1) { + advertisementType = AvertisementType.slider; + } else if (selectedAdvertismentOption?['id'] == 2) { + advertisementType = AvertisementType.list; + } + + context.read().create( + type: advertisementType.value, + propertyId: widget.property.id.toString(), + image: _pickImage.pickedFile); + } + + @override + void dispose() { + _pickImage.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + Future.delayed( + const Duration(milliseconds: 500), + () { + context + .read() + .getLimits(SubscriptionLimitType.advertisement); + }, + ); + } + + bool hasPackage = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: UiUtils.buildAppBar( + context, + showBackButton: true, + title: UiUtils.translate(context, "createAdvertisment"), + ), + bottomNavigationBar: BlocConsumer( + listener: (context, state) { + if (state is GetSubsctiptionPackageLimitsInProgress) { + Widgets.showLoader(context); + } + + if (state is GetSubsctiptionPackageLimitsFailure) { + Widgets.hideLoder(context); + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "somethingWentWrng"), + type: MessageType.error); + Navigator.pop(context); + } + if (state is GetSubsctiptionPackageLimitsSuccess) { + Widgets.hideLoder(context); + } + }, + builder: (context, state) { + if ((state is GetSubsctiptionPackageLimitsSuccess)) { + hasPackage = state.packageLimit.hasPackage == true; + } + + return Padding( + padding: const EdgeInsetsDirectional.fromSTEB(20, 0, 20, 8), + child: UiUtils.buildButton(context, onPressed: () { + if (hasPackage) { + _createAdvertisment(); + } else { + Navigator.pushNamed( + context, Routes.subscriptionPackageListRoute); + } + }, + // disabled: !hasPackage, + prefixWidget: hasPackage + ? null + : Icon( + Icons.lock, + color: context.color.buttonColor, + ), + buttonTitle: hasPackage + ? UiUtils.translate(context, "promote") + : UiUtils.translate(context, "subscribeToPackage")), + ); + // return MaterialButton( + // disabledColor: Colors.grey, + // onPressed: (isAdvertisementCreationLimitReached + // ? null + // : _createAdvertisment), + // height: 45, + // color: Theme.of(context).colorScheme.teritoryColor, + // child: Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // if (isAdvertisementCreationLimitReached) ...[ + // const Icon(Icons.lock), + // ], + // Text(isAdvertisementCreationLimitReached + // ? "Subscribe to package" + // : "Promote") + // .color(const Color.fromARGB(255, 255, 255, 255)), + // ], + // ), + // ); + }, + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: BlocConsumer( + listener: (context, state) { + if (state is CreateAdvertisementInProgress) { + Widgets.showLoader(context); + } + if (state is CreateAdvertisementFailure) { + Widgets.hideLoder(context); + HelperUtils.showSnackBarMessage( + context, + UiUtils.translate(context, "somethingWentWrng"), + type: MessageType.error, + ); + } + + if (state is CreateAdvertisementSuccess) { + Widgets.hideLoder(context); + // Constant.promotedProeprtiesIds.add(state.proeprtyId); + context.read().update(state.property); + HelperUtils.showSnackBarMessage( + context, + UiUtils.translate(context, "success"), + type: MessageType.success, + ); + Navigator.pop(context); + } + }, + builder: (context, state) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + UiUtils.translate(context, "preview"), + ) + .size( + 17, + ) + .color(context.color.textColorDark), + SizedBox( + height: 15.rh(context), + ), + Container( + height: 300, + width: MediaQuery.of(context).size.width, + color: const Color.fromARGB(255, 231, 231, 231), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IgnorePointer(ignoring: true, child: getPreview()), + ], + ), + ), + OptionsRow( + initialValue: (initial) { + selectedAdvertismentOption = initial; + }, + selected: (selected) { + selectedAdvertismentOption = selected; + setState(() {}); + }, + ), + const SizedBox( + height: 10, + ), + if (selectedAdvertismentOption?['id'] == 1) + Padding( + padding: const EdgeInsets.all(20.0), + child: Container( + width: context.screenWidth, + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(10)), + height: 48, + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(UiUtils.translate( + context, "pickSliderImage")), + MaterialButton( + onPressed: () { + _pickImage.pick(); + }, + child: Text( + UiUtils.translate(context, "uploadBtnLbl")), + ) + ], + ), + ), + ), + ), + ], + ), + ); + }, + ), + ), + ); + } +} + +class OptionsRow extends StatefulWidget { + final Function(Map initial)? initialValue; + final Function(Map selected) selected; + const OptionsRow({super.key, required this.selected, this.initialValue}); + @override + State createState() => _OptionsRowState(); +} + +class _OptionsRowState extends State { + int selectedOption = 0; + + ///add options here + List options = ["Home", "Slider", "List"]; + + Widget buildOption(String name, int index) => Expanded( + child: Padding( + padding: const EdgeInsets.all(1.0), + child: UiUtils.buildButton( + context, + height: 34, + buttonTitle: name, + textColor: index == selectedOption + ? context.color.textAutoAdapt(context.color.textColorDark) + : Theme.of(context).colorScheme.textColorDark, + radius: 7, + fontSize: context.font.normal, + onPressed: () { + selectedOption = index; + widget.selected({"id": index, "value": name}); + setState(() {}); + }, + buttonColor: index == selectedOption + ? Theme.of(context).colorScheme.tertiaryColor.withAlpha(255) + : Theme.of(context).colorScheme.tertiaryColor.withAlpha(50), + ))); + + @override + void initState() { + widget.initialValue?.call({"id": 0, "value": options[0]}); + super.initState(); + } + + @override + Widget build(BuildContext context) { + ///We need index to select some value , So when we are storing it in list + /// so we don't have index in list we can use for loop but it will not be perfect as this, + /// here we are converting list to map so it will get index automaticly and then using .map(k,v) + /// method so we can get index in key and do work on this + /// .values will only use its String values and here .cast will convert List dynamic to List of widgets + List optionList = options + .asMap() + .map((key, value) => MapEntry(key, buildOption(value, key))) + .values + .toList() + .cast(); + + return Row( + children: optionList, + ); + } +} diff --git a/lib/Ui/screens/Advertisement/my_advertisment_screen.dart b/lib/Ui/screens/Advertisement/my_advertisment_screen.dart new file mode 100644 index 0000000..be19eb0 --- /dev/null +++ b/lib/Ui/screens/Advertisement/my_advertisment_screen.dart @@ -0,0 +1,298 @@ +import 'package:ebroker/data/cubits/Utility/proeprty_edit_global.dart'; +import 'package:ebroker/utils/api.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; + +import '../../../app/routes.dart'; +import '../../../data/Repositories/advertisement_repository.dart'; +import '../../../data/cubits/delete_advertisment_cubit.dart'; +import '../../../data/cubits/favorite/add_to_favorite_cubit.dart'; +import '../../../data/cubits/favorite/fetch_favorites_cubit.dart'; +import '../../../data/cubits/property/fetch_my_promoted_propertys_cubit.dart'; +import '../../../data/model/property_model.dart'; +import '../../../utils/AdMob/bannerAdLoadWidget.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../home/Widgets/property_horizontal_card.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/Erros/no_data_found.dart'; +import '../widgets/Erros/no_internet.dart'; +import '../widgets/Erros/something_went_wrong.dart'; +import '../widgets/blurred_dialoge_box.dart'; + +class MyAdvertismentScreen extends StatefulWidget { + const MyAdvertismentScreen({super.key}); + static Route route(RouteSettings settings) { + return BlurredRouter( + builder: (context) { + return BlocProvider( + create: (context) => FetchMyPromotedPropertysCubit(), + child: const MyAdvertismentScreen(), + ); + }, + ); + } + + @override + State createState() => _MyAdvertismentScreenState(); +} + +class _MyAdvertismentScreenState extends State { + final ScrollController _pageScrollController = ScrollController(); + Map? statusMap; + @override + void initState() { + context.read().fetchMyPromotedPropertys(); + + Future.delayed( + Duration.zero, + () { + statusMap = { + 0: UiUtils.translate(context, "approved"), + 1: UiUtils.translate(context, "pending"), + 2: UiUtils.translate(context, "rejected") + }; + }, + ); + + _pageScrollController.addListener(_pageScroll); + super.initState(); + } + + void _pageScroll() { + if (_pageScrollController.isEndReached()) { + if (context.read().hasMoreData()) { + context + .read() + .fetchMyPromotedPropertysMore(); + } + } + } + + @override + void didChangeDependencies() { + statusMap = { + 0: UiUtils.translate(context, "approved"), + 1: UiUtils.translate(context, "pending"), + 2: UiUtils.translate(context, "rejected") + }; + super.didChangeDependencies(); + } + + Color? statusColor(status) { + if (status == 0) { + return Colors.green; + } else if (status == 1) { + return Colors.purple; + } else if (status == 2) { + return Colors.red; + } + return null; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: UiUtils.buildAppBar(context, + showBackButton: true, title: UiUtils.translate(context, "myAds")), + bottomNavigationBar: const BottomAppBar( + child: BannerAdWidget(bannerSize: AdSize.banner), + ), + body: BlocBuilder( + builder: (context, state) { + if (state is FetchMyPromotedPropertysInProgress) { + return Center(child: UiUtils.progress()); + } + if (state is FetchMyPromotedPropertysFailure) { + if (state.errorMessage is ApiException) { + if (state.errorMessage.errorMessage == "no-internet") { + return NoInternet( + onRetry: () { + context + .read() + .fetchMyPromotedPropertys(); + }, + ); + } + } + + return const SomethingWentWrong(); + } + if (state is FetchMyPromotedPropertysSuccess) { + if (state.propertymodel.isEmpty) { + return NoDataFound( + title: "noFeaturedAdsYes".translate(context), + description: "noFeaturedDescription".translate(context), + onTap: () { + context + .read() + .fetchMyPromotedPropertys(); + setState(() {}); + }, + ); + } + + return Column( + children: [ + Expanded( + child: ListView.builder( + controller: _pageScrollController, + itemCount: state.propertymodel.length, + padding: const EdgeInsets.symmetric(horizontal: 16), + itemBuilder: (context, index) { + PropertyModel property = state.propertymodel[index]; + + property = + context.watch().get(property); + return GestureDetector( + onTap: () { + Navigator.pushNamed( + context, + Routes.propertyDetails, + arguments: { + 'propertyData': property, + 'fromMyProperty': true + }, + ); + }, + child: BlocProvider( + create: (context) => DeleteAdvertismentCubit( + AdvertisementRepository()), + child: PropertyHorizontalCard( + property: property, + useRow: true, + onLikeChange: (type) { + if (type == FavoriteType.add) { + context + .read() + .add(state.propertymodel[index]); + } else { + context + .read() + .remove(state.propertymodel[index].id); + } + }, + additionalHeight: 50, + addBottom: [ + SizedBox( + width: 10.rw(context), + ), + Row(children: [ + Text(UiUtils.translate(context, "status")), + SizedBox( + width: 5.rw(context), + ), + SizedBox( + child: Chip( + label: Text( + statusMap![property.advertisment[0] + ['status']] + .toString() + .firstUpperCase(), + ) + .size(context.font.small) + .color(context.color.buttonColor), + backgroundColor: statusColor( + property.advertisment[0]['status'], + ), + visualDensity: + const VisualDensity(horizontal: 1), + padding: EdgeInsets.zero, + ), + ) + ]), + SizedBox( + width: 10.rw(context), + ), + Row( + children: [ + Text(UiUtils.translate(context, "type")), + SizedBox( + width: 5.rw(context), + ), + Chip( + label: Text(property.advertisment[0] + ['type'] + .toString())) + ], + ), + const Spacer(), + BlocConsumer( + listener: (context, state) { + if (state is DeleteAdvertismentSuccess) { + context + .read() + .delete(property.id); + } + }, + builder: (BuildContext context, + DeleteAdvertismentState state) { + ///it will only show delete button when status is pending it means 1. + + if (property.advertisment[0]['status'] != 1) { + return Container(); + } + + return IconButton( + onPressed: () { + UiUtils.showBlurredDialoge(context, + dialoge: BlurredDialogBox( + title: UiUtils.translate( + context, "deleteBtnLbl"), + onAccept: () async { + if (Constant.isDemoModeOn) { + HelperUtils.showSnackBarMessage( + context, + UiUtils.translate( + context, + "thisActionNotValidDemo")); + } else { + context + .read< + DeleteAdvertismentCubit>() + .delete(property + .advertisment[0] + ["id"]); + } + }, + content: Text(UiUtils.translate( + context, + "confirmDeleteAdvert")))); + }, + icon: (state + is DeleteAdvertismentInProgress) + ? UiUtils.progress() + : Icon( + Icons.delete, + color: + context.color.textColorDark, + )); + }, + ), + SizedBox( + width: 10.rw(context), + ), + ], + ), + ), + ); + }, + ), + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ); + } + return Container(); + }, + ), + ); + } +} diff --git a/lib/Ui/screens/Articles/article_details.dart b/lib/Ui/screens/Articles/article_details.dart new file mode 100644 index 0000000..239c9c9 --- /dev/null +++ b/lib/Ui/screens/Articles/article_details.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; + +import '../../../data/model/article_model.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; + +class ArticleDetails extends StatelessWidget { + final ArticleModel article; + + const ArticleDetails({super.key, required this.article}); + static Route route(RouteSettings settings) { + Map? arguments = settings.arguments as Map; + return BlurredRouter( + builder: (context) { + return ArticleDetails( + article: arguments['model'], + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.primaryColor, + appBar: UiUtils.buildAppBar( + context, + showBackButton: true, + ), + body: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Padding( + padding: const EdgeInsets.all( + 20.0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + clipBehavior: Clip.antiAlias, + borderRadius: BorderRadius.circular( + 10, + ), + child: SizedBox( + width: context.screenWidth, + height: 200.rh( + context, + ), + child: UiUtils.getImage( + article.image!, + fit: BoxFit.cover, + ), + ), + ), + SizedBox( + height: 15.rh(context), + ), + Text(article.date.toString().formatDate()) + .size(context.font.small) + .color(context.color.textLightColor), + const SizedBox( + height: 12, + ), + Text( + (article.title ?? "").firstUpperCase(), + ) + .size( + context.font.larger, + ) + .color( + context.color.textColorDark, + ) + .bold( + weight: FontWeight.w500, + ), + SizedBox( + height: 4.rh(context), + ), + Html(data: article.description ?? "") + ], + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/Articles/articles_screen.dart b/lib/Ui/screens/Articles/articles_screen.dart new file mode 100644 index 0000000..d115d6d --- /dev/null +++ b/lib/Ui/screens/Articles/articles_screen.dart @@ -0,0 +1,296 @@ +import 'package:ebroker/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart' + show BlurredRouter; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_html/flutter_html.dart' show Html; + +import '../../../app/routes.dart'; +import '../../../data/cubits/fetch_articles_cubit.dart'; +import '../../../data/model/article_model.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/api.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/Erros/no_data_found.dart'; +import '../widgets/Erros/no_internet.dart' show NoInternet; +import '../widgets/Erros/something_went_wrong.dart'; +import '../widgets/shimmerLoadingContainer.dart'; + +class ArticlesScreen extends StatefulWidget { + const ArticlesScreen({super.key}); + + static Route route(RouteSettings settings) { + return BlurredRouter( + builder: (context) { + return const ArticlesScreen(); + }, + ); + } + + @override + State createState() => _ArticlesScreenState(); +} + +class _ArticlesScreenState extends State { + final ScrollController _pageScrollController = ScrollController(); + + @override + void initState() { + context.read().fetchArticles(); + _pageScrollController.addListener(pageScrollListen); + super.initState(); + } + + void pageScrollListen() { + if (_pageScrollController.isEndReached()) { + if (context.read().hasMoreData()) { + context.read().fetchArticlesMore(); + } + } + } + + @override + void dispose() { + _pageScrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return RefreshIndicator( + color: context.color.tertiaryColor, + onRefresh: () async { + context.read().fetchArticles(); + }, + child: Scaffold( + backgroundColor: context.color.primaryColor, + appBar: UiUtils.buildAppBar( + context, + showBackButton: true, + title: UiUtils.translate( + context, + "articles", + ), + ), + body: BlocBuilder( + builder: (context, state) { + if (state is FetchArticlesInProgress) { + return buildArticlesShimmer(); + } + if (state is FetchArticlesFailure) { + if (state.errorMessage is ApiException) { + if (state.errorMessage.errorMessage == "no-internet") { + return NoInternet( + onRetry: () { + context.read().fetchArticles(); + }, + ); + } + } + return const SomethingWentWrong(); + } + if (state is FetchArticlesSuccess) { + if (state.articlemodel.isEmpty) { + return const NoDataFound(); + } + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: ListView.builder( + controller: _pageScrollController, + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.all(16), + itemCount: state.articlemodel.length, + itemBuilder: (context, index) { + ArticleModel article = state.articlemodel[index]; + + return buildArticleCard(context, article); + + // return article(state, index); + }), + ), + if (state.isLoadingMore) const CircularProgressIndicator(), + if (state.loadingMoreError) + Text(UiUtils.translate(context, "somethingWentWrng")) + ], + ); + } + return Container(); + }, + ), + ), + ); + } + + Widget buildArticleCard(BuildContext context, ArticleModel article) { + return Padding( + padding: const EdgeInsets.all(7.0), + child: GestureDetector( + onTap: () { + Navigator.pushNamed( + context, + Routes.articleDetailsScreenRoute, + arguments: { + "model": article, + }, + ); + }, + child: ClipRRect( + clipBehavior: Clip.antiAlias, + borderRadius: BorderRadius.circular(18), + child: Container( + width: double.infinity, + // height: 290, + decoration: BoxDecoration( + color: context.color.secondaryColor, + border: Border.all( + width: 1.5, + color: context.color.borderColor, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: + const EdgeInsetsDirectional.fromSTEB(12.0, 12, 12, 0), + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: UiUtils.getImage( + article.image!, + fit: BoxFit.cover, + width: double.infinity, + height: 160, + ), + ), + ), + Padding( + padding: + const EdgeInsetsDirectional.fromSTEB(12.0, 12, 12, 6), + child: Text( + (article.title ?? "").firstUpperCase(), + ) + .color( + context.color.textColorDark, + ) + .size(context.font.normal) + .setMaxLines( + lines: 2, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12, + ), + child: Text(stripHtmlTags(article.description ?? "").trim()) + .setMaxLines(lines: 3) + .size(context.font.small) + .color(context.color.textLightColor), + ), + Padding( + padding: const EdgeInsetsDirectional.fromSTEB(12.0, 4, 12, 6), + child: Text(article.date == null + ? "" + : article.date.toString().formatDate()) + .size(context.font.smaller) + .color(context.color.textLightColor), + ), + const SizedBox( + height: 5, + ), + ], + ), + ), + ), + ), + ); + } + + String stripHtmlTags(String htmlString) { + RegExp exp = RegExp(r"<[^>]*>", multiLine: true, caseSensitive: true); + String strippedString = htmlString.replaceAll(exp, ''); + return strippedString; + } + + Widget buildArticlesShimmer() { + return ListView.builder( + itemCount: 10, + shrinkWrap: true, + padding: const EdgeInsets.all(16), + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: ClipRRect( + clipBehavior: Clip.antiAlias, + borderRadius: BorderRadius.circular(18), + child: Container( + width: double.infinity, + height: 287.rh(context), + decoration: BoxDecoration( + color: context.color.secondaryColor, + border: Border.all( + width: 1.5, color: context.color.borderColor)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomShimmer( + width: double.infinity, + height: 160.rh(context), + ), + Padding( + padding: const EdgeInsets.all(10.0), + child: CustomShimmer( + width: 100.rw(context), + height: 10.rh(context), + ), + ), + Padding( + padding: const EdgeInsets.all(10.0), + child: CustomShimmer( + width: 160.rw(context), + height: 10.rh(context), + ), + ), + Padding( + padding: const EdgeInsets.all(10.0), + child: CustomShimmer( + width: 150.rw(context), + height: 10.rh(context), + ), + ), + ], + ), + ), + ), + ); + }); + } + + Container article(FetchArticlesSuccess state, int index) { + return Container( + constraints: const BoxConstraints( + minHeight: 50, + maxHeight: double.infinity, + ), + child: Card( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(state.articlemodel[index].title!).color(Colors.black), + const Divider(), + if (state.articlemodel[index].image != "") ...[ + Image.network(state.articlemodel[index].image!) + ], + const Divider(), + Html(data: state.articlemodel[index].description!) + ], + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/ChatNew/MessageTypes/audio_message.dart b/lib/Ui/screens/ChatNew/MessageTypes/audio_message.dart new file mode 100644 index 0000000..d756464 --- /dev/null +++ b/lib/Ui/screens/ChatNew/MessageTypes/audio_message.dart @@ -0,0 +1,400 @@ +import 'dart:math'; + +import 'package:audioplayers/audioplayers.dart'; +import 'package:ebroker/Ui/screens/ChatNew/MessageTypes/blueprint.dart'; +import 'package:ebroker/Ui/screens/widgets/custom_inkWell.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/convert.dart'; +import 'package:flutter/material.dart'; + +import '../../../../data/cubits/chatCubits/delete_message_cubit.dart'; +import '../../../../data/cubits/chatCubits/send_message.dart'; +import '../../../../exports/main_export.dart'; +import '../../../../utils/ui_utils.dart'; + +class AudioMessage extends Message { + @override + String type = "audio"; + late AudioPlayer audioPlayer; + ValueNotifier isPlaying = ValueNotifier(false); + int position = 0; + int durationChanged = 0; + ValueNotifier duration = ValueNotifier(Duration.zero); + ValueNotifier progressValue = ValueNotifier(0); + AudioMessage() { + id = DateTime.now().toString(); + } + @override + void init() async { + print("HELLO I AM INIT"); + audioPlayer = AudioPlayer(); + audioPlayer.onDurationChanged.listen((Duration event) { + durationChanged = event.inSeconds; + duration.value = event; + }); + + audioPlayer.onPlayerStateChanged.listen((PlayerState event) { + isPlaying.value = event == PlayerState.playing; + print("PLAYER STATE IS#${event} "); + }); + audioPlayer.onPositionChanged.listen((Duration event) { + position = event.inSeconds; + duration.value = event; + double progressIndicatorValue = ConvertNumber.inRange( + currentValue: event.inSeconds.toDouble(), + minValue: 0, + maxValue: durationChanged.toDouble(), + newMaxValue: 1, + newMinValue: 0); + progressValue.value = progressIndicatorValue; + }); + audioPlayer.setSourceUrl(message!.audio!); + + if (isSentNow && isSentByMe && isSent == false) { + try { + context!.read().send( + senderId: HiveUtils.getUserId().toString(), + recieverId: message!.receiverId!, + attachment: message?.file, + message: message!.message!, + proeprtyId: message!.propertyId!, + audio: message?.audio, + ); + } catch (e) { + print("i am issue!!! $e"); + } + } + + ///if this message is not sent now so it will set id from server + if (isSentNow == false) { + id = message!.id!; + } + // duration.value = await audioPlayer.getDuration(); + // print("DURATION IS ${duration}"); + super.init(); + } + + @override + void onRemove() { + context! + .read() + .delete(int.parse(id), receiverId: int.parse(message!.receiverId!)); + super.onRemove(); + } + + @override + void dispose() { + audioPlayer.dispose(); + super.dispose(); + } + + @override + Widget render(context) { + + return ValueListenableBuilder( + valueListenable: duration, + builder: (context, duration, child) { + return Align( + alignment: + isSentByMe ? Alignment.centerRight : Alignment.centerLeft, + child: Column( + crossAxisAlignment: isSentByMe + ? CrossAxisAlignment.end + : CrossAxisAlignment.start, + children: [ + Container( + height: 67, + margin: const EdgeInsets.symmetric(vertical: 5), + width: context.screenWidth * 0.74, + decoration: isSentByMe + ? getSentByMeDecoration(context) + : getOtherUserDecoration(context), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 6.5), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Row( + children: [ + CustomInkWell( + onTap: () { + if (isPlaying.value == true) { + audioPlayer.pause(); + } else { + audioPlayer.resume(); + } + }, + color: isSentByMe + ? getSentByMeDecoration(context) + .color! + .darken(20) + : getOtherUserDecoration(context) + .color! + .darken(20), + shape: BoxShape.circle, + child: Container( + clipBehavior: Clip.antiAlias, + width: 50 / 1.4, + height: 50 / 1.4, + decoration: const BoxDecoration( + shape: BoxShape.circle, + ), + child: ValueListenableBuilder( + valueListenable: isPlaying, + builder: (context, isPlaying, child) { + return Icon( + isPlaying + ? Icons.pause + : Icons.play_arrow_outlined, + ); + }), + ), + ), + const SizedBox( + width: 5, + ), + Expanded( + child: ValueListenableBuilder( + valueListenable: progressValue, + builder: (context, progressValue, child) { + return GradientProgressIndicator( + key: Key(message!.id!), + onProgressDrag: (progress) { + double progressIndicatorValue = + ConvertNumber.inRange( + currentValue: progress, + minValue: 0, + maxValue: 1, + newMaxValue: + durationChanged.toDouble(), + newMinValue: 0); + + audioPlayer.seek(Duration( + seconds: progressIndicatorValue + .toInt())); + }, + value: progressValue, + ); + }, + ), + ), + const SizedBox( + width: 10, + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 0), + child: Text( + "${duration!.inMinutes}:${duration.inSeconds}"), + ) + ], + ), + ), + BlocBuilder( + builder: (context, state) { + if (state is SendMessageInProgress) { + return Icon(Icons.watch_later_outlined); + } + return SizedBox.shrink(); + }, + ) + ], + ), + ), + ), + SizedBox( + width: 5, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 9), + child: Text((DateTime.parse(message!.date!)) + .toLocal() + .toIso8601String() + .toString() + .formatDate(format: "hh:mm aa")) + .size(context.font.smaller) + .color(context.color.textLightColor), + ), + ], + ), + ); + }); + } + + BoxDecoration getSentByMeDecoration(BuildContext context) { + return BoxDecoration( + color: const Color(0xffEEEEEE), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: context.color.borderColor, width: 1.5)); + } + + BoxDecoration getOtherUserDecoration(BuildContext context) { + return BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: context.color.borderColor, width: 1.5), + ); + } +} + +Map> preserveHeightMap = {}; + +class GradientProgressIndicator extends ProgressIndicator { + GradientProgressIndicator({ + Key? key, + double? value, + Color? backgroundColor, + this.width, + Color? color, + Animation? valueColor, + String? semanticsLabel, + String? semanticsValue, + this.minHeight, + this.onProgressDrag, + this.borderRadius = BorderRadius.zero, + }) : super( + key: key, + value: value, + backgroundColor: backgroundColor, + color: color, + valueColor: valueColor, + semanticsLabel: semanticsLabel, + semanticsValue: semanticsValue, + ); + + @override + Color? get backgroundColor => super.backgroundColor; + final Function(double progress)? onProgressDrag; + final double? minHeight; + final BorderRadiusGeometry borderRadius; + final double? width; + + @override + State createState() => + _GradientLinearProgressIndicatorState(); +} + +class _GradientLinearProgressIndicatorState + extends State + with AutomaticKeepAliveClientMixin { + List heightMap = []; + ValueNotifier progress = ValueNotifier(0); + final GlobalKey _globalKey = GlobalKey(); + double maxDragOffset = 0; + int numberOfContainers = 0; + + @override + void initState() { + super.initState(); + if (!preserveHeightMap.containsKey(widget.key)) { + Future.delayed( + const Duration(milliseconds: 10), + () { + Map widgetInfo = + UiUtils.getWidgetInfo(context, _globalKey); + maxDragOffset = widgetInfo['width']!; + numberOfContainers = (maxDragOffset / 3).floor(); + heightMap = getRandomHeight(numberOfContainers); + + ///This is to solve pattern change issue ... this will store pattern. + preserveHeightMap[widget.key!] = heightMap; + setState(() {}); + }, + ); + + progress.value = widget.value ?? 0.0; + } else { + heightMap = preserveHeightMap[widget.key!]!; + setState(() {}); + } + } + + @override + void didUpdateWidget(covariant GradientProgressIndicator oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.value != oldWidget.value) { + progress.value = widget.value ?? 0.0; + } + Map widgetInfo = UiUtils.getWidgetInfo(context, _globalKey); + if (widgetInfo['width'] != maxDragOffset) { + Map widgetInfo = + UiUtils.getWidgetInfo(context, _globalKey); + maxDragOffset = widgetInfo['width']!; + numberOfContainers = (maxDragOffset / 3).floor(); + } + } + + @override + Widget build(BuildContext context) { + super.build(context); + return GestureDetector( + onTapDown: (details) { + double inRange = ConvertNumber.inRange( + currentValue: (details.localPosition).dx.clamp(0, maxDragOffset), + minValue: 0, + maxValue: maxDragOffset, + newMaxValue: 1, + newMinValue: 0); + widget.onProgressDrag?.call(inRange); + progress.value = inRange; + }, + onHorizontalDragUpdate: (DragUpdateDetails details) { + double inRange = ConvertNumber.inRange( + currentValue: (details.localPosition).dx.clamp(0, maxDragOffset), + minValue: 0, + maxValue: maxDragOffset, + newMaxValue: 1, + newMinValue: 0); + progress.value = inRange; + print("HEYYY $details"); + widget.onProgressDrag?.call(inRange); + }, + child: ValueListenableBuilder( + valueListenable: progress, + builder: (context, progressValue, child) { + return SizedBox( + child: Container( + key: _globalKey, + height: widget.minHeight, + child: Row( + children: [ + ...List.generate(heightMap.length, (index) { + double inRange = ConvertNumber.inRange( + currentValue: progressValue.toDouble(), + minValue: 0, + maxValue: 1, + newMaxValue: heightMap.length.toDouble(), + newMinValue: 0); + return Container( + height: heightMap[index], + width: 3, + decoration: BoxDecoration( + color: inRange < index + ? Colors.grey + : context.color.tertiaryColor, + borderRadius: BorderRadius.circular(2)), + ); + }), + ], + ), + ), + ); + }), + ); + } + + List getRandomHeight(int count) { + List _heightMap = []; + + for (int index = 0; index < count; index++) { + double height = Random().nextDouble() * (widget.minHeight ?? 30.0); + _heightMap.add(height); + } + return _heightMap; + } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; +} diff --git a/lib/Ui/screens/ChatNew/MessageTypes/blueprint.dart b/lib/Ui/screens/ChatNew/MessageTypes/blueprint.dart new file mode 100644 index 0000000..cb00f7f --- /dev/null +++ b/lib/Ui/screens/ChatNew/MessageTypes/blueprint.dart @@ -0,0 +1,49 @@ +import 'package:ebroker/Ui/screens/ChatNew/model.dart'; +import 'package:flutter/material.dart'; + +enum MessageSendStatus { progress, success, fail } + +abstract class Message { + abstract String type; + String id = ""; + bool? isSent; + bool isSentByMe = false; + bool isSentNow = false; + ChatMessageModel? message; + BuildContext? context; + + Message(); + + @override + String toString() { + return 'Message{type: $type, id: $id}'; + } + + void init() {} + void dispose() {} + void onRemove() {} + + void setContext(BuildContext context) { + this.context = context; + } + + Widget render(BuildContext context); +} + +class MessageAction { + final String action; + final Message message; + + MessageAction({required this.action, required this.message}); +} + +class MessageId { + final String id; + MessageId(this.id); + factory MessageId.empty(String id) { + return MessageId(id); + } + factory MessageId.senderId(String id) { + return MessageId(id); + } +} diff --git a/lib/Ui/screens/ChatNew/MessageTypes/file_message.dart b/lib/Ui/screens/ChatNew/MessageTypes/file_message.dart new file mode 100644 index 0000000..1b3db90 --- /dev/null +++ b/lib/Ui/screens/ChatNew/MessageTypes/file_message.dart @@ -0,0 +1,163 @@ +// import 'dart:developer'; + +import 'package:ebroker/Ui/screens/ChatNew/MessageTypes/blueprint.dart'; +import 'package:ebroker/Ui/screens/ChatNew/MessageTypes/text_and_file.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; + +import '../../../../data/cubits/chatCubits/delete_message_cubit.dart'; +import '../../../../data/cubits/chatCubits/send_message.dart'; + +class FileMessage extends Message { + @override + String type = "file"; + List imageExtensions = ["png", "jpg", "jpeg", "webp", "bmp"]; + FileMessage() { + id = DateTime.now().toString(); + } + @override + void init() { + if (isSentNow && isSentByMe && isSent == false) { + context!.read().send( + senderId: HiveUtils.getUserId().toString(), + recieverId: message!.receiverId!, + attachment: message?.file, + message: message!.message!, + proeprtyId: message!.propertyId!, + audio: message?.audio, + ); + } + if (isSentNow == false) { + id = message!.id!; + } + super.init(); + } + + @override + Widget render(context) { + String extension = message!.file!.split(".").last.toString(); + + if (imageExtensions.contains(extension)) { + return ImageAttachmentWidget( + isSentByMe: isSentByMe, + message: message, + onFileSent: () { + isSent = true; + }, + onId: (id) { + this.id = id; + }, + ); + } + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10.0), + child: fileWidget(context, extension), + ); + } + + @override + void onRemove() { + context! + .read() + .delete(int.parse(id), receiverId: int.parse(message!.receiverId!)); + super.onRemove(); + } + + Widget fileWidget(BuildContext context, String extension) { + return Align( + alignment: isSentByMe ? Alignment.centerRight : Alignment.centerLeft, + child: Column( + crossAxisAlignment: + isSentByMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, + children: [ + Container( + width: context.screenWidth * 0.74, + // height: 65, + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: context.color.borderColor, + width: 1.5, + )), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 65, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 5), + child: Container( + height: 65, + child: Center( + child: Text(extension.toUpperCase()) + .color(context.color.textColorDark) + .size(context.font.small)), + ), + ), + Container( + width: 1.5, + height: 50, + color: context.color.borderColor.darken(10), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 7), + child: Text(message!.file!.split("/").last), + ), + ), + FileDownloadButton( + url: message!.file!, + ), + ], + ), + ), + BlocConsumer( + listener: (context, state) { + if (state is SendMessageSuccess) { + this.id = state.messageId.toString(); + // widget.onId.call(state.messageId.toString()); + // widget.onFileSent.call(); + isSent = true; + } + }, + builder: (context, state) { + if (state is SendMessageInProgress) { + return const Padding( + padding: EdgeInsets.all(2.0), + child: Icon( + Icons.watch_later_outlined, + size: 10, + ), + ); + } + return const SizedBox.shrink(); + }, + ) + ], + ), + ), + const SizedBox( + height: 5, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Text((DateTime.parse(message!.date!)) + .toLocal() + .toIso8601String() + .toString() + .formatDate(format: "hh:mm aa")) + .size(context.font.smaller) + .color(context.color.textLightColor), + ) + ], + ), + ); + } +} diff --git a/lib/Ui/screens/ChatNew/MessageTypes/registerar.dart b/lib/Ui/screens/ChatNew/MessageTypes/registerar.dart new file mode 100644 index 0000000..ed5a211 --- /dev/null +++ b/lib/Ui/screens/ChatNew/MessageTypes/registerar.dart @@ -0,0 +1,223 @@ +import 'dart:async'; + +import 'package:ebroker/Ui/screens/ChatNew/MessageTypes/audio_message.dart'; +import 'package:ebroker/Ui/screens/ChatNew/MessageTypes/file_message.dart'; +import 'package:ebroker/Ui/screens/ChatNew/MessageTypes/text_and_file.dart'; +import 'package:ebroker/Ui/screens/ChatNew/MessageTypes/text_message.dart'; +import 'package:ebroker/Ui/screens/ChatNew/model.dart'; +import 'package:flutter/material.dart'; + +import '../../../../utils/context_menu.dart'; +import 'blueprint.dart'; + +class MessageType { + final List _messageTypes = [ + TextMessage(), + AudioMessage(), + FileMessage(), + FileAndText(), + ]; + + Message? get(String type) { + return _messageTypes.where((element) => element.type == type).first; + } +} + +Message filterMessageType(ChatMessageModel data) { + return MessageType().get(data.chatMessageType!)!; +} + +class ChatMessageHandler { + static final List sentMessageIds = []; + static final List _messages = []; + static BuildContext? messageContext; + static final StreamController _messageStream = + StreamController.broadcast(); + static final StreamController> _allMessageStream = + StreamController>.broadcast(); + + static Stream> listenMessages() { + return _allMessageStream.stream; + } + + static void add(ChatMessageModel data) async { + try { + Message message = filterMessageType(data); + + message.isSentByMe = data.isSentByMe ?? false; + message.isSentNow = data.isSentNow ?? false; + message.message = data; + message.isSent = + data.isSentByMe == true ? sentMessageIds.contains(message.id) : null; + + ///This is to determine which messages are sent..because in flutter reverse list view there is issue of calling initstate of another instance + if (!sentMessageIds.contains(message.id) && + message.isSentByMe && + message.isSentNow) { + sentMessageIds.add(message.id); + } + + ///this is to resolve flutter's strange issue of not calling init state + if (message.type == "audio") { + if (messageContext!.mounted) { + message.setContext(messageContext!); + } + + message.init(); + } + + _messageStream.sink.add(MessageAction(action: "add", message: message)); + } catch (e) {} + } + + static void remove(dynamic id) async { + try { + int messageIndex = _messages.indexWhere((element) { + return element.id == id; + }); + Message deleatableMessage = _messages[messageIndex]; + _messageStream.sink.add(MessageAction( + action: "remove", + message: deleatableMessage, + )); + } catch (e) {} + } + + static void flush() { + _messages.clear(); + } + + static void fillMessages(List messages) { + _messages.addAll(messages); + + ///this will call init state of the audio element when loading which we are not calling in render method so here we have to call it + messages.forEach((element) { + if (element.type == "audio") { + element.init(); + } + }); + _allMessageStream.sink.add(messages); + } + + static void syncMessages() { + _allMessageStream.sink.add(_messages); + } + + static void handle() { + _messageStream.stream.listen( + (MessageAction messageAction) { + if (messageAction.action == "add") { + _messages.insert(0, messageAction.message); + + syncMessages(); + } + if (messageAction.action == "remove") { + messageAction.message.onRemove(); + _messages.remove(messageAction.message); + syncMessages(); + } + }, + ); + } +} + +///This class is using for render changes and +class RenderMessage extends StatefulWidget { + final Message message; + + const RenderMessage({key, required this.message}) : super(key: key); + + @override + MessageRenderState createState() => _RenderMessageState(); +} + +class _RenderMessageState extends MessageRenderState + with AutomaticKeepAliveClientMixin { + Widget? render; + @override + void initState() { + // if (isRendered()) { + + if (context.mounted) { + ChatMessageHandler.messageContext = context; + } + + widget.message.setContext(context); + if (widget.message.type != "audio") { + widget.message.init(); + } + // } + super.initState(); + } + + @override + void didChangeDependencies() { + if (mounted) { + widget.message.setContext(context); + } + + super.didChangeDependencies(); + } + + @override + void dispose() { + widget.message.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + + return ContextMenuRegion( + contextMenuBuilder: (context, offset) { + return AdaptiveTextSelectionToolbar.buttonItems( + anchors: TextSelectionToolbarAnchors( + primaryAnchor: offset, + ), + buttonItems: [ + if (widget.message.type == "text" || + widget.message.type == "file_and_text") + ContextMenuButtonItem( + type: ContextMenuButtonType.copy, + onPressed: () { + ContextMenuController.removeAny(); + }, + label: + 'Copy${widget.message.type == "file_and_text" ? " Text" : ""}', + ), + if (widget.message.isSentByMe) + ContextMenuButtonItem( + type: ContextMenuButtonType.delete, + onPressed: () { + ChatMessageHandler.remove(widget.message.id); + + ContextMenuController.removeAny(); + }, + label: 'Delete', + ), + ], + ); + }, + child: Builder(builder: (ctx) { + return widget.message.render(context); + }), + ); + } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; +} + +abstract class MessageRenderState extends State { + static List renderedMessage = []; + bool isRendered() { + if (renderedMessage.contains(this.widget.key)) { + return true; + } else { + renderedMessage.add(this.widget.key); + return false; + } + } +} diff --git a/lib/Ui/screens/ChatNew/MessageTypes/text_and_file.dart b/lib/Ui/screens/ChatNew/MessageTypes/text_and_file.dart new file mode 100644 index 0000000..e473ac4 --- /dev/null +++ b/lib/Ui/screens/ChatNew/MessageTypes/text_and_file.dart @@ -0,0 +1,478 @@ +import 'dart:developer'; +import 'dart:io'; +import 'dart:ui'; + +import 'package:dio/dio.dart'; +import 'package:ebroker/Ui/screens/ChatNew/MessageTypes/blueprint.dart'; +import 'package:ebroker/Ui/screens/ChatNew/model.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:open_filex/open_filex.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; + +import '../../../../data/cubits/chatCubits/delete_message_cubit.dart'; +import '../../../../data/cubits/chatCubits/send_message.dart'; +import '../../../../utils/helper_utils.dart'; + +class FileAndText extends Message { + @override + String type = "file_and_text"; + + List imageExtensions = ["png", "jpg", "jpeg", "webp", "bmp"]; + + @override + void init() { + if (isSentNow && isSentByMe && isSent == false) { + context!.read().send( + senderId: HiveUtils.getUserId().toString(), + recieverId: message!.receiverId!, + attachment: message?.file, + message: message!.message!, + proeprtyId: message!.propertyId!, + audio: message?.audio, + ); + } + super.init(); + } + + @override + void onRemove() { + context! + .read() + .delete(int.parse(id), receiverId: int.parse(message!.receiverId!)); + super.onRemove(); + } + + @override + render(context) { + String extension = message!.file!.split(".").last.toString(); + + if (imageExtensions.contains(extension)) { + return ImageAttachmentWidget( + isSentByMe: isSentByMe, + message: message, + onFileSent: () { + isSent = true; + }, + onId: (id) { + this.id = id; + }, + ); + } + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10.0), + child: Align( + alignment: isSentByMe ? Alignment.centerRight : Alignment.centerLeft, + child: Column( + crossAxisAlignment: + isSentByMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, + children: [ + Container( + width: context.screenWidth * 0.74, + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: context.color.borderColor, + width: 1.5, + )), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 65, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 5), + child: Container( + height: 65, + child: Center( + child: Text(extension.toUpperCase()) + .color(context.color.textColorDark) + .size(context.font.small)), + ), + ), + Container( + width: 1.5, + height: 50, + color: context.color.borderColor.darken(10), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 7), + child: Text(message!.file!.split("/").last), + ), + ), + FileDownloadButton( + url: message!.file!, + ), + ], + ), + ), + if (message?.message != null && + message!.message!.isNotEmpty) ...[ + const Divider(), + Padding( + padding: + const EdgeInsets.only(bottom: 8, right: 8, left: 8), + child: Text(message!.message!), + ) + ], + BlocConsumer( + listener: (context, state) { + if (state is SendMessageSuccess) { + this.id = state.messageId.toString(); + // widget.onId.call(state.messageId.toString()); + // widget.onFileSent.call(); + isSent = true; + } + }, + builder: (context, state) { + if (state is SendMessageInProgress) { + return const Padding( + padding: EdgeInsets.all(2.0), + child: Icon( + Icons.watch_later_outlined, + size: 10, + ), + ); + } + return const SizedBox.shrink(); + }, + ) + ], + ), + ), + const SizedBox( + height: 5, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Text((DateTime.parse(message!.date!)) + .toLocal() + .toIso8601String() + .toString() + .formatDate(format: "hh:mm aa")) + .size(context.font.smaller) + .color(context.color.textLightColor), + ) + ], + ), + ), + ); + } +} + +class FileDownloadButton extends StatefulWidget { + final String url; + const FileDownloadButton({super.key, required this.url}); + + @override + State createState() => _FileDownloadButtonState(); +} + +class _FileDownloadButtonState extends State { + final ValueNotifier _progressNotifier = ValueNotifier(0); + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: _progressNotifier, + builder: (context, value, child) { + if (value != 0 && value != 1) { + return Padding( + padding: const EdgeInsets.all(10.0), + child: SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + value: value, + color: context.color.tertiaryColor, + ), + ), + ); + } + + return IconButton( + onPressed: () { + downloadFile(); + }, + icon: const Icon(Icons.download)); + }); + } + + String getExtentionOfFile() { + return widget.url.toString().split(".").last; + } + + String getFileName() { + return widget.url.toString().split("/").last; + } + + Future downloadFile() async { + try { + String? downloadPath = await getDownloadPath(); + await Dio().download( + widget.url, + "${downloadPath!}/${getFileName()}", + onReceiveProgress: (int count, int total) async { + _progressNotifier.value = (count) / total; + if (_progressNotifier.value == 1) { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "fileSavedIn"), + type: MessageType.success); + + await OpenFilex.open("$downloadPath/${getFileName()}"); + } + setState(() {}); + }, + ); + } catch (e) { + print("Download Error is: $e"); + + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "errorFileSave"), + type: MessageType.success); + } + } + + Future getDownloadPath() async { + Directory? directory; + try { + if (Platform.isIOS) { + directory = await getApplicationDocumentsDirectory(); + } else { + directory = Directory('/storage/emulated/0/Download'); + // Put file in global download folder, if for an unknown reason it didn't exist, we fallback + // ignore: avoid_slow_async_io + if (!await directory.exists()) { + directory = await getExternalStorageDirectory(); + } + } + } catch (err) { + if (kDebugMode) { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "fileNotSaved"), + type: MessageType.success); + } + } + return directory?.path; + } +} + +class ImageAttachmentWidget extends StatefulWidget { + final Function(String id) onId; + final Function() onFileSent; + const ImageAttachmentWidget({ + super.key, + required this.isSentByMe, + required this.message, + required this.onId, + required this.onFileSent, + }); + + final bool isSentByMe; + final ChatMessageModel? message; + + @override + State createState() => _ImageAttachmentWidgetState(); +} + +class _ImageAttachmentWidgetState extends State { + bool isFileDownloading = false; + double persontage = 0; + String getExtentionOfFile() { + return widget.message!.file.toString().split(".").last; + } + + String getFileName() { + return widget.message!.file.toString().split("/").last; + } + + Future downloadFile() async { + try { + if (!(await Permission.storage.isGranted)) { + await Permission.storage.request(); + HelperUtils.showSnackBarMessage( + context, "Please give storage permission"); + + return; + } + + String? downloadPath = await HelperUtils.getDownloadPath( + onError: (err) { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "fileNotSaved"), + type: MessageType.success); + }, + ); + await Dio().download( + widget.message!.file!, + "${downloadPath!}/${getFileName()}", + onReceiveProgress: (int count, int total) async { + persontage = (count) / total; + + if (persontage == 1) { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "fileSavedIn"), + type: MessageType.success); + + await OpenFilex.open("$downloadPath/${getFileName()}"); + } + setState(() {}); + }, + ); + } catch (e) { + print("Download Error is: $e"); + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "errorFileSave"), + type: MessageType.success); + } + } + + @override + Widget build(BuildContext context) { + bool isLocalFile = widget.message!.file!.startsWith("/data/user/0/"); + + return Align( + alignment: + widget.isSentByMe ? Alignment.centerRight : Alignment.centerLeft, + child: Column( + crossAxisAlignment: widget.isSentByMe + ? CrossAxisAlignment.end + : CrossAxisAlignment.start, + children: [ + Container( + margin: const EdgeInsets.symmetric(vertical: 10), + width: context.screenWidth * 0.74, + // height: context.screenHeight * 0.4, + constraints: BoxConstraints(minHeight: context.screenHeight * 0.4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: context.color.secondaryColor, + border: Border.all(color: context.color.borderColor, width: 1.5), + ), + padding: const EdgeInsets.all(5), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: context.screenHeight * 0.4, + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: GestureDetector( + onTap: () { + FileImage fileImage = + FileImage(File(widget.message!.file!)); + NetworkImage networkImage = + NetworkImage(widget.message!.file!); + + late ImageProvider image; + if (isLocalFile) { + image = fileImage; + } else { + image = networkImage; + } + + UiUtils.showFullScreenImage( + context, + downloadOption: true, + provider: image, + ); + }, + child: isLocalFile + ? BlurredImage( + image: FileImage(File(widget.message!.file!)), + ) + : BlurredImage( + image: NetworkImage(widget.message!.file!), + ), + ), + ), + ), + if (widget.message!.message != "" && + widget.message!.message != "[File]") + Padding( + padding: + const EdgeInsets.symmetric(vertical: 5, horizontal: 2), + child: Text("${widget.message?.message ?? ""}"), + ), + BlocConsumer( + listener: (context, state) { + if (state is SendMessageSuccess) { + log("message senttt ${state.messageId}"); + // this.id = state.messageId.toString(); + widget.onId.call(state.messageId.toString()); + widget.onFileSent.call(); + } + }, + builder: (context, state) { + if (state is SendMessageInProgress) { + return const Padding( + padding: EdgeInsets.all(2.0), + child: Icon( + Icons.watch_later_outlined, + size: 10, + ), + ); + } + return const SizedBox.shrink(); + }, + ) + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Text((DateTime.parse(widget.message!.date!)) + .toLocal() + .toIso8601String() + .toString() + .formatDate(format: "hh:mm aa")) + .size(context.font.smaller) + .color(context.color.textLightColor), + ) + ], + ), + ); + } +} + +class BlurredImage extends StatelessWidget { + final ImageProvider image; + const BlurredImage({super.key, required this.image}); + + @override + Widget build(BuildContext context) { + return Container( + child: Stack( + fit: StackFit.expand, + children: [ + Image(image: image, fit: BoxFit.cover), + Container( + height: 220, + width: 150, + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 6, sigmaY: 5), + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.2), + ), + ), + ), + ), + Image( + image: image, + ) + ], + )); + } +} diff --git a/lib/Ui/screens/ChatNew/MessageTypes/text_message.dart b/lib/Ui/screens/ChatNew/MessageTypes/text_message.dart new file mode 100644 index 0000000..b369a00 --- /dev/null +++ b/lib/Ui/screens/ChatNew/MessageTypes/text_message.dart @@ -0,0 +1,139 @@ +import 'dart:developer'; + +import 'package:ebroker/Ui/screens/ChatNew/MessageTypes/blueprint.dart'; +import 'package:ebroker/data/cubits/chatCubits/delete_message_cubit.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; + +import '../../../../data/cubits/chatCubits/send_message.dart'; +import '../../../../exports/main_export.dart'; + +class TextMessage extends Message { + TextMessage() { + id = DateTime.now().toString(); + } + @override + void init() { + if (isSentNow && isSentByMe && isSent == false) { + context?.read().send( + senderId: HiveUtils.getUserId().toString(), + recieverId: message!.receiverId!, + attachment: message?.file, + message: message!.message!, + proeprtyId: message!.propertyId!, + audio: message?.audio, + ); + } + + ///if this message is not sent now so it will set id from server + if (isSentNow == false) { + id = message!.id!; + } + + super.init(); + } + + @override + void onRemove() async { + context! + .read() + .delete(int.parse(id), receiverId: int.parse(message!.receiverId!)); + + super.onRemove(); + } + + @override + Widget render(context) { + Color messageColor = context.color.textColorDark; + if (isSentByMe) { + messageColor = context.color.brightness == Brightness.light + ? context.color.textColorDark + : Colors.black; + } + + return Align( + alignment: isSentByMe ? Alignment.centerRight : Alignment.centerLeft, + child: Container( + margin: const EdgeInsets.symmetric(vertical: 10), + child: Column( + crossAxisAlignment: + isSentByMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, + children: [ + Container( + constraints: BoxConstraints(maxWidth: context.screenWidth * 0.74), + decoration: isSentByMe + ? getSentByMeDecoration(context) + : getOtherUserDecoration(context), + // color: isSentByMe ? Color(0xffEEEEEE) : context.color.secondaryColor, + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.all(12.0), + child: Text(message?.message ?? "") + .size(context.font.normal) + .color(messageColor), + ), + BlocConsumer( + listener: (context, state) { + if (state is SendMessageSuccess) { + log("message senttt ${state.messageId}"); + this.id = state.messageId.toString(); + isSent = true; + } + }, + builder: (context, state) { + if (state is SendMessageInProgress) { + return const Padding( + padding: EdgeInsets.all(2.0), + child: Icon( + Icons.watch_later_outlined, + size: 10, + ), + ); + } + return const SizedBox.shrink(); + }, + ) + ], + ), + ), + const SizedBox( + height: 5, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Text((DateTime.parse(message!.date!)) + .toLocal() + .toIso8601String() + .toString() + .formatDate(format: "hh:mm aa")) + .size(context.font.smaller) + .color(context.color.textLightColor), + ) + ], + ), + ), + ); + } + + BoxDecoration getSentByMeDecoration(BuildContext context) { + return BoxDecoration( + color: const Color(0xffEEEEEE), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: context.color.borderColor, width: 1.5)); + } + + BoxDecoration getOtherUserDecoration(BuildContext context) { + return BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: context.color.borderColor, width: 1.5), + ); + } + + @override + String type = "text"; +} diff --git a/lib/Ui/screens/ChatNew/model.dart b/lib/Ui/screens/ChatNew/model.dart new file mode 100644 index 0000000..4a5042f --- /dev/null +++ b/lib/Ui/screens/ChatNew/model.dart @@ -0,0 +1,119 @@ +import 'dart:math'; + +class ChatMessageModel { + String? id; + bool? isSentByMe; + bool? isSentNow; + + String? date; + + String? propertyTitleImage; + String? timeAgo; + String? receiverId; + String? sound; + String? userProfile; + String? body; + String? title; + String? clickAction; + String? message; + String? senderId; + String? propertyId; + String? file; + String? chatMessageType; + String? audio; + String? username; + + ChatMessageModel( + {this.date, + this.id, + this.isSentByMe, + this.isSentNow, + this.propertyTitleImage, + this.timeAgo, + this.receiverId, + this.sound, + this.userProfile, + this.body, + this.title, + this.clickAction, + this.message, + this.senderId, + this.propertyId, + this.file, + this.chatMessageType, + this.audio, + this.username}); + + ChatMessageModel.fromJson(Map json) { + id = json['id'].toString(); + isSentByMe = json['isSentByMe'] ?? false; + isSentNow = json['isSentNow'] ?? false; + date = json['date']; + propertyTitleImage = json['property_title_image']; + timeAgo = json['time_ago']; + receiverId = json['receiver_id'].toString(); + sound = json['sound']; + + userProfile = json['user_profile']; + body = json['body']; + title = json['title']; + clickAction = json['click_action']; + message = json['message']; + senderId = json['sender_id'].toString(); + + propertyId = json['property_id'].toString(); + file = json['file']; + chatMessageType = json['type']; + audio = json['audio']; + username = json['username']; + } + + Map toJson() { + final Map data = {}; + + data['date'] = date; + + data['id'] = id; + + data['isSentNow'] = isSentNow; + data['isSentByMe'] = isSentByMe; + data['property_title_image'] = propertyTitleImage; + data['time_ago'] = timeAgo; + data['receiver_id'] = receiverId; + data['sound'] = sound; + data['user_profile'] = userProfile; + data['body'] = body; + data['title'] = title; + data['click_action'] = clickAction; + data['message'] = message; + data['sender_id'] = senderId; + data['property_id'] = propertyId; + data['file'] = file; + data['chat_message_type'] = chatMessageType; + data['audio'] = audio; + data['username'] = username; + return data; + } + + void setId(String id) { + this.id = id; + } + + void setIsSentByMe(bool value) { + isSentByMe = value; + } + + void setIsSentNow(bool value) { + isSentNow = value; + } + + @override + String toString() { + return 'ChatMessageModel{date: $date,sentByMe:$isSentByMe, sentNow:$isSentNow id:$id, propertyTitleImage: $propertyTitleImage, timeAgo: $timeAgo, receiverId: $receiverId, sound: $sound, userProfile: $userProfile, body: $body,title: $title, clickAction: $clickAction, message: $message, senderId: $senderId, propertyId: $propertyId, file: $file, chatMessageType: $chatMessageType, audio: $audio, username: $username}'; + } +} + +String generateUniqueId() { + // Implement a logic to generate a unique identifier (timestamp + random value) + return "${DateTime.now().millisecondsSinceEpoch}-${Random().nextInt(1000)}"; +} diff --git a/lib/Ui/screens/Converter/area_converter.dart b/lib/Ui/screens/Converter/area_converter.dart new file mode 100644 index 0000000..4440a36 --- /dev/null +++ b/lib/Ui/screens/Converter/area_converter.dart @@ -0,0 +1,231 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/area_converter.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/custom_text_form_field.dart'; + +class AreaCalculator extends StatefulWidget { + const AreaCalculator({super.key}); + static Route route(RouteSettings settings) { + return BlurredRouter( + builder: (context) { + return const AreaCalculator(); + }, + ); + } + + @override + State createState() => _AreaCalculatorState(); +} + +class _AreaCalculatorState extends State { + List values = UnitTypes.values.map((e) => e.name).toList(); + + late final ValueNotifier _from = ValueNotifier(values[0]); + late final ValueNotifier _to = ValueNotifier(values[1]); + + final TextEditingController _fromTextController = TextEditingController(); + final TextEditingController _toTextController = TextEditingController(); + final TextEditingController _resultController = + TextEditingController(text: "00"); + + @override + void dispose() { + _from.dispose(); + _to.dispose(); + _fromTextController.dispose(); + _resultController.dispose(); + _toTextController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.primaryColor, + appBar: UiUtils.buildAppBar(context, + showBackButton: true, + title: UiUtils.translate(context, "areaConvertor")), + body: Padding( + padding: const EdgeInsets.all(010.0), + child: Container( + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(10)), + width: context.screenWidth, + child: Padding( + padding: const EdgeInsets.all(15.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 15.rh(context), + ), + Text("${UiUtils.translate(context, "convert")} ${_placeSpaceBeforeCapital(_from.value)} to ${_placeSpaceBeforeCapital(_to.value)}") + .size(context.font.large) + .color(context.color.textColorDark), + SizedBox( + height: 3.rh(context), + ), + const Text("Enter the value and select desired unit") + .size(context.font.small) + .color(context.color.textLightColor), + SizedBox( + height: 15.rh(context), + ), + ValueListenableBuilder( + valueListenable: _from, + builder: (context, value, child) { + return buildField( + context, + controller: _fromTextController, + value: value, + hint: "from", + valueListanable: _from, + ); + }), + SizedBox( + height: 15.rh(context), + ), + ValueListenableBuilder( + valueListenable: _to, + builder: (context, value, child) { + return buildField( + context, + controller: _toTextController, + isReadOnly: true, + value: value, + hint: "to", + valueListanable: _to, + ); + }), + SizedBox( + height: 20.rh(context), + ), + CustomTextFormField( + isReadOnly: true, + controller: _resultController, + fillColor: context.color.textColorDark.withOpacity(0.03), + ), + SizedBox( + height: 20.rh(context), + ), + UiUtils.buildButton(context, onPressed: () { + if (_fromTextController.text.isEmpty) { + return; + } + + var convert = AreaConverter().convert( + num.parse(_fromTextController.text), + from: getEnum(_from.value), + to: getEnum(_to.value)); + + _toTextController.text = convert.toString(); + + _resultController.text = + "${_fromTextController.text} ${_placeSpaceBeforeCapital(_from.value)} = $convert ${_placeSpaceBeforeCapital(_to.value)}"; + }, buttonTitle: UiUtils.translate(context, "convert")) + ], + ), + ), + ), + ), + ); + } + + Widget buildField( + BuildContext context, { + required dynamic value, + required ValueNotifier valueListanable, + bool? isReadOnly, + String? hint, + required TextEditingController controller, + }) { + return Container( + decoration: BoxDecoration( + color: context.color.textColorDark.withOpacity(0.03), + border: Border.all(width: 1.5, color: context.color.borderColor), + borderRadius: BorderRadius.circular(10), + ), + height: 55.rh(context), + width: context.screenWidth, + child: buildConvertTextFieldWithDropdown(context, + hint: hint, + controller: controller, + isReadOnly: isReadOnly, onChange: (value) { + valueListanable.value = value; + }, value: value), + ); + } + + Widget buildConvertTextFieldWithDropdown(BuildContext context, + {required Function(dynamic value) onChange, + String? hint, + bool? isReadOnly, + required TextEditingController controller, + dynamic value}) { + return Row( + children: [ + const SizedBox( + width: 15, + ), + Expanded( + child: TextField( + controller: controller, + readOnly: isReadOnly ?? false, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d*')) + ], + decoration: InputDecoration( + border: InputBorder.none, + hintText: UiUtils.translate(context, hint ?? "")), + )), + VerticalDivider( + color: context.color.textColorDark, + endIndent: 5, + indent: 5, + ), + Expanded( + child: DropdownButton( + value: value, + isExpanded: true, + underline: const SizedBox.shrink(), + items: List.generate(values.length, (index) { + return DropdownMenuItem( + value: values[index], + child: Text(_placeSpaceBeforeCapital(values[index])), + ); + }), + onChanged: (value) { + onChange.call(value); + setState(() {}); + }, + ), + ) + ], + ); + } + + String _placeSpaceBeforeCapital(String value) { +// if(value=="Square feet") + + if (value == "squareMeter") { + return "Sq. m"; + } + if (value == "squareFeet") { + return "Sq. ft"; + } + return value.toString().replaceAllMapped( + RegExp(r'[A-Z]'), + (match) { + return " ${value[match.start]}"; + }, + ).firstUpperCase(); + } +} diff --git a/lib/Ui/screens/Dashboard/Cubits/property_list_cubit_dashboard.dart b/lib/Ui/screens/Dashboard/Cubits/property_list_cubit_dashboard.dart new file mode 100644 index 0000000..3b178a1 --- /dev/null +++ b/lib/Ui/screens/Dashboard/Cubits/property_list_cubit_dashboard.dart @@ -0,0 +1,54 @@ +import 'package:ebroker/data/model/data_output.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../Models/dashboard_property.dart' as p; +import '../Repository/dashboard_repository.dart'; + +abstract class DashboardPropertyListState {} + +class DashboardPropertyListInitial extends DashboardPropertyListState {} + +class DashboardPropertyListInProgress extends DashboardPropertyListState {} + +class DashboardPropertyListSuccess extends DashboardPropertyListState { + final List list; + final int total; + final int offset; + final bool isLoadingMore; + final bool hasError; + + DashboardPropertyListSuccess({ + required this.list, + required this.total, + required this.offset, + required this.isLoadingMore, + required this.hasError, + }); +} + +class DashboardPropertyListFailiur extends DashboardPropertyListState { + final dynamic error; + DashboardPropertyListFailiur(this.error); +} + +class DashboardPropertyListCubit extends Cubit { + DashboardPropertyListCubit() : super(DashboardPropertyListInitial()); + final DashboardRepositoryIMPL _repositoryIMPL = DashboardRepositoryIMPL(); + void fetch(DashboardPropertyParameters parameters) async { + try { + emit(DashboardPropertyListInProgress()); + + DataOutput dataOutput = + await _repositoryIMPL.fetch(parameters, Parameter(0)); + + emit(DashboardPropertyListSuccess( + hasError: false, + isLoadingMore: false, + list: dataOutput.modelList, + total: dataOutput.total, + offset: 0)); + } catch (e) { + emit(DashboardPropertyListFailiur(e)); + } + } +} diff --git a/lib/Ui/screens/Dashboard/Models/dashboard_property.dart b/lib/Ui/screens/Dashboard/Models/dashboard_property.dart new file mode 100644 index 0000000..a2a063a --- /dev/null +++ b/lib/Ui/screens/Dashboard/Models/dashboard_property.dart @@ -0,0 +1,568 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first, unused_import +// To parse this JSON data, do +// +// final DashboardPropertyModal = DashboardPropertyModalFromMap(jsonString); +import 'dart:convert'; +import 'dart:developer'; + +import 'package:ebroker/utils/Extensions/lib/adaptive_type.dart'; +import 'package:ebroker/utils/Extensions/lib/string.dart'; + +class DashboardPropertyModal { + DashboardPropertyModal( + {this.id, + this.title, + this.customerName, + this.customerEmail, + this.customerNumber, + this.customerProfile, + this.price, + this.category, + this.builtUpArea, + this.plotArea, + this.hectaArea, + this.acre, + this.houseType, + this.furnished, + this.unitType, + this.description, + this.address, + this.clientAddress, + this.properyType, + this.titleImage, + this.postCreated, + this.gallery, + this.totalView, + this.status, + this.state, + this.city, + this.country, + this.addedBy, + this.inquiry, + this.promoted, + this.isFavourite, + this.rentduration, + this.isInterested, + this.favouriteUsers, + this.interestedUsers, + this.totalInterestedUsers, + this.totalFavouriteUsers, + this.parameters, + this.latitude, + this.longitude, + this.threeDImage, + this.advertisment, + this.video, + this.assignedOutdoorFacility, + this.titleimagehash}); + + final int? id; + final String? title; + final String? price; + final String? customerName; + final String? customerEmail; + final String? customerProfile; + final String? customerNumber; + final String? rentduration; + final Categorys? category; + final dynamic builtUpArea; + final dynamic plotArea; + final dynamic hectaArea; + final dynamic acre; + final dynamic houseType; + final dynamic furnished; + final UnitType? unitType; + final String? description; + final String? address; + final String? clientAddress; + String? properyType; + final String? titleImage; + final String? titleimagehash; + final String? postCreated; + final List? gallery; + final int? totalView; + final int? status; + final String? state; + final String? city; + final String? country; + final int? addedBy; + final bool? inquiry; + final bool? promoted; + final int? isFavourite; + final int? isInterested; + final List? favouriteUsers; + final List? interestedUsers; + final int? totalInterestedUsers; + final int? totalFavouriteUsers; + final List? parameters; + final List? assignedOutdoorFacility; + final String? latitude; + final String? longitude; + final String? threeDImage; + final String? video; + final dynamic advertisment; + DashboardPropertyModal copyWith( + {int? id, + String? title, + String? price, + Categorys? category, + dynamic builtUpArea, + dynamic plotArea, + dynamic hectaArea, + dynamic acre, + dynamic houseType, + dynamic furnished, + UnitType? unitType, + String? description, + String? address, + String? clientAddress, + String? properyType, + String? titleImage, + String? postCreated, + List? gallery, + int? totalView, + int? status, + String? state, + String? city, + String? country, + int? addedBy, + bool? inquiry, + bool? promoted, + int? isFavourite, + int? isInterested, + List? favouriteUsers, + List? interestedUsers, + int? totalInterestedUsers, + int? totalFavouriteUsers, + List? parameters, + List? assignedOutdoorFacility, + String? latitude, + String? longitude, + String? threeDimage, + String? video, + dynamic advertisment, + String? rentduration, + String? titleImageHash}) => + DashboardPropertyModal( + id: id ?? this.id, + rentduration: rentduration ?? this.rentduration, + advertisment: advertisment ?? this.advertisment, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + title: title ?? this.title, + price: price ?? this.price, + category: category ?? this.category, + builtUpArea: builtUpArea ?? this.builtUpArea, + plotArea: plotArea ?? this.plotArea, + hectaArea: hectaArea ?? this.hectaArea, + acre: acre ?? this.acre, + houseType: houseType ?? this.houseType, + furnished: furnished ?? this.furnished, + unitType: unitType ?? this.unitType, + description: description ?? this.description, + address: address ?? this.address, + clientAddress: clientAddress ?? this.clientAddress, + properyType: properyType ?? this.properyType, + titleImage: titleImage ?? this.titleImage, + postCreated: postCreated ?? this.postCreated, + gallery: gallery ?? this.gallery, + totalView: totalView ?? this.totalView, + status: status ?? this.status, + state: state ?? this.state, + city: city ?? this.city, + country: country ?? this.country, + addedBy: addedBy ?? this.addedBy, + inquiry: inquiry ?? this.inquiry, + promoted: promoted ?? this.promoted, + isFavourite: isFavourite ?? this.isFavourite, + isInterested: isInterested ?? this.isInterested, + favouriteUsers: favouriteUsers ?? this.favouriteUsers, + interestedUsers: interestedUsers ?? this.interestedUsers, + totalInterestedUsers: + totalInterestedUsers ?? this.totalInterestedUsers, + totalFavouriteUsers: totalFavouriteUsers ?? this.totalFavouriteUsers, + parameters: parameters ?? this.parameters, + threeDImage: threeDimage ?? threeDImage, + video: video ?? this.video, + assignedOutdoorFacility: + assignedOutdoorFacility ?? this.assignedOutdoorFacility, + titleimagehash: titleImageHash ?? titleimagehash); + + factory DashboardPropertyModal.fromMap(Map json) { + "PROPERTY DATA:$json".log; + + return DashboardPropertyModal( + id: json["id"], + rentduration: json['rentduration'], + customerEmail: json['email'], + customerProfile: json['profile'], + customerNumber: json['mobile'], + customerName: json['customer_name'], + video: json['video_link'], + threeDImage: json['threeD_image'], + latitude: json['latitude'].toString(), + longitude: json["longitude"].toString(), + title: json["title"].toString(), + price: json["price"].toString(), + category: json["category"] == null + ? null + : Categorys.fromMap(json["category"]), + builtUpArea: json["built_up_area"], + plotArea: json["plot_area"], + hectaArea: json["hecta_area"], + acre: json["acre"], + houseType: json["house_type"], + furnished: json["furnished"], + advertisment: json['advertisement'], + unitType: json["unit_type"] == null + ? null + : UnitType.fromMap(json["unit_type"]), + description: json["description"], + address: json["address"], + clientAddress: json["client_address"], + properyType: json["property_type"].toString(), + titleImage: json["title_image"], + postCreated: json["post_created"], + gallery: List.from( + (json["gallery"] as List).map((x) => Gallery.fromMap(x))), + totalView: Adapter.forceInt((json["total_view"] as dynamic)), + status: Adapter.forceInt(json["status"]), + state: json["state"], + city: json["city"], + country: json["country"], + addedBy: Adapter.forceInt((json["added_by"] as dynamic)), + inquiry: json["inquiry"], + promoted: json["promoted"], + isFavourite: Adapter.forceInt(json["is_favourite"]), + isInterested: Adapter.forceInt(json["is_interested"]), + favouriteUsers: json["favourite_users"] == null + ? null + : List.from(json["favourite_users"].map((x) => x)), + interestedUsers: json["interested_users"] == null + ? null + : List.from(json["interested_users"].map((x) => x)), + totalInterestedUsers: Adapter.forceInt(json["total_interested_users"]), + totalFavouriteUsers: Adapter.forceInt(json["total_favourite_users"]), + parameters: json["parameters"] == null + ? [] + : List.from((json["parameters"] as List).map((x) { + return Parameter.fromMap(x); + })), + assignedOutdoorFacility: json["assign_facilities"] == null + ? [] + : List.from( + (json["assign_facilities"] as List).map((x) { + return AssignedOutdoorFacility.fromJson(x); + })), + titleimagehash: json['title_image_hash']); + } + + Map toMap() => { + "id": id, + "rentduration": rentduration, + "mobile": customerNumber, + "email": customerEmail, + "customer_name": customerName, + "profile": customerProfile, + "threeD_image": threeDImage, + "title": title, + "latitude": latitude, + "longitude": longitude, + "advertisment": advertisment, + 'video_link': video, + "price": price, + "category": category?.toMap() ?? {}, + "built_up_area": builtUpArea, + "plot_area": plotArea, + "hecta_area": hectaArea, + "acre": acre, + "house_type": houseType, + "furnished": furnished, + "unit_type": unitType?.toMap() ?? {}, + "description": description, + "address": address, + "client_address": clientAddress, + "property_type": properyType, + "title_image": titleImage, + "post_created": postCreated, + "gallery": List.from(gallery?.map((x) => x) ?? []), + "total_view": totalView, + "status": status, + "state": state, + "city": city, + "country": country, + "added_by": addedBy, + "inquiry": inquiry, + "promoted": promoted, + "is_favourite": isFavourite, + "is_interested": isInterested, + "favourite_users": favouriteUsers == null + ? null + : List.from(favouriteUsers?.map((x) => x) ?? []), + "interested_users": interestedUsers == null + ? null + : List.from(interestedUsers?.map((x) => x) ?? []), + "total_interested_users": totalInterestedUsers, + "total_favourite_users": totalFavouriteUsers, + "assign_facilities": assignedOutdoorFacility == null + ? null + : List.from( + assignedOutdoorFacility?.map((e) => e.toJson()) ?? []), + "parameters": parameters == null + ? null + : List.from(parameters?.map((x) => x.toMap()) ?? []), + "title_image_hash": titleimagehash + }; + + @override + String toString() { + return 'DashboardPropertyModal(id: $id,rentduration:$rentduration , title: $title,assigned_facilities:[$assignedOutdoorFacility] advertisment:$advertisment, price: $price, category: $category,, builtUpArea: $builtUpArea, plotArea: $plotArea, hectaArea: $hectaArea, acre: $acre, houseType: $houseType, furnished: $furnished, unitType: $unitType, description: $description, address: $address, clientAddress: $clientAddress, properyType: $properyType, titleImage: $titleImage, title_image_hash: $titleimagehash, postCreated: $postCreated, gallery: $gallery, totalView: $totalView, status: $status, state: $state, city: $city, country: $country, addedBy: $addedBy, inquiry: $inquiry, promoted: $promoted, isFavourite: $isFavourite, isInterested: $isInterested, favouriteUsers: $favouriteUsers, interestedUsers: $interestedUsers, totalInterestedUsers: $totalInterestedUsers, totalFavouriteUsers: $totalFavouriteUsers, parameters: $parameters, latitude: $latitude, longitude: $longitude, threeD_image: $threeDImage, video: $video)'; + } +} + +class Categorys { + Categorys({ + this.id, + this.category, + this.image, + }); + + final int? id; + final String? category; + final String? image; + + Categorys copyWith({ + int? id, + String? category, + String? image, + }) => + Categorys( + id: id ?? this.id, + category: category ?? this.category, + image: image ?? this.image, + ); + + factory Categorys.fromJson(String str) => Categorys.fromMap(json.decode(str)); + + String toJson() => json.encode(toMap()); + + factory Categorys.fromMap(Map json) => Categorys( + id: json["id"], + category: json["category"], + image: json["image"], + ); + + Map toMap() => { + "id": id, + "category": category, + "image": image, + }; +} + +class Parameter { + Parameter({ + this.id, + this.name, + this.typeOfParameter, + this.typeValues, + this.image, + this.value, + }); + + final int? id; + final String? name; + final String? typeOfParameter; + final dynamic typeValues; + final String? image; + final dynamic value; + + Parameter copyWith({ + int? id, + String? name, + String? typeOfParameter, + dynamic typeValues, + String? image, + dynamic value, + }) => + Parameter( + id: id ?? this.id, + name: name ?? this.name, + typeOfParameter: typeOfParameter ?? this.typeOfParameter, + typeValues: typeValues ?? this.typeValues, + image: image ?? this.image, + value: value ?? this.value, + ); + + static dynamic ifListConvertToString(dynamic value) { + if (value is List) { + return value.join(","); + } + return value; + } + + factory Parameter.fromMap(Map json) { + return Parameter( + id: json["id"], + name: json["name"], + typeOfParameter: json["type_of_parameter"], + typeValues: json["type_values"], + image: json["image"], + value: ifListConvertToString(json['value']), + ); + } + + Map toMap() => { + "id": id, + "name": name, + "type_of_parameter": typeOfParameter, + "type_values": typeValues, + "image": image, + "value": value, + }; + + @override + String toString() { + return 'Parameter(id: $id, name: $name, typeOfParameter: $typeOfParameter, typeValues: $typeValues, image: $image, value: $value)'; + } +} + +class UnitType { + UnitType({ + this.id, + this.measurement, + }); + + final int? id; + final String? measurement; + + UnitType copyWith({ + int? id, + String? measurement, + }) => + UnitType( + id: id ?? this.id, + measurement: measurement ?? this.measurement, + ); + + factory UnitType.fromJson(String str) => UnitType.fromMap(json.decode(str)); + + String toJson() => json.encode(toMap()); + + factory UnitType.fromMap(Map json) => UnitType( + id: json["id"], + measurement: json["measurement"], + ); + + Map toMap() => { + "id": id, + "measurement": measurement, + }; +} + +class Gallery { + final int id; + final String image; + final String imageUrl; + final bool? isVideo; + Gallery( + {required this.id, + required this.image, + required this.imageUrl, + this.isVideo}); + + Gallery copyWith({ + int? id, + String? image, + String? imageUrl, + }) { + return Gallery( + id: id ?? this.id, + image: image ?? this.image, + imageUrl: imageUrl ?? this.imageUrl, + ); + } + + Map toMap() { + return { + 'id': id, + 'image': image, + 'image_url': imageUrl, + }; + } + + factory Gallery.fromMap(Map map) { + return Gallery( + id: map['id'] as int, + image: map['image'] as String, + imageUrl: map['image_url'] ?? "", + ); + } + + String toJson() => json.encode(toMap()); + + factory Gallery.fromJson(String source) => + Gallery.fromMap(json.decode(source) as Map); + + @override + String toString() => 'Gallery(id: $id, image: $image, imageUrl: $imageUrl)'; + + @override + bool operator ==(covariant Gallery other) { + if (identical(this, other)) return true; + + return other.id == id && other.image == image && other.imageUrl == imageUrl; + } + + @override + int get hashCode => id.hashCode ^ image.hashCode ^ imageUrl.hashCode; +} + +class AssignedOutdoorFacility { + int? id; + int? propertyId; + int? facilityId; + int? distance; + String? image; + String? name; + String? createdAt; + String? updatedAt; + + AssignedOutdoorFacility( + {this.id, + this.propertyId, + this.facilityId, + this.distance, + this.createdAt, + this.name, + this.image, + this.updatedAt}); + + AssignedOutdoorFacility.fromJson(Map json) { + id = json['id']; + propertyId = json['property_id']; + facilityId = json['facility_id']; + distance = json['distance']; + createdAt = json['created_at']; + image = json['image']; + name = json['name']; + updatedAt = json['updated_at']; + } + + Map toJson() { + final Map data = Map(); + data['id'] = this.id; + data['property_id'] = this.propertyId; + data['facility_id'] = this.facilityId; + data['distance'] = this.distance; + data['created_at'] = this.createdAt; + data['updated_at'] = this.updatedAt; + data['image'] = image; + data['name'] = name; + return data; + } + + @override + String toString() { + return 'AssignedOutdoorFacility{id: $id, propertyId: $propertyId, facilityId: $facilityId, distance: $distance, image: $image, name: $name, createdAt: $createdAt, updatedAt: $updatedAt}'; + } +} diff --git a/lib/Ui/screens/Dashboard/Repository/dashboard_repository.dart b/lib/Ui/screens/Dashboard/Repository/dashboard_repository.dart new file mode 100644 index 0000000..2bfb8ea --- /dev/null +++ b/lib/Ui/screens/Dashboard/Repository/dashboard_repository.dart @@ -0,0 +1,70 @@ +import 'package:ebroker/Ui/screens/Dashboard/Models/dashboard_property.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/utils/api.dart'; +import 'package:ebroker/utils/constant.dart'; + +mixin DashboardPropertyParameters { + abstract Map parameters; +} + +class Parameter { + final int offset; + + Parameter(this.offset); +} + +class All with DashboardPropertyParameters { + @override + Map parameters = {}; +} + +class Sell with DashboardPropertyParameters { + @override + Map parameters = {"property_type": "0"}; +} + +class Rent with DashboardPropertyParameters { + @override + Map parameters = {"property_type": "1"}; +} + +class Sold with DashboardPropertyParameters { + @override + Map parameters = {"property_type": "2"}; +} + +class Rented with DashboardPropertyParameters { + @override + Map parameters = {"property_type": "3"}; +} + +abstract class DashboardRepository { + Future> fetch( + DashboardPropertyParameters p, Parameter parameter); +} + +class DashboardRepositoryIMPL extends DashboardRepository { + @override + Future> fetch( + DashboardPropertyParameters p, Parameter parameter) async { + Map parameters = {}; + parameters.addAll({ + "offset": parameter.offset, + "limit": Constant.loadLimit, + }); + + parameters.addAll(p.parameters); + + Map result = + await Api.post(url: Api.apiGetProprty, parameter: parameters); + + List list = (result['data'] as List).map((e) { + return DashboardPropertyModal.fromMap(e); + }).toList(); + + return DataOutput( + total: result['total'] ?? 0, + modelList: list, + ); + } +} diff --git a/lib/Ui/screens/Dashboard/dashbord.dart b/lib/Ui/screens/Dashboard/dashbord.dart new file mode 100644 index 0000000..ab93cc0 --- /dev/null +++ b/lib/Ui/screens/Dashboard/dashbord.dart @@ -0,0 +1,242 @@ +import 'package:ebroker/Ui/screens/Dashboard/Cubits/property_list_cubit_dashboard.dart'; +import 'package:ebroker/Ui/screens/Dashboard/Repository/dashboard_repository.dart'; +import 'package:ebroker/Ui/screens/Dashboard/property_list.dart'; +import 'package:ebroker/Ui/screens/Dashboard/widgets/mTabbar.dart'; +import 'package:ebroker/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +import '../../../utils/AppIcon.dart'; + +class DashboardScreen extends StatefulWidget { + const DashboardScreen({Key? key}) : super(key: key); + static Route route(RouteSettings settings) { + return BlurredRouter( + builder: (context) { + return BlocProvider( + create: (context) { + return DashboardPropertyListCubit(); + } + ,child: const DashboardScreen() + ); + }, + ); + } + + @override + State createState() => _DashboardScreenState(); +} + +class _DashboardScreenState extends State + with SingleTickerProviderStateMixin { + final PageController _pageController = PageController(); + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + backgroundColor: context.color.secondaryColor, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Material( + elevation: 3, + child: Container( + color: context.color.primaryColor, + child: Padding( + padding: const EdgeInsets.all(15.0), + child: Column( + children: [ + SizedBox( + height: 150, + child: Row( + children: [ + Expanded( + child: CountsCard( + number: "120", + title: "Total Property", + icon: AppIcons.properties, + onTap: () {}, + ), + ), + const SizedBox( + width: 5, + ), + Expanded( + child: CountsCard( + number: "120", + title: "Total Views", + icon: AppIcons.properties, + materialIcon: Icons.remove_red_eye, + onTap: () {}, + ), + ) + ], + ), + ), + const SizedBox( + height: 5, + ), + SizedBox( + height: 150, + width: context.screenWidth, + child: CountsCard( + number: "120", + icon: AppIcons.favorites, + // materialIcon: Icons.favorite, + title: "Total Favorites", + onTap: () {}, + ), + ), + ], + ), + ), + ), + ), + const SizedBox( + height: 5, + ), + SizedBox( + height: 40, + child: MTabBar( + onChange: (page) { + DashboardRepositoryIMPL().fetch(All(), Parameter(0)); + }, + controller: _pageController, + padding: const EdgeInsets.symmetric(horizontal: 15), + activeTabDecoration: RoundedMTabDecoration( + buttonColor: context.color.tertiaryColor, + borderColor: widgetsBorderColorLight, + tColor: context.color.buttonColor, + radius: 10), + deactiveTabDecoration: RoundedMTabDecoration( + borderColor: Colors.transparent, + radius: 10, + ), + tabs: [ + MTab(title: "All"), + MTab(title: "Sell"), + MTab(title: "Rent"), + MTab(title: "Sold"), + MTab(title: "Rented"), + MTab(title: "Featured"), + // MTab(title: "Featured"), + ]), + ), + const SizedBox( + height: 12, + ), + Expanded( + child: MTabView(controller: _pageController, pages: [ + BlocProvider( + create: (context) => DashboardPropertyListCubit(), + child: PropertyListDashboard(parameters: All())), + BlocProvider( + create: (context) => DashboardPropertyListCubit(), + child: PropertyListDashboard(parameters: Sell())), + BlocProvider( + create: (context) => DashboardPropertyListCubit(), + child: PropertyListDashboard(parameters: Rent())), + BlocProvider( + create: (context) => DashboardPropertyListCubit(), + child: PropertyListDashboard(parameters: Sold())), + BlocProvider( + create: (context) => DashboardPropertyListCubit(), + child: PropertyListDashboard(parameters: Rented())), + ]), + ) + ], + )), + ); + } +} + +class CountsCard extends StatelessWidget { + final String title; + final String number; + final String icon; + final IconData? materialIcon; + + final void Function() onTap; + + const CountsCard({ + super.key, + required this.title, + required this.number, + required this.onTap, + required this.icon, + this.materialIcon, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + onTap.call(); + }, + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: context.color.secondaryColor, + ), + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () {}, + child: Container( + decoration: BoxDecoration( + border: + Border.all(color: context.color.borderColor, width: 1.5), + borderRadius: BorderRadius.circular(10)), + child: Padding( + padding: const EdgeInsets.all(15.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Row( + children: [ + if (materialIcon != null) ...[ + Icon(materialIcon!, + size: context.font.xxLarge, + color: context.color.tertiaryColor) + ] else ...[ + SvgPicture.asset( + icon, + height: context.font.xxLarge, + width: context.font.xxLarge, + color: context.color.tertiaryColor, + ), + ], + const SizedBox( + width: 5, + ), + Text(number).size(context.font.xxLarge), + ], + ), + Text(title).size(context.font.large), + const Divider(), + const Row( + children: [ + Text("VIEW"), + Spacer(), + Icon(Icons.chevron_right) + ], + ), + ], + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/Dashboard/property_list.dart b/lib/Ui/screens/Dashboard/property_list.dart new file mode 100644 index 0000000..ffef3fc --- /dev/null +++ b/lib/Ui/screens/Dashboard/property_list.dart @@ -0,0 +1,265 @@ +import 'package:ebroker/Ui/screens/Dashboard/Cubits/property_list_cubit_dashboard.dart'; +import 'package:ebroker/Ui/screens/Dashboard/Models/dashboard_property.dart'; +import 'package:ebroker/Ui/screens/Dashboard/Repository/dashboard_repository.dart'; +import 'package:ebroker/Ui/screens/widgets/Erros/no_data_found.dart'; +import 'package:ebroker/Ui/screens/widgets/shimmerLoadingContainer.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/string_extenstion.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +import '../../../app/routes.dart'; +import '../../../data/cubits/outdoorfacility/fetch_outdoor_facility_list.dart'; +import '../../../data/cubits/property/delete_property_cubit.dart'; +import '../../../data/model/category.dart'; +import '../../../utils/AppIcon.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/helper_utils.dart'; +import '../widgets/blurred_dialoge_box.dart'; + +class PropertyListDashboard extends StatefulWidget { + final DashboardPropertyParameters parameters; + const PropertyListDashboard({Key? key, required this.parameters}) + : super(key: key); + + @override + State createState() => _PropertyListDashboardState(); +} + +class _PropertyListDashboardState extends State + with AutomaticKeepAliveClientMixin { + @override + void initState() { + context.read().fetch(widget.parameters); + super.initState(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + + return BlocBuilder( + builder: (context, state) { + if (state is DashboardPropertyListInProgress) { + return ListView.builder( + itemCount: 5, + padding: const EdgeInsets.all(15), + itemBuilder: (context, index) { + return const Padding( + padding: EdgeInsets.symmetric(vertical: 3), + child: CustomShimmer( + height: 100, + ), + ); + }, + ); + } + if (state is DashboardPropertyListSuccess) { + if (state.list.isEmpty) { + return const SizedBox( + width: 100, + height: 100, + child: NoDataFound( + height: 100, + ), + ); + } + return ListView.builder( + itemCount: state.list.length, + itemBuilder: (context, index) { + DashboardPropertyModal model = state.list[index]; + return Padding( + padding: + const EdgeInsets.symmetric(horizontal: 15, vertical: 5), + child: Container( + height: 65, + child: Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Image.network( + model.titleImage ?? "", + width: 65, + height: 65, + fit: BoxFit.cover, + ), + ), + const SizedBox( + width: 8, + ), + Expanded( + flex: 5, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text((model.title?.toString().firstUpperCase()) ?? "") + .setMaxLines(lines: 1), + Text((model.description?.toString().firstUpperCase()) ?? "") + .size(context.font.small) + .setMaxLines(lines: 1), + Row( + children: [ + Icon( + Icons.remove_red_eye, + color: context.color.textLightColor, + size: (context.font.small), + ), + const SizedBox( + width: 5, + ), + Text(model.totalView.toString().priceFormate()) + .size(context.font.small), + const SizedBox( + width: 5, + ), + Icon( + Icons.favorite, + color: context.color.textLightColor, + size: (context.font.small), + ), + const SizedBox( + width: 5, + ), + Text(model.totalFavouriteUsers + .toString() + .priceFormate()) + .size(context.font.small) + ], + ), + ], + ), + ), + Expanded( + flex: 5, + child: Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + IconButton( + constraints: BoxConstraints(), + onPressed: () { + context + .read() + .fetch(); + Constant.addProperty.addAll({ + "category": Category( + category: model.category!.category, + id: model.category?.id!.toString(), + image: model.category?.image, + parameterTypes: { + "parameters": model.parameters + ?.map((e) => e.toMap()) + .toList() + }, + ) + }); + // log("GOING THROW IT ${property?.parameters}"); + Navigator.pushNamed(context, + Routes.addPropertyDetailsScreen, + arguments: { + "details": { + "id": model.id, + "catId": model.category?.id, + "propType": model.properyType, + "name": model.title, + "desc": model.description, + "city": model.city, + "state": model.state, + "country": model.country, + "latitude": model.latitude, + "longitude": model.longitude, + "address": model.address, + "client": model.clientAddress, + "price": model.price, + 'parms': model.parameters, + "images": model.gallery + ?.map((e) => e.imageUrl) + .toList(), + "rentduration": + model.rentduration, + "assign_facilities": + model.assignedOutdoorFacility, + "titleImage": model.titleImage + } + }); + }, + icon: Icon( + Icons.edit, + // size: 16, + color: context.color.textLightColor, + )), + IconButton( + constraints: BoxConstraints(), + // padding: EdgeInsets.zero, + onPressed: () {}, + // splashRadius: 15, + icon: SvgPicture.asset( + AppIcons.promoted, + // width: 16, + // height: 16, + color: context.color.textLightColor, + )), + IconButton( + constraints: BoxConstraints(), + // padding: EdgeInsets.zero, + autofocus: false, + onPressed: () async { + var delete = + await UiUtils.showBlurredDialoge( + context, + dialoge: BlurredDialogBox( + title: UiUtils.translate( + context, + "deleteBtnLbl", + ), + content: Text( + UiUtils.translate(context, + "deletepropertywarning"), + ), + ), + ); + if (delete == true) { + Future.delayed( + Duration.zero, + () { + if (Constant.isDemoModeOn) { + HelperUtils.showSnackBarMessage( + context, + UiUtils.translate(context, + "thisActionNotValidDemo")); + } else { + context + .read() + .delete(model.id!); + } + }, + ); + } + }, + // splashRadius: 15, + icon: SvgPicture.asset( + AppIcons.delete, + // width: 16, + // height: 16, + color: context.color.textLightColor, + )), + ], + ), + )) + ], + ), + ), + ); + }); + } + return Container(); + }); + } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; +} diff --git a/lib/Ui/screens/Dashboard/widgets/mTabbar.dart b/lib/Ui/screens/Dashboard/widgets/mTabbar.dart new file mode 100644 index 0000000..f9564a7 --- /dev/null +++ b/lib/Ui/screens/Dashboard/widgets/mTabbar.dart @@ -0,0 +1,220 @@ +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; + +class MTabBar extends StatefulWidget { + final List tabs; + final MTabDecoration? activeTabDecoration; + final MTabDecoration? deactiveTabDecoration; + final EdgeInsetsGeometry? padding; + final PageController controller; + final Function(int page) onChange; + MTabBar( + {Key? key, + required this.tabs, + this.activeTabDecoration, + this.deactiveTabDecoration, + this.padding, + required this.controller, + required this.onChange}) + : super(key: key) {} + + @override + State createState() => _MTabBarState(); +} + +class _MTabBarState extends State { + int _activeTabindex = 0; + Map? selectedTab; + + ScrollController _scrollController = ScrollController(); + GlobalKey? selectedKey; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + List<_IndexedMTab> indexxedList = + List.generate(widget.tabs.length, (index) { + return _IndexedMTab(index, widget.tabs[index]); + }).toList(); + return ListView( + shrinkWrap: true, + controller: _scrollController, + padding: widget.padding, + scrollDirection: Axis.horizontal, + children: [ + ...indexxedList.map((_IndexedMTab e) { + MTabDecoration? decoration = _activeTabindex == e.index + ? widget.activeTabDecoration + : widget.deactiveTabDecoration; + GlobalKey _key = GlobalKey(); + if (decoration == null) { + return MaterialButton( + key: _key, + onPressed: () { + setState(() { + Map info = + UiUtils.getWidgetInfo(context, _key); + selectedTab = info; + + selectedKey = _key; + _activeTabindex = e.index; + widget.controller.jumpToPage(e.index); + widget.onChange(e.index); + + setState(() {}); + }); + }, + child: Text(e.tab.title), + ); + } + return decoration._buildButton( + _key, + context, + child: Text(e.tab.title), + onPressed: (info) { + selectedTab = info; + widget.controller.jumpToPage(e.index); + _activeTabindex = e.index; + widget.onChange(e.index); + setState(() {}); + }, + ); + }).toList() + ], + ); + } +} + +class MTab { + final String title; + final MTabDecoration? activeDecoration; + final MTabDecoration? deactiveDecoration; + MTab({required this.title, this.activeDecoration, this.deactiveDecoration}); +} + +class _IndexedMTab { + final int index; + final MTab tab; + + _IndexedMTab(this.index, this.tab); +} + +class MTabDecoration { + final Color? color; + final Color? textColor; + final double? elevation; + final EdgeInsetsGeometry? padding; + final BorderRadius? borderRadius; + final MaterialStateProperty? overlayColor; + final BorderSide? side; + final MaterialTapTargetSize? tapTargetSize; + final Duration? animationDuration; + final Clip? clipBehavior; + final FocusNode? focusNode; + final bool? autofocus; + + MTabDecoration({ + this.color, + this.textColor, + this.elevation, + this.padding, + this.borderRadius, + this.overlayColor, + this.side, + this.tapTargetSize, + this.animationDuration, + this.clipBehavior, + this.focusNode, + this.autofocus, + }); + + // Helper method to create the MaterialButton + MaterialButton _buildButton(GlobalKey key, BuildContext context, + {required Widget child, Function(Map info)? onPressed}) { + return MaterialButton( + key: key, + onPressed: () { + Map info = UiUtils.getWidgetInfo(context, key); + + onPressed?.call(info); + }, + child: child, + color: color, + textColor: textColor, + elevation: elevation, + padding: padding, + shape: RoundedRectangleBorder( + borderRadius: borderRadius ?? BorderRadius.circular(8.0), + side: side ?? BorderSide.none, + ), + materialTapTargetSize: tapTargetSize, + animationDuration: animationDuration, + clipBehavior: clipBehavior ?? Clip.antiAlias, + focusNode: focusNode, + onHighlightChanged: (isHighlighted) { + // You can add any additional logic here when the button is highlighted. + }, + onLongPress: () { + // You can add any additional logic here when the button is long-pressed. + }, + mouseCursor: SystemMouseCursors.click, + enableFeedback: true, + visualDensity: VisualDensity.adaptivePlatformDensity, + focusColor: Colors.transparent, + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + splashColor: Colors.transparent, + disabledColor: Colors.transparent, + focusElevation: 0.0, + hoverElevation: 0.0, + highlightElevation: 0.0, + ); + } +} + +class RoundedMTabDecoration extends MTabDecoration { + final Color borderColor; + final double radius; + final Color? tColor; + final Color? buttonColor; + + RoundedMTabDecoration({ + required this.radius, + required this.borderColor, + this.tColor, + this.buttonColor, + }); + @override + BorderRadius? get borderRadius => BorderRadius.circular(radius); + @override + Color? get textColor => tColor ?? super.textColor; + @override + Color? get color => buttonColor ?? super.color; + + @override + BorderSide? get side => BorderSide(width: 1.5, color: borderColor); +} + +class MTabView extends StatefulWidget { + final PageController controller; + final List pages; + const MTabView({Key? key, required this.controller, required this.pages}) + : super(key: key); + + @override + State createState() => _MTabViewState(); +} + +class _MTabViewState extends State { + @override + Widget build(BuildContext context) { + return PageView( + controller: widget.controller, + children: widget.pages, + ); + } +} diff --git a/lib/Ui/screens/Personalized/.DS_Store b/lib/Ui/screens/Personalized/.DS_Store new file mode 100644 index 0000000..62bc80c Binary files /dev/null and b/lib/Ui/screens/Personalized/.DS_Store differ diff --git a/lib/Ui/screens/Personalized/personalized_property_screen.dart b/lib/Ui/screens/Personalized/personalized_property_screen.dart new file mode 100644 index 0000000..72d7652 --- /dev/null +++ b/lib/Ui/screens/Personalized/personalized_property_screen.dart @@ -0,0 +1,253 @@ +// import 'dart:developer'; + +import 'package:ebroker/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import 'package:ebroker/data/Repositories/location_repository.dart'; +import 'package:ebroker/data/model/category.dart'; +import 'package:ebroker/data/model/outdoor_facility.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/Extensions/lib/list.dart'; +import 'package:ebroker/utils/string_extenstion.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_typeahead/flutter_typeahead.dart'; + +import '../../../data/Repositories/personalized_feed_repository.dart'; +import '../../../data/cubits/Personalized/add_update_personalized_interest.dart'; +import '../../../data/cubits/Personalized/fetch_personalized_properties.dart'; +import '../../../data/helper/widgets.dart'; +import '../../../data/model/google_place_model.dart'; +import '../../../utils/helper_utils.dart'; + +part 'segments/choose_category.dart'; +part 'segments/choose_nearby.dart'; +part 'segments/other_interest.dart'; + +enum PersonalizedVisitType { FirstTime, Normal } + +class PersonalizedPropertyScreen extends StatefulWidget { + final PersonalizedVisitType type; + + const PersonalizedPropertyScreen({Key? key, required this.type}) + : super(key: key); + + static Route route(RouteSettings settings) { + final args = settings.arguments as Map?; + return BlurredRouter( + builder: (context) => PersonalizedPropertyScreen( + type: args?['type'], + ), + ); + } + + @override + State createState() => + _PersonalizedPropertyScreenState(); +} + +class _PersonalizedPropertyScreenState + extends State { + List selectedCategoryId = personalizedInterestSettings.categoryIds; + List selectedNearbyPlacesId = []; + int selectedPage = 0; + RangeValues? _selectedPriceRange; + String selectedLocation = ""; + List selectedPropertyType = []; + + @override + void initState() { + context.read().fetchIfFailed(); + super.initState(); + } + + final PageController _pageController = PageController(); + + @override + void dispose() { + _pageController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.backgroundColor, + bottomNavigationBar: _buildBottomNavigationBar(), + body: BlocConsumer(listener: (context, state) { + if (state is AddUpdatePersonalizedInterestInProgress) { + Widgets.showLoader(context); + } + if (state is AddUpdatePersonalizedInterestFail) { + Widgets.hideLoder(context); + HelperUtils.showSnackBarMessage( + context, "unableToSave".translate(context)); + } + if (state is AddUpdatePersonalizedInterestSuccess) { + Widgets.hideLoder(context); + context.read().fetch( + forceRefresh: true, + ); + if (widget.type == PersonalizedVisitType.FirstTime) { + Future.delayed( + Duration.zero, + () { + HelperUtils.showSnackBarMessage( + context, + "successfullyAdded".translate(context), + type: MessageType.success, + ); + HelperUtils.killPreviousPages( + context, + Routes.main, + {"from": "login"}, + ); + }, + ); + } else { + HelperUtils.showSnackBarMessage( + context, + "successfullySaved".translate(context), + type: MessageType.success, + ); + Navigator.pop(context); + } + } + }, builder: (context, state) { + return SafeArea( + child: SizedBox( + width: context.screenWidth, + child: PageView( + controller: _pageController, + physics: const NeverScrollableScrollPhysics(), + onPageChanged: _onPageChanged, + children: [ + CategoryInterestChoose( + controller: _pageController, + type: widget.type, + onInteraction: _onCategoryInteraction, + ), + NearbyInterest( + controller: _pageController, + type: widget.type, + onInteraction: _onNearbyInteraction, + ), + OtherInterests( + type: widget.type, + onInteraction: _onOtherInterestsInteraction, + ), + ], + ), + ), + ); + }), + ); + } + + Widget _buildBottomNavigationBar() { + return BottomAppBar( + color: context.color.primaryColor, + child: Row( + children: [ + if (selectedPage > 0) + Expanded( + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB(15, 5, 5, 5), + child: _buildNavigationButton( + onPressed: selectedCategoryId.isEmpty + ? null + : () { + _pageController.animateToPage( + --selectedPage, + duration: const Duration(milliseconds: 500), + curve: Curves.linear, + ); + }, + buttonTextColor: (selectedCategoryId.isEmpty + ? context.color.textColorDark + : null), + label: "previouslbl".translate(context), + color: context.color.tertiaryColor.withOpacity(0.5), + ), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB(5, 5, 15, 5), + child: _buildNavigationButton( + onPressed: selectedCategoryId.isEmpty + ? null + : () { + if (selectedPage < 2) { + _pageController.animateToPage( + ++selectedPage, + duration: const Duration(milliseconds: 500), + curve: Curves.easeIn, + ); + } else { + _updatePersonalizedFeed(); + } + }, + label: "next".translate(context), + buttonTextColor: selectedCategoryId.isEmpty + ? context.color.textColorDark + : null, + color: context.color.tertiaryColor, + ), + ), + ), + ], + ), + ); + } + + Widget _buildNavigationButton( + {VoidCallback? onPressed, + required String label, + required Color color, + Color? buttonTextColor}) { + return MaterialButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + onPressed: onPressed, + height: 48, + color: color, + child: Text(label).color(buttonTextColor ?? context.color.buttonColor), + ); + } + + void _updatePersonalizedFeed() async { + context.read().addOrUpdate( + action: PersonalizedFeedAction.add, + categoryIds: selectedCategoryId, + outdoorFacilityList: selectedNearbyPlacesId, + priceRange: _selectedPriceRange, + city: selectedLocation, + selectedPropertyType: selectedPropertyType); + } + + void _onPageChanged(int value) { + selectedPage = value; + setState(() {}); + } + + void _onCategoryInteraction(List id) { + selectedCategoryId = id; + setState(() {}); + } + + void _onNearbyInteraction(List idlist) { + selectedNearbyPlacesId = idlist; + setState(() {}); + } + + void _onOtherInterestsInteraction( + RangeValues priceRange, String location, List propertyTypes) { + _selectedPriceRange = priceRange; + selectedLocation = location; + selectedPropertyType = propertyTypes; + + setState(() {}); + } +} diff --git a/lib/Ui/screens/Personalized/segments/choose_category.dart b/lib/Ui/screens/Personalized/segments/choose_category.dart new file mode 100644 index 0000000..8d5ee4d --- /dev/null +++ b/lib/Ui/screens/Personalized/segments/choose_category.dart @@ -0,0 +1,101 @@ +part of '../personalized_property_screen.dart'; + +class CategoryInterestChoose extends StatefulWidget { + final PageController controller; + final PersonalizedVisitType type; + final Function(List selectedCategoryId) onInteraction; + const CategoryInterestChoose( + {super.key, + required this.controller, + required this.onInteraction, + required this.type}); + + @override + State createState() => _CategoryInterestChooseState(); +} + +class _CategoryInterestChooseState extends State + with AutomaticKeepAliveClientMixin { + List selectedCategoryId = personalizedInterestSettings.categoryIds; + + @override + Widget build(BuildContext context) { + bool isFirstTime = widget.type == PersonalizedVisitType.FirstTime; + super.build(context); + return Column( + children: [ + const SizedBox( + height: 25, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Spacer( + flex: 2, + ), + Text("chooseYourInterest".translate(context)) + .color(context.color.textColorDark) + .size(context.font.xxLarge) + .centerAlign(), + Spacer( + flex: isFirstTime ? 1 : 2, + ), + if (isFirstTime) + GestureDetector( + onTap: () { + HelperUtils.killPreviousPages( + context, Routes.main, {"from": "login"}); + }, + child: Chip( + label: Text("skip".translate(context)) + .color(context.color.buttonColor))), + const SizedBox( + width: 14, + ), + ], + ), + const SizedBox( + height: 25, + ), + Wrap( + children: List.generate( + (context.watch().getCategories().length), + (index) { + Category categorie = + context.watch().getCategories()[index]; + bool isSelected = + selectedCategoryId.contains(int.parse(categorie.id!)); + return Padding( + padding: const EdgeInsets.all(3.0), + child: GestureDetector( + onTap: () { + selectedCategoryId.addOrRemove(int.parse(categorie.id!)); + widget.onInteraction.call(selectedCategoryId); + setState(() {}); + }, + child: Chip( + shape: StadiumBorder( + side: BorderSide(color: context.color.borderColor)), + backgroundColor: isSelected + ? context.color.tertiaryColor + : context.color.secondaryColor, + padding: const EdgeInsets.all(5), + label: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(categorie.category.toString()).color( + isSelected + ? context.color.buttonColor + : context.color.textColorDark), + )), + ), + ); + }), + ) + ], + ); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/Ui/screens/Personalized/segments/choose_nearby.dart b/lib/Ui/screens/Personalized/segments/choose_nearby.dart new file mode 100644 index 0000000..359fe78 --- /dev/null +++ b/lib/Ui/screens/Personalized/segments/choose_nearby.dart @@ -0,0 +1,128 @@ +part of '../personalized_property_screen.dart'; + +class NearbyInterest extends StatefulWidget { + final PageController controller; + final PersonalizedVisitType type; + final Function(List selectedNearbyPlacesIds) onInteraction; + const NearbyInterest({ + super.key, + required this.controller, + required this.onInteraction, + required this.type, + }); + + @override + State createState() => _NearbyInterestState(); +} + +class _NearbyInterestState extends State + with AutomaticKeepAliveClientMixin { + List selectedIds = personalizedInterestSettings.outdoorFacilityIds; + @override + void initState() { + context.read().fetchIfFailed(); + Future.delayed( + Duration(seconds: 1), + () { + setState(() {}); + }, + ); + super.initState(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + bool isFirstTime = widget.type == PersonalizedVisitType.FirstTime; + var facilityList = context.watch().getList(); + int facilityLength = facilityList.length; + FetchOutdoorFacilityListState state = + context.watch().state; + return SingleChildScrollView( + child: Column( + children: [ + const SizedBox( + height: 25, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Spacer( + flex: 2, + ), + Text("chooseNearbyPlaces".translate(context)) + .color(context.color.textColorDark) + .size(context.font.xxLarge) + .centerAlign(), + Spacer( + flex: isFirstTime ? 1 : 2, + ), + if (isFirstTime) + GestureDetector( + onTap: () { + HelperUtils.killPreviousPages( + context, + Routes.main, + {"from": "login"}, + ); + }, + child: Chip( + label: Text("skip".translate(context)) + .color(context.color.buttonColor))), + // const Chip(label: Text("Skip")), + const SizedBox( + width: 14, + ), + ], + ), + const SizedBox( + height: 10, + ), + Text("getRecommandation".translate(context)) + .color(context.color.textColorDark.withOpacity(0.6)) + .centerAlign() + .size(context.font.small), + const SizedBox( + height: 15, + ), + if (state is FetchOutdoorFacilityListInProgress) ...{ + UiUtils.progress() + }, + Wrap( + children: List.generate((facilityLength), (index) { + OutdoorFacility facility = facilityList[index]; + return Padding( + padding: const EdgeInsets.all(3.0), + child: GestureDetector( + onTap: () { + selectedIds.addOrRemove(facility.id!); + widget.onInteraction.call(selectedIds); + setState(() {}); + }, + child: Chip( + shape: StadiumBorder( + side: BorderSide(color: context.color.borderColor)), + backgroundColor: selectedIds.contains(facility.id!) + ? context.color.tertiaryColor + : context.color.secondaryColor, + padding: const EdgeInsets.all(5), + label: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(facility.name.toString()).color( + selectedIds.contains(facility.id!) + ? context.color.buttonColor + : context.color.textColorDark), + )), + ), + ); + }), + ) + ], + ), + ); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/Ui/screens/Personalized/segments/other_interest.dart b/lib/Ui/screens/Personalized/segments/other_interest.dart new file mode 100644 index 0000000..9f50807 --- /dev/null +++ b/lib/Ui/screens/Personalized/segments/other_interest.dart @@ -0,0 +1,352 @@ +part of '../personalized_property_screen.dart'; + +class OtherInterests extends StatefulWidget { + final PersonalizedVisitType type; + final Function( + RangeValues priceRange, String location, List propertyType) + onInteraction; + const OtherInterests( + {super.key, required this.onInteraction, required this.type}); + + @override + State createState() => _OtherInterestsState(); +} + +class _OtherInterestsState extends State { + String selectedLocation = ""; + final TextEditingController _controller = TextEditingController(); + late final min = personalizedInterestSettings.priceRange.first; + late final max = personalizedInterestSettings.priceRange.last; + RangeValues _priceRangeValues = const RangeValues(0, 100); + RangeValues _selectedRangeValues = const RangeValues(0, 50); + + GooglePlaceRepository googlePlaceRepository = GooglePlaceRepository(); + List selectedPropertyType = [1, 2]; + @override + void initState() { + Future.delayed( + Duration.zero, + () { + selectedPropertyType = personalizedInterestSettings.propertyType; + + if (personalizedInterestSettings.city.isNotEmpty) { + _controller.text = personalizedInterestSettings.city.firstUpperCase(); + selectedLocation = personalizedInterestSettings.city; + } + + widget.onInteraction + .call(_selectedRangeValues, selectedLocation, selectedPropertyType); + setState(() {}); + FetchSystemSettingsState state = + context.read().state; + if (state is FetchSystemSettingsSuccess) { + var settingsData = state.settings['data']; + var minPrice = double.parse(settingsData['min_price']); + var maxPrice = double.parse(settingsData['max_price']); + _priceRangeValues = RangeValues(minPrice, maxPrice); + if (min != 0.0 && max != 0.0) { + _selectedRangeValues = RangeValues(min, max); + } else { + _selectedRangeValues = RangeValues(minPrice, maxPrice / 4); + } + } + }, + ); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + bool isFirstTime = widget.type == PersonalizedVisitType.FirstTime; + + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.fromLTRB(20.0, 0, 20, 20), + child: Column( + children: [ + const SizedBox( + height: 25, + ), + Row( + children: [ + const Spacer( + flex: 2, + ), + Text("selectCityYouWantToSee".translate(context)) + .color(context.color.textColorDark) + .size(context.font.extraLarge) + .centerAlign(), + Spacer( + flex: isFirstTime ? 1 : 2, + ), + if (isFirstTime) + GestureDetector( + onTap: () { + HelperUtils.killPreviousPages( + context, Routes.main, {"from": "login"}); + }, + child: Chip( + label: Text("skip".translate(context)) + .color(context.color.buttonColor))), + ], + ), + const SizedBox( + height: 25, + ), + buildCitySearchTextField(context), + const SizedBox( + height: 10, + ), + Align( + alignment: Alignment.centerLeft, + child: Row( + children: [ + Text("selectedLocation".translate(context)) + .color(context.color.textColorDark.withOpacity(0.6)), + Expanded(child: Text(selectedLocation)) + ], + )), + const SizedBox( + height: 20, + ), + Text("choosePropertyType".translate(context)) + .color(context.color.textColorDark) + .size(context.font.extraLarge) + .centerAlign(), + const SizedBox( + height: 10, + ), + PropertyTypeSelector( + onInteraction: (List values) { + selectedPropertyType = values; + + widget.onInteraction + .call(_selectedRangeValues, selectedLocation, values); + + setState(() {}); + }, + ), + const SizedBox( + height: 25, + ), + Text("chooseTheBudeget".translate(context)) + .color(context.color.textColorDark) + .size(context.font.extraLarge) + .centerAlign(), + const SizedBox( + height: 25, + ), + Row( + children: [ + Expanded( + flex: 1, + child: Column( + children: [ + Text("minLbl".translate(context)), + Text(_selectedRangeValues.start + .toInt() + .toString() + .priceFormate()), + ], + ), + ), + Expanded( + flex: 6, + child: RangeSlider( + // labels: RangeLabels(_priceRangeValues.start.toString(), _priceRangeValues.end.toString()), + activeColor: context.color.tertiaryColor, + values: _selectedRangeValues, + onChanged: (RangeValues value) { + _selectedRangeValues = value; + widget.onInteraction.call(_selectedRangeValues, + selectedLocation, selectedPropertyType); + setState(() {}); + }, + min: _priceRangeValues.start, + max: _priceRangeValues.end, + ), + ), + Expanded( + flex: 1, + child: Column( + children: [ + Text("maxLbl".translate(context)), + Text(_selectedRangeValues.end + .toInt() + .toString() + .priceFormate()), + ], + ), + ), + ], + ), + ], + ), + ), + ); + } + + Widget buildCitySearchTextField(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(0.0), + child: TypeAheadField( + debounceDuration: const Duration(milliseconds: 500), + loadingBuilder: (context) { + return Center(child: UiUtils.progress()); + }, + minCharsForSuggestions: 2, + textFieldConfiguration: TextFieldConfiguration( + controller: _controller, + decoration: InputDecoration( + hintText: "searchCity".translate(context), + suffixIcon: GestureDetector( + onTap: () { + _controller.text = ""; + }, + child: Icon( + Icons.close, + color: context.color.tertiaryColor, + )), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: context.color.tertiaryColor), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: context.color.tertiaryColor), + ), + )), + suggestionsBoxDecoration: SuggestionsBoxDecoration( + color: context.color.secondaryColor.withOpacity(1), + ), + itemBuilder: (context, GooglePlaceModel itemData) { + List address = [ + itemData.city, + // itemData.state, + // itemData.country + ]; + + return ListTile( + title: Text(address.join(",").toString()), + ); + }, + onSuggestionSelected: (GooglePlaceModel suggestion) { + List addressList = [ + suggestion.city, + // suggestion.state, + // suggestion.country + ]; + String address = addressList.join(","); + _controller.text = address; + selectedLocation = address; + widget.onInteraction.call( + _selectedRangeValues, selectedLocation, selectedPropertyType); + + setState(() {}); + }, + suggestionsCallback: (pattern) async { + return await googlePlaceRepository.serchCities(pattern); + }, + ), + ); + } +} + +class PropertyTypeSelector extends StatefulWidget { + final Function(List values) onInteraction; + const PropertyTypeSelector({ + super.key, + required this.onInteraction, + }); + + @override + State createState() => _PropertyTypeSelectorState(); +} + +class _PropertyTypeSelectorState extends State { + List selectedPropertyType = [1, 2]; + @override + void initState() { + Future.delayed( + Duration.zero, + () { + if (personalizedInterestSettings.propertyType.isNotEmpty) { + selectedPropertyType = personalizedInterestSettings.propertyType; + } + + widget.onInteraction.call(selectedPropertyType); + }, + ); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Row( + children: [ + GestureDetector( + onTap: () { + selectedPropertyType.clearAndAddAll([1, 2]); + widget.onInteraction.call(selectedPropertyType); + + setState(() {}); + }, + child: Chip( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + label: Text("all".translate(context)) + .size(context.font.large) + .color(selectedPropertyType.containesAll([1, 2]) + ? context.color.buttonColor + : context.color.textColorDark), + backgroundColor: selectedPropertyType.containesAll([1, 2]) + ? context.color.tertiaryColor + : context.color.secondaryColor, + ), + ), + const SizedBox( + width: 5, + ), + GestureDetector( + onTap: () { + selectedPropertyType.clearAndAdd(0); + widget.onInteraction.call(selectedPropertyType); + + setState(() {}); + }, + child: Chip( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + label: Text("sell".translate(context)) + .size(context.font.large) + .color(selectedPropertyType.isSingleElementAndIs(0) + ? context.color.buttonColor + : context.color.textColorDark), + backgroundColor: selectedPropertyType.isSingleElementAndIs(0) + ? context.color.tertiaryColor + : context.color.secondaryColor, + ), + ), + const SizedBox( + width: 5, + ), + GestureDetector( + onTap: () { + selectedPropertyType.clearAndAdd(1); + widget.onInteraction.call(selectedPropertyType); + setState(() {}); + }, + child: Chip( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + label: Text("rent".translate(context)) + .size(context.font.large) + .color(selectedPropertyType.isSingleElementAndIs(1) + ? context.color.buttonColor + : context.color.textColorDark), + backgroundColor: selectedPropertyType.isSingleElementAndIs(1) + ? context.color.tertiaryColor + : context.color.secondaryColor, + )), + ], + ); + } +} diff --git a/lib/Ui/screens/Report/report_property_screen.dart b/lib/Ui/screens/Report/report_property_screen.dart new file mode 100644 index 0000000..3ec2507 --- /dev/null +++ b/lib/Ui/screens/Report/report_property_screen.dart @@ -0,0 +1,196 @@ +import 'package:ebroker/data/cubits/Report/property_report_cubit.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/helper_utils.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../data/cubits/Report/fetch_property_report_reason_list.dart'; +import '../../../data/model/ReportProperty/reason_model.dart'; + +class ReportPropertyScreen extends StatefulWidget { + final int propertyId; + const ReportPropertyScreen({Key? key, required this.propertyId}) + : super(key: key); + + @override + State createState() => _ReportPropertyScreenState(); +} + +class _ReportPropertyScreenState extends State { + List? reasons = []; + late int selectedId; + TextEditingController _reportmessageController = TextEditingController(); + @override + void initState() { + reasons = + context.read().getList() ?? []; + + if (reasons?.isEmpty ?? true) { + selectedId = -10; + } else { + selectedId = reasons!.first.id; + } + + super.initState(); + } + + @override + Widget build(BuildContext context) { + double bottomPadding = (MediaQuery.of(context).viewInsets.bottom - 50); + bool isBottomPaddingNagative = bottomPadding.isNegative; + return SizedBox( + width: MediaQuery.of(context).size.width, + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Report property").size(context.font.larger), + SizedBox( + height: 15, + ), + ListView.separated( + shrinkWrap: true, + itemCount: reasons?.length ?? 0, + physics: const BouncingScrollPhysics(), + separatorBuilder: (context, index) { + return const SizedBox(height: 10); + }, + itemBuilder: (context, index) { + return InkWell( + borderRadius: BorderRadius.circular(10), + onTap: () { + if (selectedId == reasons![index].id) { + // selectedId = -10; + } else { + selectedId = reasons![index].id; + } + setState(() {}); + }, + child: Container( + decoration: BoxDecoration( + color: context.color.primaryColor, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: selectedId == reasons?[index].id + ? context.color.tertiaryColor + : context.color.borderColor, + width: 1.5), + ), + child: Padding( + padding: const EdgeInsets.all(14.0), + child: Text(reasons?[index].reason.firstUpperCase() ?? "") + .color(selectedId == reasons?[index].id + ? context.color.tertiaryColor + : context.color.textColorDark), + ), + ), + ); + + return RadioListTile( + value: reasons![index].id, + groupValue: selectedId, + fillColor: + MaterialStatePropertyAll(context.color.tertiaryColor), + onChanged: (dynamic value) { + if (selectedId == value) { + selectedId = -10; + } else { + selectedId = value; + } + setState(() {}); + }, + title: Text(reasons![index].reason.firstUpperCase()), + ); + }, + ), + if (selectedId.isNegative) + Padding( + padding: EdgeInsets.only( + bottom: isBottomPaddingNagative ? 0 : bottomPadding, + left: 0, + right: 0), + child: TextField( + maxLines: null, + controller: _reportmessageController, + cursorColor: context.color.tertiaryColor, + decoration: InputDecoration( + hintText: "writeReasonHere".translate(context), + focusColor: context.color.tertiaryColor, + focusedBorder: UnderlineInputBorder( + borderSide: + BorderSide(color: context.color.tertiaryColor))), + ), + ), + SizedBox( + height: 14, + ), + BlocConsumer( + listener: (context, state) { + if (state is PropertyReportInSuccess) { + HelperUtils.showSnackBarMessage(context, state.responseMessage); + + Navigator.pop(context); + } + }, builder: (context, state) { + return Align( + alignment: Alignment.centerRight, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + MaterialButton( + height: 40, + minWidth: 104.rw(context), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + side: BorderSide( + color: context.color.borderColor, + width: 1.5, + )), + onPressed: () { + Navigator.pop(context); + }, + child: Text("cancelLbl".translate(context)) + .color(context.color.tertiaryColor), + ), + const SizedBox( + width: 10, + ), + MaterialButton( + height: 40, + minWidth: 104.rw(context), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 30, + ), + ), + color: context.color.tertiaryColor, + onPressed: () async { + if (selectedId.isNegative) { + context.read().report( + property_id: widget.propertyId, + reason_id: selectedId, + message: _reportmessageController.text); + } else { + context.read().report( + property_id: widget.propertyId, + reason_id: selectedId); + } + }, + child: (state is PropertyReportInProgress) + ? UiUtils.progress(width: 24, height: 24) + : Text("report".translate(context)) + .color(context.color.buttonColor), + ), + ], + ), + ); + }) + ], + ), + ), + ); + } +} diff --git a/lib/Ui/screens/analytics/analytics_screen.dart b/lib/Ui/screens/analytics/analytics_screen.dart new file mode 100644 index 0000000..1faee69 --- /dev/null +++ b/lib/Ui/screens/analytics/analytics_screen.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; + +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/ui_utils.dart'; + +class AnalyticsScreen extends StatelessWidget { + final String interestUserCount; + const AnalyticsScreen({super.key, required this.interestUserCount}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black.withOpacity(0.05), + appBar: AppBar( + elevation: 0, + backgroundColor: Colors.transparent, + automaticallyImplyLeading: true, + iconTheme: IconThemeData(color: context.color.tertiaryColor), + ), + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Container( + width: context.screenWidth, + height: 100, + decoration: BoxDecoration( + color: context.color.primaryColor, + borderRadius: BorderRadius.circular( + 10, + ), + border: Border.all( + color: context.color.borderColor, + width: 1.5, + ), + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(UiUtils.translate(context, "interestedUserCount")) + .color(context.color.textColorDark) + .size(context.font.larger) + .bold(), + Center( + child: Text(interestUserCount) + .color(context.color.textColorDark) + .size(context.font.extraLarge) + .italic(), + ) + ], + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/Ui/screens/auth/login_screen.dart b/lib/Ui/screens/auth/login_screen.dart new file mode 100644 index 0000000..53462fe --- /dev/null +++ b/lib/Ui/screens/auth/login_screen.dart @@ -0,0 +1,973 @@ +import 'dart:async'; +import 'dart:developer'; +import 'dart:io'; + +import 'package:country_picker/country_picker.dart'; +import 'package:ebroker/settings.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_sim_country_code/flutter_sim_country_code.dart'; +import 'package:sms_autofill/sms_autofill.dart'; + +import '../../../app/default_app_setting.dart'; +import '../../../app/routes.dart'; +import '../../../data/cubits/auth/auth_cubit.dart'; +import '../../../data/cubits/auth/login_cubit.dart'; +import '../../../data/cubits/auth/send_otp_cubit.dart'; +import '../../../data/cubits/auth/verify_otp_cubit.dart'; +import '../../../data/cubits/system/delete_account_cubit.dart'; +import '../../../data/cubits/system/fetch_system_settings_cubit.dart'; +import '../../../data/cubits/system/user_details.dart'; +import '../../../data/helper/designs.dart'; +import '../../../data/helper/widgets.dart'; +import '../../../data/model/system_settings_model.dart'; +import '../../../utils/AppIcon.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/Network/apiCallTrigger.dart'; +import '../../../utils/api.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/guestChecker.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/hive_utils.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../../../utils/validator.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; + +class LoginScreen extends StatefulWidget { + final bool? isDeleteAccount; + final bool? popToCurrent; + const LoginScreen({Key? key, this.isDeleteAccount, this.popToCurrent}) + : super(key: key); + + @override + State createState() => LoginScreenState(); + static route(RouteSettings routeSettings) { + Map? args = routeSettings.arguments as Map?; + return BlurredRouter( + builder: (_) => MultiBlocProvider( + providers: [ + BlocProvider(create: (context) => SendOtpCubit()), + BlocProvider(create: (context) => VerifyOtpCubit()), + ], + child: LoginScreen( + isDeleteAccount: args?['isDeleteAccount'], + popToCurrent: args?['popToCurrent'], + ), + ), + ); + } +} + +class LoginScreenState extends State { + final TextEditingController mobileNumController = TextEditingController( + text: Constant.isDemoModeOn ? Constant.demoMobileNumber : ""); + //final TextEditingController otpController = TextEditingController(); + final List _controllers = []; + final List _focusNodes = []; + List list = []; + String otpVerificationId = ""; + final _formKey = GlobalKey(); + bool isOtpSent = false; //to swap between login & OTP screen + bool isChecked = false; //Privacy policy checkbox value check + // bool enableResend = false; + String? phone, otp, countryCode, countryName, flagEmoji; + int otpLength = 6; + Timer? timer; + int backPressedTimes = 0; + int focusIndex = 0; + late Size size; + bool isOTPautofilled = false; + ValueNotifier otpResendTime = ValueNotifier( + Constant.otpResendSecond + 1, + ); + TextEditingController otpController = TextEditingController(); + CountryService countryCodeService = CountryService(); + bool isLoginButtonDisabled = true; + String otpIs = ""; + @override + void initState() { + super.initState(); + context.read().fetchSettings( + isAnonymouse: true, + forceRefresh: true, + ); + mobileNumController.addListener( + () { + if (mobileNumController.text.isEmpty) { + isLoginButtonDisabled = true; + setState(() {}); + } else { + isLoginButtonDisabled = false; + setState(() {}); + } + }, + ); + + if (widget.isDeleteAccount ?? false) { + sendVerificationCode(number: HiveUtils.getUserDetails().mobile); + isOtpSent = true; + } + getSimCountry().then((value) { + countryCode = value.phoneCode; + flagEmoji = value.flagEmoji; + setState(() {}); + }); + + for (int i = 0; i < otpLength; i++) { + final TextEditingController controller = TextEditingController(); + final FocusNode focusNode = FocusNode(); + _controllers.add(controller); + _focusNodes.add(focusNode); + } + + Future.delayed(Duration.zero, () { + listenotp(); + }); + + _controllers[otpLength - 1].addListener(() { + if (isOTPautofilled) { + _loginOnOTPFilled(); + } + }); + } + + /// it will return user's sim cards country code + Future getSimCountry() async { + List countryList = countryCodeService.getAll(); + String? simCountryCode; + + try { + simCountryCode = await FlutterSimCountryCode.simCountryCode; + } catch (e) { + log("--don't--remove"); + } + + Country simCountry = countryList.firstWhere( + (element) { + return element.phoneCode == simCountryCode; + }, + orElse: () { + return countryList + .where( + (element) => element.phoneCode == Constant.defaultCountryCode, + ) + .first; + }, + ); + + if (Constant.isDemoModeOn) { + simCountry = countryList + .where((element) => element.phoneCode == Constant.demoCountryCode) + .first; + } + + return simCountry; + } + + void listenotp() { + final SmsAutoFill autoFill = SmsAutoFill(); + + autoFill.code.listen((event) { + if (isOtpSent) { + Future.delayed(Duration.zero, () { + for (int i = 0; i < _controllers.length; i++) { + _controllers[i].text = event[i]; + } + + _focusNodes[focusIndex].unfocus(); + + bool allFilled = true; + for (int i = 0; i < _controllers.length; i++) { + if (_controllers[i].text.isEmpty) { + allFilled = false; + break; + } + } + + // Call the API if all OTP fields are filled + if (allFilled) { + _loginOnOTPFilled(); + } + + if (mounted) setState(() {}); + }); + } + }); + } + + void _loginOnOTPFilled() { + onTapLogin(); + } + + @override + void dispose() { + for (final controller in _controllers) { + controller.dispose(); + } + if (timer != null) { + timer!.cancel(); + } + for (final FocusNode fNode in _focusNodes) { + fNode.dispose(); + } + otpResendTime.dispose(); + mobileNumController.dispose(); + if (isOtpSent) { + SmsAutoFill().unregisterListener(); + } + super.dispose(); + } + + void resendOTP() { + if (isOtpSent) { + context + .read() + .sendOTP(phoneNumber: "+${countryCode!}${mobileNumController.text}"); + } + } + + void startTimer() async { + timer?.cancel(); + timer = Timer.periodic( + const Duration(seconds: 1), + (Timer timer) { + if (otpResendTime.value == 0) { + timer.cancel(); + otpResendTime.value = Constant.otpResendSecond + 1; + setState(() {}); + } else { + otpResendTime.value--; + } + }, + ); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + size = MediaQuery.of(context).size; + + if (context.watch().state + is FetchSystemSettingsSuccess) { + Constant.isDemoModeOn = context + .watch() + .getSetting(SystemSetting.demoMode); + } + + return SafeArea( + child: AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: context.color.tertiaryColor, + statusBarIconBrightness: Brightness.light, + statusBarBrightness: Brightness.light, + ), + child: GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: WillPopScope( + onWillPop: onBackPress, + child: Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + automaticallyImplyLeading: false, + leadingWidth: 100 + 14, + leading: Builder(builder: (context) { + if (widget.popToCurrent == true) { + return const SizedBox.shrink(); + } + return FittedBox( + fit: BoxFit.none, + child: MaterialButton( + color: context.color.secondaryColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + side: BorderSide( + color: context.color.borderColor, width: 1.5), + ), + elevation: 0, + onPressed: () { + GuestChecker.set(isGuest: true); + HiveUtils.setIsGuest(); + APICallTrigger.trigger(); + HiveUtils.setUserIsNotNew(); + Navigator.pushReplacementNamed( + context, + Routes.main, + arguments: { + "from": "login", + "isSkipped": true, + }, + ); + }, + child: const Text("Skip"), + ), + ); + }), + actions: [ + if (!AppSettings.disableCountrySelection) + Visibility( + visible: !isOtpSent, + child: FittedBox( + fit: BoxFit.none, + child: GestureDetector( + onTap: () { + showCountryCode(); + }, + child: Row( + children: [ + CircleAvatar( + radius: 20, + backgroundColor: context.color.tertiaryColor + .withOpacity(0.1), + child: Text(flagEmoji ?? ""), + ), + UiUtils.getSvg( + AppIcons.downArrow, + color: context.color.textLightColor, + ), + ], + ), + ), + ), + ) + ], + ), + body: buildLoginFields(context), + ), + ), + ), + ), + ); + } + + Future onBackPress() { + if (widget.isDeleteAccount ?? false) { + Navigator.pop(context); + } else { + if (isOtpSent == true) { + setState(() { + isOtpSent = false; + }); + } else { + return Future.value(true); + } + } + return Future.value(false); + } + + Widget buildLoginFields(BuildContext context) { + return BlocConsumer( + listener: (context, state) { + if (state is AccountDeleted) { + context.read().clear(); + Future.delayed(const Duration(milliseconds: 500), () { + Navigator.pushReplacementNamed(context, Routes.login); + }); + } + }, + builder: (context, state) { + return ScrollConfiguration( + behavior: RemoveGlow(), + child: SingleChildScrollView( + padding: EdgeInsetsDirectional.only( + top: MediaQuery.of(context).padding.top + 40, + ), + child: BlocListener( + listener: (context, state) async { + if (state is LoginInProgress) { + Widgets.showLoader(context); + } else { + if (widget.isDeleteAccount ?? false) { + } else { + Widgets.hideLoder(context); + } + } + if (state is LoginFailure) { + HelperUtils.showSnackBarMessage(context, state.errorMessage, + type: MessageType.error); + } + if (state is LoginSuccess) { + FirebaseAnalytics analytics = FirebaseAnalytics.instance; + GuestChecker.set(isGuest: false); + HiveUtils.setIsNotGuest(); + await LoadAppSettings().load(); + context + .read() + .fill(HiveUtils.getUserDetails()); + + APICallTrigger.trigger(); + analytics.setUserId( + id: HiveUtils.getUserDetails().id.toString(), + ); + analytics.setUserProperty( + name: "id", + value: HiveUtils.getUserDetails().id.toString()); + context + .read() + .fetchSettings(isAnonymouse: false, forceRefresh: true); + var settings = context.read(); + + if (!const bool.fromEnvironment("force-disable-demo-mode", + defaultValue: false)) { + Constant.isDemoModeOn = + settings.getSetting(SystemSetting.demoMode) ?? false; + } + if (state.isProfileCompleted) { + HiveUtils.setUserIsAuthenticated(); + HiveUtils.setUserIsNotNew(); + context.read().updateFCM(context); + if (widget.popToCurrent == true) { + Navigator.pop(context); + } else { + Navigator.pushReplacementNamed( + context, + Routes.main, + arguments: {"from": "login"}, + ); + } + } else { + HiveUtils.setUserIsNotNew(); + context.read().updateFCM(context); + + if (widget.popToCurrent == true) { + //Navigate to Edit profile field + Navigator.pushNamed( + context, + Routes.completeProfile, + arguments: { + "from": "login", + "popToCurrent": widget.popToCurrent + }, + ); + } else { + //Navigate to Edit profile field + Navigator.pushReplacementNamed( + context, + Routes.completeProfile, + arguments: { + "from": "login", + "popToCurrent": widget.popToCurrent + }, + ); + } + } + } + }, + child: BlocListener( + listener: (context, state) { + if (state is DeleteAccountProgress) { + Widgets.hideLoder(context); + Widgets.showLoader(context); + } + if (state is AccountDeleted) { + Widgets.hideLoder(context); + } + }, + child: BlocListener( + listener: (context, state) { + if (state is VerifyOtpInProgress) { + Widgets.showLoader(context); + } else { + if (widget.isDeleteAccount ?? false) { + } else { + Widgets.hideLoder(context); + } + } + if (state is VerifyOtpFailure) { + HelperUtils.showSnackBarMessage( + context, + state.errorMessage, + type: MessageType.error, + ); + } + + if (state is VerifyOtpSuccess) { + if (widget.isDeleteAccount ?? false) { + context + .read() + .deleteUserAccount(context); + } else { + context.read().login( + phoneNumber: state.credential.user!.phoneNumber?? AppSettings.appNumber, + fireabseUserId: state.credential.user!.uid, + countryCode: countryCode); + } + } + }, + child: BlocListener( + listener: (context, state) { + print("HERE STATE: $state"); + if (state is SendOtpInProgress) { + Widgets.showLoader(context); + } else { + if (widget.isDeleteAccount ?? false) { + } else { + Widgets.hideLoder(context); + } + } + + if (state is SendOtpSuccess) { + startTimer(); + isOtpSent = true; + if (isOtpSent) { + HelperUtils.showSnackBarMessage( + context, + UiUtils.translate( + context, "optsentsuccessflly"), + type: MessageType.success); + } + otpVerificationId = state.verificationId; + setState(() {}); + + // context.read().setToInitial(); + } + if (state is SendOtpFailure) { + HelperUtils.showSnackBarMessage( + context, state.errorMessage, + type: MessageType.error); + } + }, + child: Form( + key: _formKey, + child: isOtpSent + ? buildOtpVerificationScreen() + : buildLoginScreen(), + ), + ), + ), + ), + )), + ); + }, + ); + } + + Widget buildOtpVerificationScreen() { + String demoOTP() { + if (Constant.isDemoModeOn && + Constant.demoMobileNumber == mobileNumController.text) { + return Constant.demoModeOTP; // If true, return the demo mode OTP. + } else { + return ""; // If false, return an empty string. + } + } + + return Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(UiUtils.translate(context, "enterCodeSend")) + .size(context.font.xxLarge) + .bold(weight: FontWeight.w700) + .color(context.color.textColorDark), + SizedBox( + height: 15.rh(context), + ), + if (widget.isDeleteAccount ?? false) ...[ + Text("${UiUtils.translate(context, "weSentCodeOnNumber")} +${HiveUtils.getUserDetails().mobile}") + .size(context.font.large) + .color(context.color.textColorDark.withOpacity(0.8)), + ] else ...[ + Text("${UiUtils.translate(context, "weSentCodeOnNumber")} +$countryCode${mobileNumController.text}") + .size(context.font.large) + .color(context.color.textColorDark.withOpacity(0.8)), + ], + SizedBox( + height: 20.rh(context), + ), + PinFieldAutoFill( + autoFocus: true, + controller: otpController, + textInputAction: TextInputAction.done, + // cursor: Cursor( + // + // color: context.color.teritoryColor, + // width: 2, + // enabled: true, + // height: context.font.extraLarge, + // ), + decoration: UnderlineDecoration( + lineHeight: 1.5, + colorBuilder: PinListenColorBuilder( + context.color.tertiaryColor, + Colors.grey, + ), + ), + currentCode: demoOTP(), + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + keyboardType: Platform.isIOS + ? const TextInputType.numberWithOptions(signed: true) + : const TextInputType.numberWithOptions(), + onCodeSubmitted: (code) { + if (widget.isDeleteAccount ?? false) { + context + .read() + .verifyOTP(verificationId: verificationID, otp: code); + } else { + context + .read() + .verifyOTP(verificationId: otpVerificationId, otp: code); + } + }, + onCodeChanged: (code) { + if (code?.length == 6) { + otpIs = code!; + // setState(() {}); + } + }, + ), + + // loginButton(context), + if (!(timer?.isActive ?? false)) ...[ + SizedBox( + height: 70, + child: Align( + alignment: Alignment.centerLeft, + child: IgnorePointer( + ignoring: timer?.isActive ?? false, + child: setTextbutton( + UiUtils.translate(context, "resendCodeBtnLbl"), + (timer?.isActive ?? false) + ? Theme.of(context).colorScheme.textLightColor + : Theme.of(context).colorScheme.tertiaryColor, + FontWeight.bold, + resendOTP, + context, + ), + ), + ), + ), + ], + + Align( + alignment: Alignment.centerLeft, + child: SizedBox(child: resendOtpTimerWidget()), + ), + + loginButton(context) + ]), + ); + } + + Widget buildLoginScreen() { + return Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(UiUtils.translate(context, "enterYourNumber")) + .size(context.font.xxLarge) + .bold(weight: FontWeight.w700) + .color(context.color.textColorDark), + SizedBox( + height: 15.rh(context), + ), + Text(UiUtils.translate(context, "weSendYouCode")) + .size(context.font.large) + .color(context.color.textColorDark.withOpacity(0.8)), + SizedBox( + height: 41.rh(context), + ), + buildMobileNumberField(), + SizedBox( + height: size.height * 0.05, + ), + buildNextButton(context), + SizedBox( + height: 20.rh(context), + ), + buildTermsAndPrivacyWidget() + ]), + ); + } + + Widget resendOtpTimerWidget() { + return ValueListenableBuilder( + valueListenable: otpResendTime, + builder: (context, value, child) { + if (!(timer?.isActive ?? false)) { + return const SizedBox.shrink(); + } + String formatSecondsToMinutes(int seconds) { + int minutes = seconds ~/ 60; + int remainingSeconds = seconds % 60; + return '$minutes:${remainingSeconds.toString().padLeft(2, '0')}'; + } + + return SizedBox( + height: 70, + child: Align( + alignment: Alignment.centerLeft, + child: RichText( + text: TextSpan( + text: "${UiUtils.translate(context, "resendMessage")} ", + style: TextStyle( + color: Theme.of(context).colorScheme.textColorDark, + letterSpacing: 0.5), + children: [ + TextSpan( + text: formatSecondsToMinutes(int.parse(value.toString())), + style: TextStyle( + color: Theme.of(context).colorScheme.tertiaryColor, + fontWeight: FontWeight.w400, + letterSpacing: 0.5), + ), + TextSpan( + text: UiUtils.translate( + context, + "resendMessageDuration", + ), + style: TextStyle( + color: Theme.of(context).colorScheme.tertiaryColor, + fontWeight: FontWeight.w400, + letterSpacing: 0.5), + ), + ])), + ), + ); + }); + } + + Widget buildMobileNumberField() { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 0), + child: TextFormField( + maxLength: 16, + autofocus: true, + buildCounter: (context, + {required currentLength, required isFocused, maxLength}) { + return const SizedBox.shrink(); + }, + decoration: InputDecoration( + border: InputBorder.none, + hintText: "0000000000", + hintStyle: TextStyle( + fontSize: context.font.xxLarge, + color: context.color.textLightColor), + prefixIcon: FittedBox( + fit: BoxFit.scaleDown, + child: Text("+" "$countryCode ").size(context.font.xxLarge), + ), + ), + validator: ((value) { + return Validator.validatePhoneNumber(value); + }), + onChanged: (String value) { + setState(() { + phone = "${countryCode!} $value"; + }); + }, + textAlignVertical: TextAlignVertical.center, + style: TextStyle(fontSize: context.font.xxLarge), + cursorColor: context.color.tertiaryColor, + keyboardType: TextInputType.phone, + controller: mobileNumController, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + ), + ); + } + + void showCountryCode() { + showCountryPicker( + context: context, + showWorldWide: false, + showPhoneCode: true, + countryListTheme: CountryListThemeData( + borderRadius: BorderRadius.circular(11), + backgroundColor: context.color.backgroundColor, + inputDecoration: InputDecoration( + prefixIcon: Icon(Icons.search), + iconColor: context.color.tertiaryColor, + prefixIconColor: context.color.tertiaryColor, + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: context.color.tertiaryColor)), + floatingLabelStyle: TextStyle(color: context.color.tertiaryColor), + labelText: "Search", + border: OutlineInputBorder())), + onSelect: (Country value) { + flagEmoji = value.flagEmoji; + countryCode = value.phoneCode; + setState(() {}); + }, + ); + } + + Future sendVerificationCode({String? number}) async { + if (widget.isDeleteAccount ?? false) { + context.read().sendOTP(phoneNumber: "+$number"); + } + final form = _formKey.currentState; + + if (form == null) return; + form.save(); + // //checkbox value should be 1 before Login/SignUp + if (form.validate()) { + if (widget.isDeleteAccount ?? false) { + } else { + print("see"); + context.read().sendOTP( + phoneNumber: "+${countryCode!}${mobileNumController.text}"); + } + + // // firebaseLoginProcess(); + } + // // showSnackBar( UiUtils.getTranslatedLabel(context, "acceptPolicy"), context); + } + + Future onTapLogin() async { + if (otpIs.length < otpLength) { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "lblEnterOtp"), + messageDuration: 2); + return; + } + + if (widget.isDeleteAccount ?? false) { + context + .read() + .verifyOTP(verificationId: verificationID, otp: otpIs); + } else { + context + .read() + .verifyOTP(verificationId: otpVerificationId, otp: otpIs); + } + } + + Widget buildNextButton(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 0), + child: buildButton( + context, + buttonTitle: UiUtils.translate(context, "next"), + disabled: isLoginButtonDisabled, + onPressed: () { + sendVerificationCode(); + }, + ), + ); + } + + Widget buildLoginButton(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 0), + child: buildButton( + context, + buttonTitle: UiUtils.translate(context, "next"), + onPressed: () { + sendVerificationCode(); + }, + )); + } + + Widget buildButton(BuildContext context, + {double? height, + double? width, + required VoidCallback onPressed, + bool? disabled, + required String buttonTitle}) { + return MaterialButton( + minWidth: width ?? double.infinity, + height: height ?? 56.rh(context), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + elevation: 0.5, + color: context.color.tertiaryColor, + disabledColor: context.color.textLightColor, + onPressed: (disabled != true) + ? () { + HelperUtils.unfocus(); + onPressed.call(); + } + : null, + child: Text(buttonTitle) + .color(context.color.buttonColor) + .size(context.font.larger), + ); + } + + Widget loginButton(BuildContext context) { + return buildButton( + context, + onPressed: onTapLogin, + buttonTitle: UiUtils.translate( + context, + "comfirmBtnLbl", + ), + ); + } + +//otp + Widget buildTermsAndPrivacyWidget() { + return Align( + alignment: Alignment.bottomCenter, + child: Container( + padding: const EdgeInsetsDirectional.only(top: 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RichText( + textAlign: TextAlign.center, + text: TextSpan(children: [ + TextSpan( + text: + "${UiUtils.translate(context, "policyAggreementStatement")}\n", + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Theme.of(context).colorScheme.textColorDark, + ), + ), + TextSpan( + text: UiUtils.translate(context, "termsConditions"), + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Theme.of(context).colorScheme.tertiaryColor, + decoration: TextDecoration.underline, + fontWeight: FontWeight.w600), + recognizer: TapGestureRecognizer() + ..onTap = (() { + HelperUtils.goToNextPage( + Routes.profileSettings, + context, + false, + args: { + 'title': + UiUtils.translate(context, "termsConditions"), + 'param': Api.termsAndConditions + }, + ); + }), + ), + TextSpan( + text: " ${UiUtils.translate(context, "and")} ", + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Theme.of(context).colorScheme.textColorDark, + ), + ), + TextSpan( + text: UiUtils.translate(context, "privacyPolicy"), + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Theme.of(context).colorScheme.tertiaryColor, + decoration: TextDecoration.underline, + fontWeight: FontWeight.w600), + recognizer: TapGestureRecognizer() + ..onTap = (() { + HelperUtils.goToNextPage( + Routes.profileSettings, context, false, args: { + 'title': UiUtils.translate(context, "privacyPolicy"), + 'param': Api.privacyPolicy + }); + }), + ), + ]), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Ui/screens/chat/chatAudio/audio_state.dart b/lib/Ui/screens/chat/chatAudio/audio_state.dart new file mode 100644 index 0000000..e420d61 --- /dev/null +++ b/lib/Ui/screens/chat/chatAudio/audio_state.dart @@ -0,0 +1,6 @@ +/// Choosing this method because using proper state management would be an +/// overkill for the scope of this project. +class AudioState { + AudioState._(); + static List files = []; +} diff --git a/lib/Ui/screens/chat/chatAudio/globals.dart b/lib/Ui/screens/chat/chatAudio/globals.dart new file mode 100644 index 0000000..d30a095 --- /dev/null +++ b/lib/Ui/screens/chat/chatAudio/globals.dart @@ -0,0 +1,16 @@ +import 'package:flutter/cupertino.dart'; +import 'package:path_provider/path_provider.dart'; + +class ChatGlobals { + ChatGlobals._(); + + static void init() async { + documentPath = "${(await getApplicationDocumentsDirectory()).path}/"; + } + + static const double borderRadius = 27; + static const double defaultPadding = 8; + static String documentPath = ''; + static GlobalKey audioListKey = + GlobalKey(); +} diff --git a/lib/Ui/screens/chat/chatAudio/widgets/audio_bubble.dart b/lib/Ui/screens/chat/chatAudio/widgets/audio_bubble.dart new file mode 100644 index 0000000..e19bc00 --- /dev/null +++ b/lib/Ui/screens/chat/chatAudio/widgets/audio_bubble.dart @@ -0,0 +1,489 @@ +// import 'package:flutter/material.dart'; +// import 'package:just_audio/just_audio.dart'; +// import 'package:rxdart/rxdart.dart'; + +// import '../../chatScreen.dart'; +// import '../../common.dart'; +// import '../globals.dart'; + +// class AudioBubble extends StatefulWidget { +// const AudioBubble({Key? key, required this.currIndex}) : super(key: key); + +// final int currIndex; + +// @override +// State createState() => _AudioBubbleState(); +// } + +// class _AudioBubbleState extends State { +// //AudioPlayer player = AudioPlayer(); +// //Duration? duration, totalduration; +// String msgid = ""; +// @override +// void initState() { +// super.initState(); +// msgid = chatMessageList[widget.currIndex].messageId!; +// print("key===${widget.key}"); + +// // setAudio(); +// /* player.setFilePath(widget.filepath).then((value) { +// setState(() { +// duration = value; +// }); +// }); */ +// } + +// @override +// Widget build(BuildContext context) { +// return audioWidget(chatMessageList[widget.currIndex].messageId!, +// chatMessageList[widget.currIndex].audioPlayer!); +// /* +// return Container( +// height: 45, +// width: MediaQuery.of(context).size.width / 1.5, +// padding: const EdgeInsets.only(left: 12, right: 18), +// decoration: BoxDecoration( +// borderRadius: BorderRadius.circular(Globals.borderRadius - 10), +// color: Colors.grey.withOpacity(0.2), +// //color: Colors.black, +// ), +// child: Row( +// crossAxisAlignment: CrossAxisAlignment.center, +// children: [ +// StreamBuilder( +// stream: player.playerStateStream, +// builder: (context, snapshot) { +// final playerState = snapshot.data; +// final processingState = playerState?.processingState; +// final playing = playerState?.playing; + + +// if (processingState == ProcessingState.loading || +// processingState == ProcessingState.buffering) { +// return GestureDetector( +// child: const Icon(Icons.play_arrow), +// onTap: () { + +// player.play(); +// }, +// ); +// } else if (playing != true) { +// return GestureDetector( +// child: const Icon(Icons.play_arrow), +// onTap: player.play, +// ); +// } else if (processingState != ProcessingState.completed) { +// return GestureDetector( +// child: const Icon(Icons.pause), +// onTap: player.pause, +// ); +// } else { +// return GestureDetector( +// child: const Icon(Icons.replay), +// onTap: () { +// player.seek(Duration.zero); +// }, +// ); +// } +// }, +// ), +// const SizedBox(width: 8), +// Expanded( +// child: StreamBuilder( +// stream: player.positionStream, +// builder: (context, snapshot) { +// if (snapshot.hasData) { +// return Column( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// const SizedBox(height: 4), +// LinearProgressIndicator( +// value: snapshot.data!.inMilliseconds / +// (duration?.inMilliseconds ?? 1), +// backgroundColor: Colors.grey, +// color: ColorsRes.appcolor, +// ), +// const SizedBox(height: 6), +// Row( +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// children: [ +// Text( +// prettyDuration(snapshot.data! == Duration.zero +// ? duration ?? Duration.zero +// : snapshot.data!), +// style: const TextStyle( +// fontSize: 10, +// color: Colors.grey, +// ), +// ), +// Text( +// prettyDuration(totalduration ?? Duration.zero), +// style: const TextStyle( +// fontSize: 10, +// color: Colors.grey, +// ), +// ), +// /*const Text( +// "M4A", +// style: TextStyle( +// fontSize: 10, +// color: Colors.grey, +// ), +// ),*/ +// ], +// ), + +// ], +// ); +// } else { +// return const LinearProgressIndicator(); +// } +// }, +// ), +// ), +// ], +// ), +// ); +// */ +// } +// /* +// @override +// Widget build(BuildContext context) { +// return Padding( +// padding: const EdgeInsets.symmetric(vertical: 4), +// child: Row( +// mainAxisAlignment: MainAxisAlignment.end, +// children: [ +// SizedBox(width: MediaQuery.of(context).size.width * 0.4), +// Expanded( +// child: Container( +// height: 45, +// padding: const EdgeInsets.only(left: 12, right: 18), +// decoration: BoxDecoration( +// borderRadius: BorderRadius.circular(Globals.borderRadius - 10), +// color: Colors.white, +// //color: Colors.black, +// ), +// child: Column( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// // const SizedBox(height: 4), +// Row( +// children: [ +// StreamBuilder( +// stream: player.playerStateStream, +// builder: (context, snapshot) { +// final playerState = snapshot.data; +// final processingState = playerState?.processingState; +// final playing = playerState?.playing; +// if (processingState == ProcessingState.loading || +// processingState == ProcessingState.buffering) { +// return GestureDetector( +// child: const Icon(Icons.play_arrow), +// onTap: player.play, +// ); +// } else if (playing != true) { +// return GestureDetector( +// child: const Icon(Icons.play_arrow), +// onTap: player.play, +// ); +// } else if (processingState != +// ProcessingState.completed) { +// return GestureDetector( +// child: const Icon(Icons.pause), +// onTap: player.pause, +// ); +// } else { +// return GestureDetector( +// child: const Icon(Icons.replay), +// onTap: () { +// player.seek(Duration.zero); +// }, +// ); +// } +// }, +// ), +// const SizedBox(width: 8), +// Expanded( +// child: StreamBuilder( +// stream: player.positionStream, +// builder: (context, snapshot) { +// if (snapshot.hasData) { +// return Column( +// children: [ +// const SizedBox(height: 4), +// LinearProgressIndicator( +// value: snapshot.data!.inMilliseconds / +// (duration?.inMilliseconds ?? 1), +// ), +// const SizedBox(height: 6), +// Row( +// mainAxisAlignment: +// MainAxisAlignment.spaceBetween, +// children: [ +// Text( +// prettyDuration( +// snapshot.data! == Duration.zero +// ? duration ?? Duration.zero +// : snapshot.data!), +// style: const TextStyle( +// fontSize: 10, +// color: Colors.grey, +// ), +// ), +// const Text( +// "M4A", +// style: TextStyle( +// fontSize: 10, +// color: Colors.grey, +// ), +// ), +// ], +// ), +// ], +// ); +// } else { +// return const LinearProgressIndicator(); +// } +// }, +// ), +// ), +// ], +// ), +// ], +// ), +// ), +// ), +// ], +// ), +// ); +// } */ + +// String prettyDuration(Duration d) { +// var min = d.inMinutes < 10 ? "0${d.inMinutes}" : d.inMinutes.toString(); +// var sec = d.inSeconds < 10 ? "0${d.inSeconds}" : d.inSeconds.toString(); +// return "$min:$sec"; +// } + +// audioWidget(String msgid, AudioPlayer? player) { +// // print("map-$msgid-$audioplaysermap"); +// Map? map = audioplaysermap[msgid]; +// //AudioPlayer player = map!["player"]; +// if (player == null) return const SizedBox.shrink(); +// return Container( +// //height: 50, +// width: MediaQuery.of(context).size.width / 1.5, +// padding: const EdgeInsets.only(left: 8, right: 18), +// decoration: BoxDecoration( +// borderRadius: BorderRadius.circular(Globals.borderRadius - 10), +// color: Colors.grey.withOpacity(0.2), +// //color: Colors.black, +// ), +// child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [ +// StreamBuilder( +// stream: player.playerStateStream, +// builder: (context, snapshot) { +// final playerState = snapshot.data; +// final processingState = playerState?.processingState; +// bool playing = playerState?.playing ?? false; +// print("state---$playing==$processingState"); + +// if (processingState == ProcessingState.completed && +// player.position != Duration.zero) { +// player.seek(Duration.zero); +// player.pause(); +// } + +// if (processingState == ProcessingState.loading || +// processingState == ProcessingState.buffering) { +// return IconButton( +// icon: const Icon(Icons.play_arrow), +// iconSize: 30.0, +// onPressed: () {}, +// ); +// } else if (!playing || +// processingState == ProcessingState.completed) { +// return IconButton( +// icon: const Icon(Icons.play_arrow), +// iconSize: 30.0, +// onPressed: () { +// /* if (currPlayingAudioId != msgid && +// currAudioplayer != null && +// currAudioplayer!.playing) { +// currAudioplayer!.stop(); +// } */ +// if (currPlayingAudioId != msgid && +// audioplaysermap.containsKey(currPlayingAudioId) && +// audioplaysermap[currPlayingAudioId]!["player"] != null && +// audioplaysermap[currPlayingAudioId]!["player"].playing) { +// audioplaysermap[currPlayingAudioId]!["player"].pause(); +// } +// print("click===${player.duration}"); +// if (processingState == ProcessingState.completed && +// player.position != Duration.zero) { +// player.seek(Duration.zero); +// } +// player.play(); +// print("click===**${player.playing}"); +// currPlayingAudioId = msgid; +// currAudioplayer = player; +// }, +// ); +// } else if (processingState != ProcessingState.completed) { +// return IconButton( +// icon: const Icon(Icons.pause), +// iconSize: 30.0, +// onPressed: player.pause, +// ); +// } else { +// return IconButton( +// icon: const Icon(Icons.replay), +// iconSize: 64.0, +// onPressed: () => player.seek(Duration.zero), +// ); +// } +// }, +// ), +// Expanded( +// child: StreamBuilder( +// //stream: _positionDataStream, +// stream: getPositionStream(player), +// builder: (context, snapshot) { +// final positionData = snapshot.data; +// return SeekBar( +// duration: positionData?.duration ?? Duration.zero, +// position: positionData?.position ?? Duration.zero, +// bufferedPosition: +// positionData?.bufferedPosition ?? Duration.zero, +// onChangeEnd: player.seek, +// ); +// }, +// ), +// ) +// ]), +// ); +// } + +// Stream getPositionStream(AudioPlayer player) { +// Stream data = +// Rx.combineLatest3( +// player.positionStream, +// player.bufferedPositionStream, +// player.durationStream, +// (position, bufferedPosition, duration) => PositionData( +// position, bufferedPosition, duration ?? Duration.zero)); +// return data; +// } +// } +// /* + +// audioWidget(String msgid, AudioPlayer? player) { +// print("map-$msgid-$audioplaysermap"); +// Map? map = audioplaysermap[msgid]; +// //AudioPlayer player = map!["player"]; +// if (player == null) return const SizedBox.shrink(); +// return Row(mainAxisSize: MainAxisSize.min, children: [ +// CircleAvatar( +// radius: 30, +// child: Column(mainAxisSize: MainAxisSize.min, children: [ +// const Icon(Icons.headphones), +// Text( +// Constant.getDurationFormat(player.duration!), +// style: +// Theme.of(context).textTheme.caption!.apply(color: Colors.white), +// ) +// ]), +// ), +// StreamBuilder( +// stream: player.playerStateStream, +// builder: (context, snapshot) { +// final playerState = snapshot.data; +// final processingState = playerState?.processingState; +// bool playing = playerState?.playing ?? false; +// print("state---$playing==$processingState"); + +// if (processingState == ProcessingState.completed && +// player.position != Duration.zero) { +// player.seek(Duration.zero); +// player.pause(); +// } + +// if (processingState == ProcessingState.loading || +// processingState == ProcessingState.buffering) { +// return Container( +// margin: const EdgeInsets.all(8.0), +// width: 30.0, +// height: 30.0, +// child: const CircularProgressIndicator(), +// ); +// } else if (!playing || processingState == ProcessingState.completed) { +// return IconButton( +// icon: const Icon(Icons.play_arrow), +// iconSize: 30.0, +// onPressed: () { +// /* if (currPlayingAudioId != msgid && +// currAudioplayer != null && +// currAudioplayer!.playing) { +// currAudioplayer!.stop(); +// } */ +// if (currPlayingAudioId != msgid && +// audioplaysermap.containsKey(currPlayingAudioId) && +// audioplaysermap[currPlayingAudioId]!["player"] != null && +// audioplaysermap[currPlayingAudioId]!["player"].playing) { +// audioplaysermap[currPlayingAudioId]!["player"].pause(); +// } +// print("click===${player.duration}"); +// if (processingState == ProcessingState.completed && +// player.position != Duration.zero) { +// player.seek(Duration.zero); +// } +// player.play(); +// print("click===**${player.playing}"); +// currPlayingAudioId = msgid; +// currAudioplayer = player; +// }, +// ); +// } else if (processingState != ProcessingState.completed) { +// return IconButton( +// icon: const Icon(Icons.pause), +// iconSize: 30.0, +// onPressed: player.pause, +// ); +// } else { +// return IconButton( +// icon: const Icon(Icons.replay), +// iconSize: 64.0, +// onPressed: () => player.seek(Duration.zero), +// ); +// } +// }, +// ), +// Expanded( +// child: StreamBuilder( +// //stream: _positionDataStream, +// stream: getPositionStream(player), +// builder: (context, snapshot) { +// final positionData = snapshot.data; +// return SeekBar( +// duration: positionData?.duration ?? Duration.zero, +// position: positionData?.position ?? Duration.zero, +// bufferedPosition: positionData?.bufferedPosition ?? Duration.zero, +// onChangeEnd: player.seek, +// ); +// }, +// ), +// ) +// ]); +// } + +// Stream getPositionStream(AudioPlayer player) { +// Stream data = +// Rx.combineLatest3( +// player.positionStream, +// player.bufferedPositionStream, +// player.durationStream, +// (position, bufferedPosition, duration) => PositionData( +// position, bufferedPosition, duration ?? Duration.zero)); +// return data; +// } +// */ \ No newline at end of file diff --git a/lib/Ui/screens/chat/chatAudio/widgets/chat_widget.dart b/lib/Ui/screens/chat/chatAudio/widgets/chat_widget.dart new file mode 100644 index 0000000..3ab6033 --- /dev/null +++ b/lib/Ui/screens/chat/chatAudio/widgets/chat_widget.dart @@ -0,0 +1,446 @@ +// ignore_for_file: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member + +import 'dart:io'; + +import 'package:any_link_preview/any_link_preview.dart'; +import 'package:audioplayers/audioplayers.dart'; +import 'package:dio/dio.dart'; +import 'package:ebroker/Ui/screens/chat/chat_screen.dart'; +import 'package:ebroker/app/app_theme.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:open_filex/open_filex.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../../../../../data/cubits/chatCubits/send_message.dart'; +import '../../../../../data/cubits/system/app_theme_cubit.dart'; +import '../../../../../utils/Extensions/extensions.dart'; +import '../../../../../utils/Notification/chat_message_handler.dart'; +import '../../../../../utils/helper_utils.dart'; +import '../../../../../utils/hive_utils.dart'; +import '../../../../../utils/ui_utils.dart'; + +part "parts/attachment.part.dart"; +part "parts/linkpreview.part.dart"; +part "parts/recordmsg.part.dart"; + +////Please don't make chaneges without sufficent knowledege in this file. otherwise you will be responsable for it +/// +//This will store and ensure that msg is already sent so we don't have to send it again +Set sentMessages = {}; + +class ChatMessage extends StatefulWidget { + final String message; + final String senderId; + final bool isSentByMe; + final bool? isSentNow; + final String propertyId; + final String reciverId; + final bool isChatAudio; + final bool hasAttachment; + final dynamic audioFile; + final String time; + final dynamic attachment; + final Function(int id)? onHold; + + const ChatMessage( + {super.key, + this.isSentNow, + required this.message, + required this.isSentByMe, + required this.isChatAudio, + this.audioFile, + this.attachment, + required this.senderId, + required this.time, + required this.hasAttachment, + required this.propertyId, + required this.reciverId, + this.onHold}); + + @override + State createState() => ChatMessageState(); + + Map toMap() { + Map data = {}; + data['key'] = key; + data['message'] = message; + data['isSentNow'] = isSentNow; + data['isSentByMe'] = isSentByMe; + data['isChatAudio'] = isChatAudio; + data['senderId'] = senderId; + data['propertyId'] = propertyId; + data['reciverId'] = reciverId; + data['hasAttachment'] = hasAttachment; + data['audioFile'] = audioFile; + data['time'] = time; + data['attachment'] = attachment; + return data; + } + + factory ChatMessage.fromMap(Map json) { + var chat = ChatMessage( + key: json['key'], + message: json['message'], + isSentByMe: json['isSentByMe'], + isChatAudio: json['isChatAudio'], + senderId: json['senderId'], + audioFile: json['audioFile'], + attachment: json['attachment'], + time: json['time'], + hasAttachment: json['hasAttachment'], + propertyId: json['propertyId'], + reciverId: json['reciverId']); + return chat; + } +} + +class ChatMessageState extends State + with AutomaticKeepAliveClientMixin { + bool isChatSent = false; + bool selectedMessage = false; + static bool isMounted = false; + String? link; + final ValueNotifier _linkAddNotifier = ValueNotifier(""); + @override + void initState() { + ///isSentNow is for check if we are not appending messages multiple time + if (widget.isSentByMe && + (widget.isSentNow == true) && + isChatSent == false) { + if (!sentMessages.contains(widget.key)) { + context.read().send( + senderId: HiveUtils.getUserId().toString(), + recieverId: widget.reciverId, + attachment: widget.attachment, + message: widget.message, + proeprtyId: widget.propertyId, + audio: widget.audioFile, + ); + } + sentMessages.add(widget.key); + + isMounted = true; + } + + super.initState(); + } + + String _emptyTextIfAttachmentHasNoText() { + if (widget.hasAttachment) { + if (widget.message == "[File]") { + return ""; + } else { + return widget.message; + } + } else { + return widget.message; + } + } + + bool _isLink(String input) { + ///This will check if text contains link + final matcher = RegExp( + r"(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)"); + return matcher.hasMatch(input); + } + + List _replaceLink() { + //This function will make part of text where link starts. we put invisible charector so we can split it with it + final linkPattern = RegExp( + r"(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)"); + + ///This is invisible charector [You can replace it with any special chareactor which generally nobody use] + const String substringIdentifier = "‎"; + + ///This will find and add invisible charector in prefix and suffix + String splitMapJoin = _emptyTextIfAttachmentHasNoText().splitMapJoin( + linkPattern, + onMatch: (match) { + return substringIdentifier + match.group(0)! + substringIdentifier; + }, + onNonMatch: (match) { + return match; + }, + ); + //finally we split it with invisible charector so it will become list + return splitMapJoin.split(substringIdentifier); + } + + List _matchAstric(String data) { + var pattern = RegExp(r"\*(.*?)\*"); + + String mapJoin = data.splitMapJoin( + pattern, + onMatch: (p0) { + return "‎${p0.group(0)!}‎"; + }, + onNonMatch: (p0) { + return p0; + }, + ); + + return mapJoin.split("‎"); + } + + @override + Widget build(BuildContext context) { + super.build(context); + + bool isDark = + context.watch().state.appTheme == AppTheme.dark; + + return GestureDetector( + onLongPress: () { + selectedMessageid.value = (widget.key as ValueKey).value; + selectedRecieverId.value = int.parse(widget.reciverId); + showDeletebutton.value = true; + }, + onTap: () { + selectedMessage = false; + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 7), + child: Container( + alignment: + widget.isSentByMe ? Alignment.centerRight : Alignment.centerLeft, + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only( + // top: MediaQuery.of(context).size.height * 0.007, + right: widget.isSentByMe ? 20 : 0, + left: widget.isSentByMe ? 0 : 20, + ), + child: Column( + crossAxisAlignment: widget.isSentByMe + ? CrossAxisAlignment.end + : CrossAxisAlignment.start, + children: [ + Container( + constraints: + BoxConstraints(maxWidth: context.screenWidth * 0.74), + decoration: BoxDecoration( + color: selectedMessage == true + ? (widget.isSentByMe == true + ? context.color.tertiaryColor.darken(45) + : context.color.secondaryColor.darken(45)) + : (widget.isSentByMe + ? const Color(0xffEEEEEE) + : context.color.secondaryColor), + borderRadius: BorderRadius.circular(8) + + // BorderRadius.only( + // topRight: widget.isSentByMe + // ? Radius.zero + // : const Radius.circular(10), + // topLeft: widget.isSentByMe + // ? const Radius.circular(10) + // : Radius.zero, + // bottomLeft: const Radius.circular(10), + // bottomRight: const Radius.circular(10), + // ), + ), + child: Wrap( + runAlignment: WrapAlignment.end, + alignment: WrapAlignment.end, + crossAxisAlignment: WrapCrossAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.all(12), + child: Container( + child: widget.isChatAudio + ? RecordMessage( + url: widget.audioFile ?? "", + isSentByMe: widget.isSentByMe, + ) + : Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.hasAttachment) + AttachmentMessage( + url: widget.attachment, + isSentByMe: widget.isSentByMe), + + //This is preview builder for image + ValueListenableBuilder( + valueListenable: _linkAddNotifier, + builder: (context, dynamic value, c) { + if (value == null) { + return const SizedBox.shrink(); + } + + return FutureBuilder( + future: AnyLinkPreview.getMetadata( + link: value), + builder: (context, + AsyncSnapshot snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox.shrink(); + } + return LinkPreviw( + snapshot: snapshot, + link: value, + ); + } + return const SizedBox.shrink(); + }, + ); + }), + SelectableText.rich( + TextSpan( + style: TextStyle( + color: (isDark && !widget.isSentByMe) + ? context.color.buttonColor + : Colors.black), + children: _replaceLink().map((data) { + //This will add link to msg + if (_isLink(data)) { + //This will notify priview object that it has link + _linkAddNotifier.value = data; + _linkAddNotifier.notifyListeners(); + + return TextSpan( + text: data, + recognizer: TapGestureRecognizer() + ..onTap = () async { + await launchUrl( + Uri.parse(data)); + }, + style: TextStyle( + decoration: + TextDecoration.underline, + color: Colors.blue[800])); + } + //This will make text bold + return TextSpan( + text: "", + children: + _matchAstric(data).map((text) { + if (text + .toString() + .startsWith("*") && + text.toString().endsWith("*")) { + return TextSpan( + text: + text.replaceAll("*", ""), + style: TextStyle( + color: (isDark && + !widget + .isSentByMe) + ? context + .color.buttonColor + : Colors.black, + fontWeight: + FontWeight.w800)); + } + + return TextSpan( + text: text, + style: TextStyle( + color: (isDark && + !widget.isSentByMe) + ? context + .color.buttonColor + : Colors.black)); + }).toList(), + style: TextStyle( + color: widget.isSentByMe + ? context.color.secondaryColor + : context + .color.textColorDark), + ); + }).toList(), + ), + style: TextStyle( + color: (isDark && !widget.isSentByMe) + ? context.color.buttonColor + : Colors.black), + ), + ], + ), + ), + ), + if (widget.isSentByMe && widget.isSentNow == true) ...[ + BlocConsumer( + listener: (context, state) { + if (state is SendMessageSuccess) { + isChatSent = true; + + ///Value which we added locally + ValueKey? uniqueIdentifier = widget.key as ValueKey; + + ////We were added local id so whenit completed we will replace it with server message id + + ChatMessageHandlerOLD.updateMessageId( + uniqueIdentifier.value, state.messageId); + + WidgetsBinding.instance + .addPostFrameCallback((timeStamp) { + if (mounted) setState(() {}); + }); + } + }, + builder: (context, state) { + if (state is SendMessageInProgress) { + return Padding( + padding: + const EdgeInsets.only(right: 5.0, bottom: 2), + child: Icon( + Icons.watch_later_outlined, + size: context.font.smaller, + color: Colors.black, + ), + ); + } + + if (state is SendMessageFailed) { + return Padding( + padding: + const EdgeInsets.only(right: 5.0, bottom: 2), + child: Icon( + Icons.error, + size: context.font.smaller, + color: Colors.black, + ), + ); + } + return const SizedBox.shrink(); + }, + ) + ] + ], + ), + ), + const SizedBox( + height: 5, + ), + Padding( + padding: const EdgeInsets.only(right: 3.0), + child: Text( + (DateTime.parse(widget.time)) + .toLocal() + .toIso8601String() + .toString() + .formatDate(format: "hh:mm aa"), // + style: TextStyle( + color: widget.isSentByMe + ? context.color.textLightColor + : context.color.textLightColor), + ).size(context.font.smaller), + ), + ], + ), + ), + ), + ); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/Ui/screens/chat/chatAudio/widgets/flow_shader.dart b/lib/Ui/screens/chat/chatAudio/widgets/flow_shader.dart new file mode 100644 index 0000000..051fce0 --- /dev/null +++ b/lib/Ui/screens/chat/chatAudio/widgets/flow_shader.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; + +class FlowShader extends StatefulWidget { + const FlowShader({ + Key? key, + required this.child, + this.duration = const Duration(seconds: 2), + this.direction = Axis.horizontal, + this.flowColors = const [Colors.white, Colors.black], + }) : assert(flowColors.length == 2), + super(key: key); + + final Widget child; + final Axis direction; + final Duration duration; + final List flowColors; + + @override + FlowShaderState createState() => FlowShaderState(); +} + +class FlowShaderState extends State + with SingleTickerProviderStateMixin { + late AnimationController controller; + late Animation animation1; + late Animation animation2; + late Animation animation3; + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + controller = AnimationController( + vsync: this, + duration: widget.duration, + ); + final TweenSequenceItem seqbw = TweenSequenceItem( + tween: ColorTween( + begin: widget.flowColors.last, + end: widget.flowColors.first, + ), + weight: 1, + ); + final TweenSequenceItem seqwb = TweenSequenceItem( + tween: ColorTween( + begin: widget.flowColors.first, + end: widget.flowColors.last, + ), + weight: 1, + ); + animation1 = TweenSequence([seqbw, seqwb]).animate( + CurvedAnimation( + parent: controller, + curve: const Interval(0.0, 0.45, curve: Curves.linear), + ), + ); + animation2 = TweenSequence([seqbw, seqwb]).animate( + CurvedAnimation( + parent: controller, + curve: const Interval(0.15, 0.75, curve: Curves.linear), + ), + ); + animation3 = TweenSequence([seqbw, seqwb]).animate( + CurvedAnimation( + parent: controller, + curve: const Interval(0.45, 1, curve: Curves.linear), + ), + ); + controller.repeat(); + controller.addListener(() { + setState(() {}); + }); + } + + @override + Widget build(BuildContext context) { + return ShaderMask( + shaderCallback: (rect) { + return LinearGradient( + colors: [ + animation3.value, + animation2.value, + animation1.value, + ], + begin: widget.direction == Axis.horizontal + ? Alignment.centerLeft + : Alignment.topCenter, + end: widget.direction == Axis.horizontal + ? Alignment.centerRight + : Alignment.bottomCenter, + ).createShader(rect); + }, + child: widget.child, + ); + } +} diff --git a/lib/Ui/screens/chat/chatAudio/widgets/lottie_animation.dart b/lib/Ui/screens/chat/chatAudio/widgets/lottie_animation.dart new file mode 100644 index 0000000..51c2aff --- /dev/null +++ b/lib/Ui/screens/chat/chatAudio/widgets/lottie_animation.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:lottie/lottie.dart'; + +class LottieAnimation extends StatefulWidget { + const LottieAnimation({Key? key}) : super(key: key); + + @override + State createState() => _LottieAnimationState(); +} + +class _LottieAnimationState extends State + with SingleTickerProviderStateMixin { + late AnimationController controller; + + @override + void initState() { + super.initState(); + controller = AnimationController(vsync: this); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + child: Lottie.asset( + 'assets/animation/dustbin_grey.json', + controller: controller, + onLoaded: (composition) { + controller + ..duration = composition.duration + ..forward(); + debugPrint("Lottie Duration: ${composition.duration}"); + }, + height: 40, + width: 40, + ), + ); + } +} diff --git a/lib/Ui/screens/chat/chatAudio/widgets/parts/attachment.part.dart b/lib/Ui/screens/chat/chatAudio/widgets/parts/attachment.part.dart new file mode 100644 index 0000000..7669a24 --- /dev/null +++ b/lib/Ui/screens/chat/chatAudio/widgets/parts/attachment.part.dart @@ -0,0 +1,158 @@ +part of "../chat_widget.dart"; + +class AttachmentMessage extends StatefulWidget { + final String url; + final bool isSentByMe; + const AttachmentMessage( + {super.key, required this.url, required this.isSentByMe}); + + @override + State createState() => _AttachmentMessageState(); +} + +class _AttachmentMessageState extends State { + bool isFileDownloading = false; + double persontage = 0; + String getExtentionOfFile() { + return widget.url.toString().split(".").last; + } + + String getFileName() { + return widget.url.toString().split("/").last; + } + + Future downloadFile() async { + await Permission.storage.request(); + try { + if (!(await Permission.storage.isGranted)) { + HelperUtils.showSnackBarMessage( + context, "Please give storage permission"); + + return; + } + + String? downloadPath = await getDownloadPath(); + await Dio().download( + widget.url, + "${downloadPath!}/${getFileName()}", + onReceiveProgress: (int count, int total) async { + persontage = (count) / total; + + if (persontage == 1) { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "fileSavedIn"), + type: MessageType.success); + + await OpenFilex.open("$downloadPath/${getFileName()}"); + } + setState(() {}); + }, + ); + } catch (e) { + print("Download Error is: $e"); + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "errorFileSave"), + type: MessageType.success); + } + } + + Future getDownloadPath() async { + Directory? directory; + try { + if (Platform.isIOS) { + directory = await getApplicationDocumentsDirectory(); + } else { + directory = Directory('/storage/emulated/0/Download'); + // Put file in global download folder, if for an unknown reason it didn't exist, we fallback + // ignore: avoid_slow_async_io + if (!await directory.exists()) { + directory = await getExternalStorageDirectory(); + } + } + } catch (err) { + if (kDebugMode) { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "fileNotSaved"), + type: MessageType.success); + } + } + return directory?.path; + } + + @override + Widget build(BuildContext context) { + // if (tempIssue != null) { + // return GestureDetector( + // onTap: () async { + // await Clipboard.setData(ClipboardData(text: tempIssue.toString())); + // }, + // child: Text(tempIssue.toString())); + // } + return Row( + children: [ + InkWell( + onTap: () async { + await downloadFile(); + }, + child: Container( + height: 50, + width: 50, + alignment: Alignment.center, + decoration: BoxDecoration( + color: context.color.secondaryColor.withOpacity(0.064), + borderRadius: BorderRadius.circular(10), + border: + Border.all(color: context.color.borderColor, width: 1.5)), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (persontage != 0 && persontage != 1) ...[ + Stack( + alignment: Alignment.center, + children: [ + CircularProgressIndicator( + strokeWidth: 1.7, + color: context.color.tertiaryColor, + value: persontage, + ), + const Icon(Icons.close) + ], + ), + ] else ...[ + Text(getExtentionOfFile().toString().toUpperCase()).color( + widget.isSentByMe + ? Colors.black + : context.color.textColorDark), + Icon( + Icons.download, + size: 14, + color: context.color.tertiaryColor, + ) + ] + ], + ), + ), + ), + const SizedBox( + width: 5, + ), + Expanded( + child: Container( + height: 50, + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Text(getFileName().toString()) + .setMaxLines( + lines: 1, + ) + .color(widget.isSentByMe + ? Colors.black + : context.color.textColorDark), + ), + ), + ), + ], + ); + } +} diff --git a/lib/Ui/screens/chat/chatAudio/widgets/parts/linkpreview.part.dart b/lib/Ui/screens/chat/chatAudio/widgets/parts/linkpreview.part.dart new file mode 100644 index 0000000..e415eb5 --- /dev/null +++ b/lib/Ui/screens/chat/chatAudio/widgets/parts/linkpreview.part.dart @@ -0,0 +1,77 @@ +part of "../chat_widget.dart"; + +class LinkPreviw extends StatefulWidget { + final String link; + final AsyncSnapshot snapshot; + const LinkPreviw({ + super.key, + required this.snapshot, + required this.link, + }); + + @override + State createState() => _LinkPreviwState(); +} + +class _LinkPreviwState extends State { + final ValueNotifier _errorChecker = ValueNotifier(false); + + @override + void dispose() { + _errorChecker.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () async { + await launchUrl(Uri.parse(widget.link), + mode: LaunchMode.externalApplication); + }, + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: context.color.tertiaryColor), + child: Padding( + padding: const EdgeInsets.all(3.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ValueListenableBuilder( + valueListenable: _errorChecker, + builder: (context, value, child) { + if (value == true) { + return const SizedBox.shrink(); + } + + return AspectRatio( + aspectRatio: 1 / 0.5, + child: Image.network( + (widget.snapshot.data as Metadata).image!, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + WidgetsBinding.instance + .addPostFrameCallback((timeStamp) { + _errorChecker.value = true; + }); + return const SizedBox.shrink(); + }, + ), + ); + }), + Text((widget.snapshot.data as Metadata).title ?? "") + .color(context.color.primaryColor.withOpacity(0.9)) + .size(context.font.small), + Text((widget.snapshot.data as Metadata).desc ?? "") + .setMaxLines(lines: 1) + .color(context.color.primaryColor.withOpacity(0.8)) + .size(context.font.smaller) + ], + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/chat/chatAudio/widgets/parts/recordmsg.part.dart b/lib/Ui/screens/chat/chatAudio/widgets/parts/recordmsg.part.dart new file mode 100644 index 0000000..7f38616 --- /dev/null +++ b/lib/Ui/screens/chat/chatAudio/widgets/parts/recordmsg.part.dart @@ -0,0 +1,98 @@ +part of "../chat_widget.dart"; + +class RecordMessage extends StatefulWidget { + final String url; + final bool isSentByMe; + const RecordMessage({super.key, required this.url, required this.isSentByMe}); + + @override + State createState() => _RecordMessageState(); +} + +class _RecordMessageState extends State { + AudioPlayer audioPlayer = AudioPlayer(); + bool isPlaying = false; + int position = 0; + int durationChanged = 0; + + @override + void initState() { + audioPlayer.onDurationChanged.listen((Duration event) { + durationChanged = event.inSeconds; + setState(() {}); + }); + + audioPlayer.onPlayerStateChanged.listen((PlayerState event) { + isPlaying = event == PlayerState.playing; + + setState(() {}); + }); + audioPlayer.onPositionChanged.listen((Duration event) { + position = event.inSeconds; + setState(() {}); + }); + // audioPlayer.seek(const Duration(seconds: 1)); + + super.initState(); + } + + @override + void dispose() { + audioPlayer.dispose(); + super.dispose(); + } + + @override + void setState(VoidCallback fn) { + if (mounted) { + super.setState(fn); + } + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () { + if (!isPlaying) { + if (widget.url.startsWith(("http")) || + widget.url.startsWith("https")) { + audioPlayer.play(UrlSource(widget.url)); + } else { + audioPlayer.play(DeviceFileSource(widget.url)); + } + } else { + audioPlayer.stop(); + } + }, + child: Icon( + isPlaying ? Icons.pause : Icons.play_arrow, + color: widget.isSentByMe + ? context.color.primaryColor + : context.color.tertiaryColor, + )), + Slider( + activeColor: widget.isSentByMe + ? context.color.primaryColor + : context.color.tertiaryColor, + inactiveColor: widget.isSentByMe + ? context.color.primaryColor.withOpacity(0.3) + : context.color.tertiaryColor.withOpacity(0.3), + value: position.toDouble(), + onChanged: (v) { + audioPlayer.seek(Duration(seconds: v.toInt())); + setState(() {}); + }, + min: 0, + max: durationChanged.toDouble(), + ), + if ((durationChanged - position) != 0) + Text((durationChanged - position).toString()).color(widget.isSentByMe + ? context.color.primaryColor + : context.color.textColorDark) + ], + ); + } +} diff --git a/lib/Ui/screens/chat/chatAudio/widgets/record_button.dart b/lib/Ui/screens/chat/chatAudio/widgets/record_button.dart new file mode 100644 index 0000000..d87e0be --- /dev/null +++ b/lib/Ui/screens/chat/chatAudio/widgets/record_button.dart @@ -0,0 +1,386 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_vibrate/flutter_vibrate.dart'; +import 'package:record/record.dart'; + +import '../../../../../utils/Extensions/extensions.dart'; +import '../../../../../utils/ui_utils.dart'; +import '../audio_state.dart'; +import '../globals.dart'; +import 'flow_shader.dart'; +import 'lottie_animation.dart'; + +class RecordButton extends StatefulWidget { + const RecordButton( + {Key? key, + required this.controller, + required this.callback, + required this.isSending}) + : super(key: key); + + final AnimationController controller; + final Function(dynamic path)? callback; + final bool isSending; + + @override + State createState() => _RecordButtonState(); +} + +class _RecordButtonState extends State { + static const double size = 43; + + final double lockerHeight = 200; + double timerWidth = 0; + + late Animation buttonScaleAnimation; + late Animation timerAnimation; + late Animation lockerAnimation; + + DateTime? startTime; + Timer? timer; + String recordDuration = "00:00"; + Record? record; + + bool isLocked = false; + bool showLottie = false; + + @override + void initState() { + super.initState(); + buttonScaleAnimation = Tween(begin: 1, end: 2).animate( + CurvedAnimation( + parent: widget.controller, + curve: const Interval(0.0, 0.6, curve: Curves.elasticInOut), + ), + ); + widget.controller.addListener(() { + setState(() {}); + }); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + timerWidth = + MediaQuery.of(context).size.width - 2 * ChatGlobals.defaultPadding - 4; + timerAnimation = + Tween(begin: timerWidth + ChatGlobals.defaultPadding, end: 0) + .animate(CurvedAnimation( + parent: widget.controller, + curve: const Interval(0.2, 1, curve: Curves.easeIn), + )); + lockerAnimation = + Tween(begin: lockerHeight + ChatGlobals.defaultPadding, end: 0) + .animate(CurvedAnimation( + parent: widget.controller, + curve: const Interval(0.2, 1, curve: Curves.easeIn), + )); + } + + @override + void dispose() { + if (record != null) record!.dispose(); + timer?.cancel(); + timer = null; + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Stack( + clipBehavior: Clip.none, + children: [ + (timer?.isActive ?? false) ? lockSlider() : const SizedBox.shrink(), + (timer?.isActive ?? false) ? cancelSlider() : const SizedBox.shrink(), + audioButton(), + if (isLocked) timerLocked(), + ], + ); + } + + Widget lockSlider() { + return Positioned( + bottom: -lockerAnimation.value, + child: Container( + height: lockerHeight, + width: size, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(ChatGlobals.borderRadius), + color: context.color.secondaryColor, + //color: Colors.black, + ), + padding: const EdgeInsets.symmetric(vertical: 15), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + //const FaIcon(FontAwesomeIcons.lock, size: 20), + const Icon(Icons.lock, size: 20), + const SizedBox(height: 8), + FlowShader( + direction: Axis.vertical, + child: Column( + children: const [ + Icon(Icons.keyboard_arrow_up), + Icon(Icons.keyboard_arrow_up), + Icon(Icons.keyboard_arrow_up), + ], + ), + ), + ], + ), + ), + ); + } + + Widget cancelSlider() { + return Positioned( + right: -timerAnimation.value, + child: Container( + height: size, + width: timerWidth, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(ChatGlobals.borderRadius), + color: context.color.primaryColor, + //color: Colors.black, + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + showLottie ? const LottieAnimation() : Text(recordDuration), + FlowShader( + duration: const Duration(seconds: 3), + flowColors: [ + context.color.tertiaryColor, + const Color(0xFF9E9E9E) + ], + child: Row( + children: [ + const Icon(Icons.keyboard_arrow_left), + Text(UiUtils.translate(context, "slidetocancel")), + const SizedBox( + width: 10, + ), + ], + ), + //flowColors: const [Colors.white, Colors.grey], + ), + const SizedBox(width: size), + ], + ), + ), + ), + ); + } + + Widget timerLocked() { + return Positioned( + right: 0, + child: Container( + height: size, + width: timerWidth, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(ChatGlobals.borderRadius), + color: context.color.secondaryColor, + //color: Colors.black, + ), + child: Padding( + padding: const EdgeInsets.only(left: 15, right: 25), + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () async { + /*Vibrate.feedback(FeedbackType.success); + timer?.cancel(); + timer = null; + startTime = null; + recordDuration = "00:00"; + + var filePath = await Record().stop(); + AudioState.files.add(filePath!); + Globals.audioListKey.currentState! + .insertItem(AudioState.files.length - 1); + debugPrint(filePath);*/ + saveFile(); + setState(() { + isLocked = false; + }); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text(recordDuration), + const SizedBox( + width: 5, + ), + FlowShader( + duration: const Duration(seconds: 3), + flowColors: [context.color.tertiaryColor, Colors.grey], + child: Text(UiUtils.translate(context, "taploacktostop")), + //flowColors: const [Colors.white, Colors.grey], + ), + const Center( + child: Icon( + Icons.lock, + size: 18, + color: Colors.green, + ), + /* child: FaIcon( + FontAwesomeIcons.lock, + size: 18, + color: Colors.green, + ), */ + ), + ], + ), + ), + ), + ), + ); + } + + Widget audioButton() { + return GestureDetector( + child: Transform.scale( + scale: buttonScaleAnimation.value, + //child: Widgets.chatSendBtnWidget(widget.isSending, isaudio: true), + child: Container( + height: size, + width: size, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: context.color.tertiaryColor, + //color: Theme.of(context).primaryColor, + ), + child: widget.isSending + ? const CircularProgressIndicator() + : const Icon( + Icons.mic, + color: Colors.white, + ), + ), + ), + onLongPressDown: (_) { + if (widget.isSending) return; + debugPrint("onLongPressDown"); + widget.controller.forward(); + }, + onLongPressEnd: (details) async { + if (widget.isSending) return; + debugPrint("onLongPressEnd"); + + if (isCancelled(details.localPosition, context)) { + if (await Vibrate.canVibrate) Vibrate.feedback(FeedbackType.heavy); + + timer?.cancel(); + timer = null; + startTime = null; + recordDuration = "00:00"; + + setState(() { + showLottie = true; + }); + + Timer(const Duration(milliseconds: 1440), () async { + widget.controller.reverse(); + debugPrint("Cancelled recording"); + var filePath = await record!.stop(); + debugPrint(filePath); + File(filePath!).delete(); + debugPrint("Deleted $filePath"); + showLottie = false; + }); + } else if (checkIsLocked(details.localPosition)) { + widget.controller.reverse(); + + if (await Vibrate.canVibrate) Vibrate.feedback(FeedbackType.heavy); + debugPrint("Locked recording"); + debugPrint(details.localPosition.dy.toString()); + setState(() { + isLocked = true; + }); + } else { + widget.controller.reverse(); + saveFile(); + /*Vibrate.feedback(FeedbackType.success); + + timer?.cancel(); + timer = null; + startTime = null; + recordDuration = "00:00"; + + var filePath = await Record().stop(); + AudioState.files.add(filePath!); + Globals.audioListKey.currentState! + .insertItem(AudioState.files.length - 1); + debugPrint(filePath);*/ + } + }, + onLongPressCancel: () { + if (widget.isSending) return; + debugPrint("onLongPressCancel"); + widget.controller.reverse(); + }, + onLongPress: () async { + if (widget.isSending) return; + debugPrint("onLongPress"); + if (await Vibrate.canVibrate) Vibrate.feedback(FeedbackType.success); + if (await Record().hasPermission()) { + record = Record(); + await record!.start( + path: + "${ChatGlobals.documentPath}audio_${DateTime.now().millisecondsSinceEpoch}.m4a", + encoder: AudioEncoder.aacLc, + //encoder: AudioEncoder.AAC, + bitRate: 128000, + samplingRate: 44100, + ); + startTime = DateTime.now(); + // print("duration==$recordDuration"); + timer = Timer.periodic(const Duration(seconds: 1), (_) { + final minDur = DateTime.now().difference(startTime!).inMinutes; + final secDur = DateTime.now().difference(startTime!).inSeconds % 60; + String min = minDur < 10 ? "0$minDur" : minDur.toString(); + String sec = secDur < 10 ? "0$secDur" : secDur.toString(); + // print("duration==$min:$sec"); + setState(() { + recordDuration = "$min:$sec"; + }); + }); + } + }, + ); + } + + Future saveFile() async { + if (await Vibrate.canVibrate) Vibrate.feedback(FeedbackType.success); + timer?.cancel(); + timer = null; + startTime = null; + recordDuration = "00:00"; + + var filePath = await Record().stop(); + AudioState.files.add(filePath!); + if (ChatGlobals.audioListKey.currentState != null) { + ChatGlobals.audioListKey.currentState! + .insertItem(AudioState.files.length - 1); + } + debugPrint(filePath); + if (widget.callback != null) { + widget.callback!(filePath); + } + } + + bool checkIsLocked(Offset offset) { + return (offset.dy < -35); + } + + bool isCancelled(Offset offset, BuildContext context) { + return (offset.dx < -(MediaQuery.of(context).size.width * 0.2)); + } +} diff --git a/lib/Ui/screens/chat/chat_list_screen.dart b/lib/Ui/screens/chat/chat_list_screen.dart new file mode 100644 index 0000000..d6da7d9 --- /dev/null +++ b/lib/Ui/screens/chat/chat_list_screen.dart @@ -0,0 +1,405 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:ebroker/Ui/screens/main_activity.dart'; +import 'package:ebroker/app/app.dart'; +import 'package:ebroker/app/default_app_setting.dart'; +import 'package:ebroker/data/cubits/chatCubits/delete_message_cubit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:shimmer/shimmer.dart'; + +import '../../../app/app_theme.dart'; +import '../../../data/cubits/chatCubits/get_chat_users.dart'; +import '../../../data/cubits/chatCubits/load_chat_messages.dart'; +import '../../../data/cubits/system/app_theme_cubit.dart'; +import '../../../data/model/chat/chated_user_model.dart'; +import '../../../utils/AppIcon.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/Notification/notification_service.dart'; +import '../../../utils/api.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/Erros/no_internet.dart'; +import '../widgets/Erros/something_went_wrong.dart'; +import '../widgets/shimmerLoadingContainer.dart'; +import 'chat_screen.dart'; + +class ChatListScreen extends StatefulWidget { + const ChatListScreen({super.key}); + static Route route(RouteSettings settings) { + return BlurredRouter( + builder: (context) { + return const ChatListScreen(); + }, + ); + } + + @override + State createState() => _ChatListScreenState(); +} + +class _ChatListScreenState extends State + with AutomaticKeepAliveClientMixin { + @override + void initState() { + chatScreenController.addListener(() { + if (chatScreenController.isEndReached()) { + if (context.read().hasMoreData()) { + context.read().loadMore(); + } + } + }); + + context.read().fetch(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + return AnnotatedRegion( + value: SystemUiOverlayStyle( + systemNavigationBarDividerColor: Colors.transparent, + systemNavigationBarIconBrightness: + context.watch().state.appTheme == AppTheme.dark + ? Brightness.light + : Brightness.dark, + // + statusBarColor: Theme.of(context).colorScheme.secondaryColor, + statusBarBrightness: + context.watch().state.appTheme == AppTheme.dark + ? Brightness.dark + : Brightness.light, + statusBarIconBrightness: + context.watch().state.appTheme == AppTheme.dark + ? Brightness.light + : Brightness.dark), + child: Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: UiUtils.buildAppBar( + context, + title: UiUtils.translate(context, "message"), + ), + body: BlocBuilder( + builder: (context, state) { + if (state is GetChatListFailed) { + if (state.error is ApiException) { + if (state.error.errorMessage == "no-internet") { + return NoInternet(onRetry: () { + context.read().fetch(); + }); + } + } + + return const SomethingWentWrong(); + } + + if (state is GetChatListInProgress) { + return buildChatListLoadingShimmer(); + } + if (state is GetChatListSuccess) { + if (state.chatedUserList.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset(AppIcons.no_chat_found), + const SizedBox( + height: 20, + ), + Text(UiUtils.translate(context, "noChats")) + .color(context.color.tertiaryColor) + .size(context.font.extraLarge) + .bold(weight: FontWeight.w600), + const SizedBox( + height: 14, + ), + Text("startConversation".translate(context)) + .size(context.font.larger) + .centerAlign(), + ], + ), + ); + } + return Column( + children: [ + Expanded( + child: ListView.builder( + controller: chatScreenController, + shrinkWrap: true, + itemCount: state.chatedUserList.length, + padding: const EdgeInsetsDirectional.all(16), + itemBuilder: ( + context, + index, + ) { + ChatedUser chatedUser = state.chatedUserList[index]; + + return Padding( + padding: const EdgeInsets.only(top: 9.0), + child: ChatTile( + id: chatedUser.userId.toString(), + propertyId: chatedUser.propertyId.toString(), + profilePicture: chatedUser.profile ?? "", + userName: chatedUser.name ?? "", + propertyPicture: chatedUser.titleImage ?? "", + propertyName: chatedUser.title ?? "", + pendingMessageCount: "5", + ), + ); + }), + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ); + } + + return Container(); + }, + ), + ), + ); + } + + Widget buildChatListLoadingShimmer() { + return ListView.builder( + itemCount: 10, + physics: BouncingScrollPhysics(), + padding: const EdgeInsetsDirectional.all(16), + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.only(top: 9.0), + child: Container( + height: 74, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Row( + children: [ + Shimmer.fromColors( + baseColor: Theme.of(context).colorScheme.shimmerBaseColor, + highlightColor: + Theme.of(context).colorScheme.shimmerHighlightColor, + child: Stack( + children: [ + const SizedBox( + width: 58, + height: 58, + ), + GestureDetector( + onTap: () {}, + child: Container( + width: 42, + height: 42, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Colors.grey, + border: Border.all( + width: 1.5, color: Colors.white), + borderRadius: BorderRadius.circular(10)), + ), + ), + PositionedDirectional( + end: 0, + bottom: 0, + child: GestureDetector( + onTap: () {}, + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.white, width: 2)), + child: CircleAvatar( + radius: 15, + backgroundColor: context.color.tertiaryColor, + // backgroundImage: NetworkImage(profilePicture), + ), + ), + ), + ) + ], + ), + ), + const SizedBox( + width: 10, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + CustomShimmer( + height: 10, + borderRadius: 5, + width: context.screenWidth * 0.53, + ), + CustomShimmer( + height: 10, + borderRadius: 5, + width: context.screenWidth * 0.3, + ) + ], + ) + ], + ), + ), + ), + ); + }); + } + + @override + bool get wantKeepAlive => false; +} + +class ChatTile extends StatelessWidget { + final String profilePicture; + final String userName; + final String propertyPicture; + final String propertyName; + final String propertyId; + final String pendingMessageCount; + final String id; + const ChatTile({ + super.key, + required this.profilePicture, + required this.userName, + required this.propertyPicture, + required this.propertyName, + required this.pendingMessageCount, + required this.id, + required this.propertyId, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + Navigator.push(context, BlurredRouter( + builder: (context) { + currentlyChatingWith = id; + currentlyChatPropertyId = propertyId; + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => LoadChatMessagesCubit(), + ), + BlocProvider( + create: (context) => DeleteMessageCubit(), + ), + ], + child: Builder(builder: (context) { + return ChatScreen( + profilePicture: profilePicture, + proeprtyTitle: propertyName, + userId: id, + propertyImage: propertyPicture, + userName: userName, + propertyId: propertyId, + ); + }), + ); + }, + )); + }, + child: AbsorbPointer( + absorbing: true, + child: Container( + height: 74, + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: context.color.borderColor, + width: 1.5, + ), + ), + width: MediaQuery.of(context).size.width, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Row( + children: [ + Stack( + children: [ + const SizedBox( + width: 58, + height: 58, + ), + GestureDetector( + onTap: () { + UiUtils.showFullScreenImage(context, + provider: + CachedNetworkImageProvider(propertyPicture)); + }, + child: Container( + width: 42, + height: 42, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10)), + child: CachedNetworkImage( + imageUrl: propertyPicture, + fit: BoxFit.cover, + ), + ), + ), + PositionedDirectional( + end: 0, + bottom: 0, + child: GestureDetector( + onTap: () { + UiUtils.showFullScreenImage(context, + provider: + CachedNetworkImageProvider(profilePicture)); + }, + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + border: + Border.all(color: Colors.white, width: 2)), + child: profilePicture == "" + ? CircleAvatar( + radius: 15, + backgroundColor: context.color.tertiaryColor, + child: LoadAppSettings().svg( + appSettings.placeholderLogo!, + color: context.color.buttonColor, + ), + ) + : CircleAvatar( + radius: 15, + backgroundColor: context.color.tertiaryColor, + backgroundImage: NetworkImage(profilePicture), + ), + ), + ), + ) + ], + ), + const SizedBox( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + userName, + ).bold().color(context.color.textColorDark), + Expanded( + child: Text( + propertyName, + ) + .color(context.color.textColorDark) + .setMaxLines(lines: 1), + ), + ], + ), + ) + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/chat/chat_screen.dart b/lib/Ui/screens/chat/chat_screen.dart new file mode 100644 index 0000000..ad4f268 --- /dev/null +++ b/lib/Ui/screens/chat/chat_screen.dart @@ -0,0 +1,1000 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:ebroker/Ui/screens/ChatNew/MessageTypes/blueprint.dart'; +import 'package:ebroker/Ui/screens/ChatNew/MessageTypes/registerar.dart'; +import 'package:ebroker/Ui/screens/ChatNew/model.dart'; +import 'package:ebroker/Ui/screens/widgets/blurred_dialoge_box.dart'; +import 'package:ebroker/app/app.dart'; +import 'package:ebroker/data/cubits/chatCubits/delete_message_cubit.dart'; +import 'package:ebroker/utils/customHeroAnimation.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:permission_handler/permission_handler.dart'; + +import '../../../app/default_app_setting.dart'; +import '../../../app/routes.dart'; +import '../../../data/Repositories/property_repository.dart'; +import '../../../data/cubits/chatCubits/load_chat_messages.dart'; +import '../../../data/cubits/chatCubits/send_message.dart'; +import '../../../data/helper/widgets.dart'; +import '../../../data/model/data_output.dart'; +import '../../../data/model/property_model.dart'; +import '../../../utils/AppIcon.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/Notification/chat_message_handler.dart'; +import '../../../utils/Notification/notification_service.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/hive_utils.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/transparant_route.dart'; +import 'chatAudio/widgets/chat_widget.dart'; +import 'chatAudio/widgets/record_button.dart'; + +int totalMessageCount = 0; + +ValueNotifier showDeletebutton = ValueNotifier(false); + +ValueNotifier selectedMessageid = ValueNotifier(-5); +ValueNotifier selectedRecieverId = ValueNotifier(-5); + +class ChatScreen extends StatefulWidget { + final String? from; + final String profilePicture; + final String userName; + final String propertyImage; + final String proeprtyTitle; + final String userId; //for which we are messageing + final String propertyId; + const ChatScreen( + {super.key, + required this.profilePicture, + required this.userName, + required this.propertyImage, + required this.proeprtyTitle, + required this.userId, + required this.propertyId, + this.from}); + @override + State createState() => _ChatScreenState(); +} + +class _ChatScreenState extends State + with SingleTickerProviderStateMixin { + late final AnimationController _recordButtonAnimation = AnimationController( + vsync: this, + duration: const Duration( + milliseconds: 500, + ), + ); + TextEditingController controller = TextEditingController(); + PlatformFile? messageAttachment; + bool isFetchedFirstTime = false; + double scrollPositionWhenLoadMore = 0; + late Stream notificationStream = notificationPermission(); + late StreamSubscription notificationStreamSubsctription; + bool isNotificationPermissionGranted = true; + ValueNotifier showRecordButton = ValueNotifier(true); + late final ScrollController _pageScrollController = ScrollController() + ..addListener( + () { + ContextMenuController.removeAny(); + if (_pageScrollController.offset >= + _pageScrollController.position.maxScrollExtent) { + if (context.read().hasMoreChat()) { + // setState(() {}); + context.read().loadMore(); + } + } + }, + ); + @override + void initState() { + Permission.storage.request(); + + context.read().load( + userId: int.parse( + widget.userId, + ), + propertyId: int.parse( + widget.propertyId, + ), + ); + + currentlyChatPropertyId = widget.propertyId; + currentlyChatingWith = widget.userId; + notificationStreamSubsctription = + notificationStream.listen((PermissionStatus permissionStatus) { + isNotificationPermissionGranted = permissionStatus.isGranted; + if (mounted) { + // setState(() {}); + } + }); + controller.addListener(() { + if (controller.text.isNotEmpty) { + showRecordButton.value = false; + } else { + showRecordButton.value = true; + } + }); + super.initState(); + } + + Stream notificationPermission() async* { + while (true) { + await Future.delayed(const Duration(seconds: 5)); + yield* Permission.notification.request().asStream(); + } + } + + @override + void dispose() { + showRecordButton.dispose(); + _recordButtonAnimation.dispose(); + + notificationStreamSubsctription.cancel(); + super.dispose(); + } + + List supportedImageTypes = [ + 'jpeg', + 'jpg', + 'png', + 'gif', + 'webp', + 'animated_webp', + ]; + + String getSendMessageType( + String? audio, dynamic attachment, String? message) { + if (audio != null) { + return "audio"; + } else { + if (attachment != null && (message != null)) { + return "file_and_text"; + } else if (attachment != null && message == null) { + return "file"; + } else { + return "text"; + } + } + } + + @override + Widget build(BuildContext context) { + var chatBackground = "assets/chat_background/light.svg"; + var attachmentMIME = ""; + if (messageAttachment != null) { + attachmentMIME = + (messageAttachment?.path?.split(".").last.toLowerCase()) ?? ""; + } + // return Container(); + + return WillPopScope( + onWillPop: () async { + currentlyChatingWith = ""; + showDeletebutton.value = false; + ChatMessageHandler.flush(); + currentlyChatPropertyId = ""; + notificationStreamSubsctription.cancel(); + ChatMessageHandlerOLD.flushMessages(); + return true; + }, + child: SafeArea( + child: Scaffold( + backgroundColor: context.color.backgroundColor, + bottomNavigationBar: Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom, + ), + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () {}, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (messageAttachment != null) ...[ + if (supportedImageTypes.contains(attachmentMIME)) ...[ + Container( + decoration: BoxDecoration( + color: context.color.secondaryColor, + border: Border.all( + color: context.color.borderColor, width: 1.5)), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: SizedBox( + height: 100, + width: 100, + child: GestureDetector( + onTap: () { + UiUtils.showFullScreenImage(context, + provider: FileImage(File( + messageAttachment?.path ?? "", + ))); + }, + child: Image.file( + File( + messageAttachment?.path ?? "", + ), + fit: BoxFit.cover, + ), + )), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(messageAttachment?.name ?? ""), + Text(HelperUtils.getFileSizeString( + bytes: messageAttachment!.size, + ).toString()), + ], + ) + ], + ), + ) + ] else ...[ + Container( + color: context.color.secondaryColor, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: AttachmentMessage( + url: messageAttachment!.path!, isSentByMe: true), + ), + ), + ], + const SizedBox( + height: 10, + ), + ], + BottomAppBar( + padding: const EdgeInsetsDirectional.all(10), + elevation: 5, + color: context.color.secondaryColor, + child: Directionality( + textDirection: TextDirection.ltr, + child: Row( + children: [ + Expanded( + child: TextField( + controller: controller, + cursorColor: context.color.tertiaryColor, + onTap: () { + showDeletebutton.value = false; + }, + textInputAction: TextInputAction.newline, + minLines: 1, + maxLines: null, + decoration: InputDecoration( + suffixIconColor: context.color.textLightColor, + suffixIcon: IconButton( + onPressed: () async { + if (messageAttachment == null) { + FilePickerResult? pickedAttachment = + await FilePicker.platform.pickFiles( + allowMultiple: false, + ); + + messageAttachment = + pickedAttachment?.files.first; + showRecordButton.value = false; + setState(() {}); + } else { + messageAttachment = null; + showRecordButton.value = true; + setState(() {}); + } + }, + icon: messageAttachment != null + ? const Icon(Icons.close) + : Transform.rotate( + angle: -3.14 / 5.0, + child: const Icon( + Icons.attachment, + ), + ), + ), + contentPadding: const EdgeInsets.symmetric( + vertical: 6, horizontal: 8), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + borderSide: BorderSide( + color: context.color.tertiaryColor)), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + borderSide: BorderSide( + color: context.color.tertiaryColor)), + hintText: UiUtils.translate( + context, + "writeHere", + ), + ), + ), + ), + SizedBox( + width: 9.5, + ), + ValueListenableBuilder( + valueListenable: showRecordButton, + builder: (context, bool show, Widget? child) { + if (show == true) { + return RecordButton( + controller: _recordButtonAnimation, + callback: (path) { + if (Constant.isDemoModeOn) { + HelperUtils.showSnackBarMessage( + context, + UiUtils.translate(context, + "thisActionNotValidDemo")); + return; + } + ChatMessageModel chatMessageModel = + ChatMessageModel( + message: controller.text, + isSentByMe: true, + audio: path, + senderId: HiveUtils.getUserId() + .toString(), + id: DateTime.now().toString(), + propertyId: widget.propertyId, + receiverId: widget.userId, + chatMessageType: + getSendMessageType( + path, + messageAttachment, + controller.text), + date: DateTime.now().toString(), + isSentNow: true); + ChatMessageHandler.add( + chatMessageModel, + ); + _pageScrollController.jumpTo( + _pageScrollController.offset - 10); + //This is adding Chat widget in stream with BlocProvider , because we will need to do api process to store chat message to server, when it will be added to list it's initState method will be called + // ChatMessageHandlerOLD.add(BlocProvider( + // create: (context) => SendMessageCubit(), + // child: ChatMessage( + // key: ValueKey( + // DateTime.now().toString().toString()), + // message: "[AUDIO]", + // senderId: HiveUtils.getUserId().toString(), + // propertyId: widget.propertyId, + // reciverId: widget.userId, + // time: DateTime.now().toString(), + // hasAttachment: false, + // isSentByMe: true, + // isChatAudio: true, + // isSentNow: true, + // audioFile: path, + // ), + // )); + totalMessageCount++; + + setState(() {}); + }, + isSending: false, + ); + } + return GestureDetector( + onTap: () { + showDeletebutton.value = false; + + //if file is selected then user can send message without text + if (controller.text.trim().isEmpty && + messageAttachment == null) return; + //This is adding Chat widget in stream with BlocProvider , because we will need to do api process to store chat message to server, when it will be added to list it's initState method will be called + if (Constant.isDemoModeOn) { + HelperUtils.showSnackBarMessage( + context, + UiUtils.translate(context, + "thisActionNotValidDemo")); + return; + } + ChatMessageModel chatMessageModel = + ChatMessageModel( + message: controller.text, + isSentByMe: true, + file: messageAttachment?.path, + senderId: HiveUtils.getUserId() + .toString(), + id: DateTime.now().toString(), + propertyId: widget.propertyId, + receiverId: widget.userId, + chatMessageType: getSendMessageType( + null, + messageAttachment, + controller.text.isEmpty + ? null + : controller.text), + date: DateTime.now().toString(), + isSentNow: true); + ChatMessageHandler.add(chatMessageModel); + controller.text = ""; + messageAttachment = null; + + // ChatMessageHandlerOLD.add( + // BlocProvider( + // key: ValueKey( + // DateTime.now().toString().toString()), + // create: (context) => SendMessageCubit(), + // child: ChatMessage( + // key: ValueKey( + // DateTime.now().toString().toString()), + // message: controller.text, + // hasAttachment: messageAttachment != null, + // senderId: + // HiveUtils.getUserId().toString(), + // propertyId: widget.propertyId, + // reciverId: widget.userId, + // time: DateTime.now().toString(), + // isSentByMe: true, + // isChatAudio: false, + // isSentNow: true, + // attachment: messageAttachment?.path, + // ), + // ), + // ); + totalMessageCount++; + messageAttachment = null; + if (mounted) setState(() {}); + }, + child: CircleAvatar( + radius: 20, + backgroundColor: + context.color.tertiaryColor, + child: Icon( + Icons.send, + color: context.color.buttonColor, + ), + ), + ); + }), + ], + ), + ), + ), + ], + ), + ), + ), + appBar: AppBar( + centerTitle: false, + automaticallyImplyLeading: false, + leading: FittedBox( + fit: BoxFit.none, + child: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Padding( + padding: const EdgeInsets.only(left: 20.0), + child: SvgPicture.asset( + AppIcons.arrowLeft, + color: context.color.tertiaryColor, + height: 24, + ), + ), + ), + ), + leadingWidth: 24, + backgroundColor: context.color.secondaryColor, + elevation: 0, + iconTheme: IconThemeData(color: context.color.tertiaryColor), + bottom: isNotificationPermissionGranted + ? null + : PreferredSize( + preferredSize: const Size.fromHeight(25), + child: FittedBox( + fit: BoxFit.cover, + child: Container( + width: context.screenWidth, + color: const Color.fromARGB(255, 151, 151, 151), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text("turnOnNotification".translate(context)), + ), + ), + ), + ), + actions: [ + ValueListenableBuilder( + valueListenable: showDeletebutton, + builder: (context, value, child) { + if (value == false) return const SizedBox.shrink(); + return IconButton( + onPressed: () { + UiUtils.showBlurredDialoge( + context, + dialoge: BlurredDialogBox( + onAccept: () async { + context.read().delete( + (selectedMessageid.value), + receiverId: selectedRecieverId.value); + showDeletebutton.value = false; + }, + title: "areYouSure".translate(context), + content: Text( + "msgWillNotRecover".translate(context), + ), + ), + ); + }, + icon: SvgPicture.asset( + AppIcons.delete, + color: context.color.tertiaryColor, + )); + }, + ), + if (widget.from != "property") + FittedBox( + fit: BoxFit.none, + child: GestureDetector( + onTap: () async { + try { + Widgets.showLoader(context); + PropertyRepository fetch = PropertyRepository(); + DataOutput dataOutput = await fetch + .fetchPropertyFromPropertyId(widget.propertyId); + Future.delayed( + Duration.zero, + () { + Widgets.hideLoder(context); + + HelperUtils.goToNextPage( + Routes.propertyDetails, context, false, + args: { + 'propertyData': dataOutput.modelList[0], + 'propertiesList': dataOutput.modelList, + 'fromMyProperty': false, + }); + }, + ); + } catch (e) { + Widgets.hideLoder(context); + } + }, + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: SizedBox( + width: 40, + height: 40, + child: Image.network( + widget.propertyImage, + fit: BoxFit.cover, + ), + ), + ), + ), + ), + const SizedBox( + width: 18, + ), + ], + title: FittedBox( + fit: BoxFit.none, + child: Row( + children: [ + widget.profilePicture == "" + ? CircleAvatar( + backgroundColor: context.color.tertiaryColor, + child: LoadAppSettings().svg( + appSettings.placeholderLogo!, + color: context.color.buttonColor, + ), + ) + : GestureDetector( + onTap: () { + Navigator.push( + context, + TransparantRoute( + barrierDismiss: true, + builder: (context) { + return GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + color: const Color.fromARGB(69, 0, 0, 0), + ), + ); + }, + ), + ); + }, + child: CustomImageHeroAnimation( + type: CImageType.Network, + image: widget.profilePicture, + child: CircleAvatar( + backgroundImage: CachedNetworkImageProvider( + widget.profilePicture, + ), + ), + ), + ), + const SizedBox( + width: 10, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: context.screenWidth * 0.35, + child: Text(widget.userName) + .color(context.color.textColorDark) + .size(context.font.normal), + ), + SizedBox( + width: context.screenWidth * 0.35, + child: Text(widget.proeprtyTitle) + .size(context.font.small) + .color(context.color.textColorDark), + ), + ], + ) + ], + ), + ), + ), + body: Stack( + children: [ + SvgPicture.asset( + chatBackground, + height: MediaQuery.of(context).size.height, + fit: BoxFit.cover, + width: MediaQuery.of(context).size.width, + ), + BlocListener( + listener: (context, state) { + if (state is DeleteMessageSuccess) { + ChatMessageHandlerOLD.removeMessage(state.id); + showDeletebutton.value = false; + } + }, + child: GestureDetector( + onTap: () { + showDeletebutton.value = false; + }, + child: BlocConsumer( + listener: (context, state) { + if (state is LoadChatMessagesSuccess) { + ChatMessageHandler.fillMessages(state.messages); + + // ChatMessageHandlerOLD.loadMessages( + // state.messages, context); + totalMessageCount = state.messages.length; + isFetchedFirstTime = true; + setState(() {}); + } + + if (state is LoadChatMessagesFailed) {} + }, + builder: (context, state) { + return Stack( + children: [ + Column( + children: [ + if (state is LoadChatMessagesSuccess) ...{ + if (state.isLoadingMore) ...{ + Center( + child: SizedBox( + width: 20, + height: 20, + child: UiUtils.progress()), + ), + } + }, + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 18), + child: StreamBuilder( + stream: ChatMessageHandler.listenMessages(), + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.active) { + return SizedBox( + height: context.screenHeight, + child: ListView.builder( + reverse: true, + shrinkWrap: true, + addAutomaticKeepAlives: true, + controller: _pageScrollController, + physics: + const BouncingScrollPhysics(), + itemBuilder: (context, index) { + List messageList = + snapshot.data!; + messageList = + messageList.toList(); + DateTime? currentDate = + messageList[index] + .message + ?.date + ?.parseAsDate(); + + DateTime? nextDate; + + if (messageList.length > + index + 1) { + nextDate = + messageList[index + 1] + .message + ?.date + ?.parseAsDate(); + } + + Widget dateChip = getDateChip( + currentDate!, nextDate); + + if (index == + messageList.length - 1) { + dateChip = Padding( + padding: const EdgeInsets.all( + 10.0), + child: Text(currentDate + .toString() + .formatDate()), + ); + } + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => + SendMessageCubit(), + ), + BlocProvider( + create: (context) => + DeleteMessageCubit(), + ), + ], + child: Column( + children: [ + dateChip, + RenderMessage( + key: Key( + messageList[index] + .id), + message: + messageList[index]), + ], + ), + ); + }, + // separatorBuilder: (context, index) { + // List messageList = + // snapshot.data!; + // messageList = messageList.toList(); + // + // String? currentMessageDate = + // messageList[index].message!.date; + // + // String? nextMessageDate = + // messageList[index + 1].message!.date; + // + // DateTime current = + // DateTime.parse(currentMessageDate!); + // DateTime next = + // DateTime.parse(nextMessageDate!); + // if (index == 4) { + // next = DateTime.parse(nextMessageDate!) + // .add(Duration(days: 2)); + // } + // bool sameDate = isSameDate(current, next); + // + // if (sameDate == true) { + // return Center( + // child: Text(messageList[index] + // .message! + // .date! + + // "($sameDate)")); + // } + // return SizedBox.shrink(); + // }, + itemCount: snapshot.data!.length, + ), + ); + } + return Container(); + }, + ), + ), + ), + ], + ), + // StreamBuilder( + // stream: ChatMessageHandler.getChatStream(), + // builder: (context, AsyncSnapshot snapshot) { + // Widget? loadingMoreWidget; + // if (state is LoadChatMessagesSuccess) { + // if (state.isLoadingMore) { + // loadingMoreWidget = Text( + // UiUtils.getTranslatedLabel( + // context, "loading")); + // } + // } + // + // if (snapshot.connectionState == + // ConnectionState.active || + // snapshot.connectionState == + // ConnectionState.done) { + // return Column( + // children: [ + // loadingMoreWidget ?? + // const SizedBox.shrink(), + // Expanded( + // child: ListView.builder( + // reverse: true, + // shrinkWrap: true, + // physics: + // const BouncingScrollPhysics(), + // addAutomaticKeepAlives: true, + // controller: _pageScrollController, + // itemCount: snapshot.data.length, + // padding: + // const EdgeInsets.only(bottom: 10), + // itemBuilder: (context, index) { + // dynamic chat = + // (snapshot.data as List) + // .elementAt(index); + // + // return chat; + // }, + // ), + // ), + // ], + // ); + // } + // + // return Container(); + // }), + if ((state is LoadChatMessagesInProgress)) + Center( + child: UiUtils.progress(), + ) + ], + ); + }, + ), + ), + ), + ], + ), + ), + ), + ); + } +} + +Widget getDateChip(DateTime currentDate, DateTime? nextDate) { + if (nextDate == null) { + return const SizedBox.shrink(); + } + + bool sameDate = currentDate.isSameDate(nextDate); + + if (sameDate == false) { + return Padding( + padding: const EdgeInsets.all(10.0), + child: Text(currentDate.toString().formatDate()).size(15), + ); + } + + return const SizedBox.shrink(); +} + +class ChatInfoWidget extends StatelessWidget { + final String propertyTitleImage; + final String propertyTitle; + final String propertyId; + const ChatInfoWidget( + {super.key, + required this.propertyTitleImage, + required this.propertyTitle, + required this.propertyId}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + iconTheme: IconThemeData(color: context.color.tertiaryColor), + ), + backgroundColor: Colors.transparent, + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + height: context.screenHeight * 0.46, + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(10)), + width: context.screenWidth, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: GestureDetector( + onTap: () { + UiUtils.showFullScreenImage(context, + provider: + CachedNetworkImageProvider(propertyTitleImage)); + }, + child: CachedNetworkImage( + imageUrl: propertyTitleImage, + width: context.screenWidth, + fit: BoxFit.cover, + height: context.screenHeight * 0.3, + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Align( + alignment: Alignment.centerLeft, + child: Text(propertyTitle) + .setMaxLines( + lines: 2, + ) + .size( + context.font.larger.rf( + context, + ), + ), + ), + ), + const Spacer(), + Padding( + padding: const EdgeInsets.all(8.0), + child: FittedBox( + fit: BoxFit.none, + child: UiUtils.buildButton(context, onPressed: () async { + try { + Widgets.showLoader(context); + PropertyRepository fetch = PropertyRepository(); + DataOutput dataOutput = await fetch + .fetchPropertyFromPropertyId(propertyId); + Future.delayed( + Duration.zero, + () { + Widgets.hideLoder(context); + + HelperUtils.goToNextPage( + Routes.propertyDetails, context, false, + args: { + 'propertyData': dataOutput.modelList[0], + 'propertiesList': dataOutput.modelList, + 'fromMyProperty': false, + }); + }, + ); + } catch (e) { + Widgets.hideLoder(context); + } + }, + buttonTitle: + UiUtils.translate(context, "viewProperty"), + width: context.screenWidth * 0.5, + fontSize: context.font.normal, + height: 40), + ), + ) + ], + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/Ui/screens/favorites_screen.dart b/lib/Ui/screens/favorites_screen.dart new file mode 100644 index 0000000..12ced13 --- /dev/null +++ b/lib/Ui/screens/favorites_screen.dart @@ -0,0 +1,245 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../app/routes.dart'; +import '../../data/cubits/Utility/like_properties.dart'; +import '../../data/cubits/favorite/add_to_favorite_cubit.dart'; +import '../../data/cubits/favorite/fetch_favorites_cubit.dart'; +import '../../data/helper/designs.dart'; +import '../../data/model/property_model.dart'; +import '../../utils/Extensions/extensions.dart'; +import '../../utils/api.dart'; +import '../../utils/responsiveSize.dart'; +import '../../utils/ui_utils.dart'; +import 'home/Widgets/property_horizontal_card.dart'; +import 'widgets/AnimatedRoutes/blur_page_route.dart'; +import 'widgets/Erros/no_data_found.dart'; +import 'widgets/Erros/no_internet.dart'; +import 'widgets/Erros/something_went_wrong.dart'; +import 'widgets/shimmerLoadingContainer.dart'; + +class FavoritesScreen extends StatefulWidget { + const FavoritesScreen({ + super.key, + }); + + static Route route(RouteSettings settings) { + return BlurredRouter( + builder: (context) => BlocProvider( + create: (context) => FetchFavoritesCubit(), + child: const FavoritesScreen(), + ), + ); + } + + @override + State createState() => _FavoritesScreenState(); +} + +class _FavoritesScreenState extends State { + final ScrollController _pageScrollController = ScrollController(); + @override + void initState() { + _pageScrollController.addListener(_pageScrollListen); + context.read().fetchFavorites(); + super.initState(); + } + + void _pageScrollListen() { + if (_pageScrollController.isEndReached()) { + if (context.read().hasMoreData()) { + context.read().fetchFavoritesMore(); + } + } + } + + @override + void dispose() { + _pageScrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return RefreshIndicator( + onRefresh: () async {}, + color: context.color.tertiaryColor, + child: Scaffold( + backgroundColor: Theme.of(context).colorScheme.primaryColor, + appBar: UiUtils.buildAppBar( + context, + showBackButton: true, + title: UiUtils.translate( + context, + "favorites", + ), + ), + body: BlocBuilder( + builder: (context, state) { + if (state is FetchFavoritesInProgress) { + return shimmerEffect(); + } + if (state is FetchFavoritesFailure) { + if (state.errorMessage is ApiException) { + if ((state.errorMessage as ApiException).errorMessage == + "no-internet") { + return NoInternet( + onRetry: () { + context.read().fetchFavorites(); + }, + ); + } + } + return const SomethingWentWrong(); + } + if (state is FetchFavoritesSuccess) { + if (state.propertymodel.isEmpty) { + return SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: SizedBox( + height: context.screenHeight - 100.rh(context), + child: Center( + child: NoDataFound( + onTap: () { + context.read().fetchFavorites(); + }, + ), + ), + ), + ); + } + + return Column( + children: [ + Expanded( + child: ListView.builder( + controller: _pageScrollController, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.all(16.0), + itemCount: state.propertymodel.length, + shrinkWrap: true, + itemBuilder: (context, index) { + PropertyModel property = state.propertymodel[index]; + context.read().add(property.id); + return BlocProvider( + create: (context) => AddToFavoriteCubitCubit(), + child: GestureDetector( + onTap: () { + Navigator.pushNamed( + context, + Routes.propertyDetails, + arguments: { + 'propertyData': property, + 'fromMyProperty': true, + }, + ); + }, + child: PropertyHorizontalCard( + property: property, + onLikeChange: (type) { + if (type == FavoriteType.add) { + context + .read() + .add(state.propertymodel[index]); + } else { + context + .read() + .remove(state.propertymodel[index].id); + } + }, + ), + ), + ); + }, + ), + ), + if (state.isLoadingMore) + UiUtils.progress( + normalProgressColor: context.color.tertiaryColor, + ) + ], + ); + } + + return Container(); + }, + ), + ), + ); + } + + ListView shimmerEffect() { + return ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.symmetric( + vertical: 10 + defaultPadding, horizontal: defaultPadding), + itemCount: 5, + separatorBuilder: (context, index) { + return const SizedBox( + height: 12, + ); + }, + itemBuilder: (context, index) { + return Container( + width: double.maxFinite, + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ClipRRect( + clipBehavior: Clip.antiAliasWithSaveLayer, + borderRadius: BorderRadius.all(Radius.circular(15)), + child: CustomShimmer(height: 90, width: 90), + ), + const SizedBox( + width: 10, + ), + Expanded( + child: LayoutBuilder(builder: (context, c) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 10, + ), + CustomShimmer( + height: 10, + width: c.maxWidth - 50, + ), + const SizedBox( + height: 10, + ), + const CustomShimmer( + height: 10, + ), + const SizedBox( + height: 10, + ), + CustomShimmer( + height: 10, + width: c.maxWidth / 1.2, + ), + const SizedBox( + height: 10, + ), + Align( + alignment: Alignment.bottomLeft, + child: CustomShimmer( + width: c.maxWidth / 4, + ), + ), + ], + ); + }), + ) + ]), + ); + }, + ); + } +} diff --git a/lib/Ui/screens/filter_screen.dart b/lib/Ui/screens/filter_screen.dart new file mode 100644 index 0000000..6cb7c99 --- /dev/null +++ b/lib/Ui/screens/filter_screen.dart @@ -0,0 +1,773 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:ebroker/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import 'package:ebroker/Ui/screens/widgets/BottomSheets/choose_location_bottomsheet.dart'; +import 'package:ebroker/app/routes.dart'; +import 'package:ebroker/data/cubits/category/fetch_category_cubit.dart'; +import 'package:ebroker/data/model/category.dart'; +import 'package:ebroker/data/model/propery_filter_model.dart'; +import 'package:ebroker/utils/AdMob/bannerAdLoadWidget.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; + +import '../../data/model/google_place_model.dart'; +import '../../utils/api.dart'; +import '../../utils/constant.dart'; +import '../../utils/ui_utils.dart'; +import 'main_activity.dart'; + +dynamic city = ""; +dynamic _state = ""; +dynamic country = ""; + +class FilterScreen extends StatefulWidget { + final bool? showPropertyType; + const FilterScreen({ + Key? key, + this.showPropertyType, + }) : super(key: key); + + @override + FilterScreenState createState() => FilterScreenState(); + + static Route route(RouteSettings routeSettings) { + Map? arguments = routeSettings.arguments as Map?; + return BlurredRouter( + builder: (_) => FilterScreen( + showPropertyType: arguments?['showPropertyType'], + ), + ); + } +} + +class FilterScreenState extends State { + TextEditingController minController = + TextEditingController(text: Constant.propertyFilter?.minPrice); + TextEditingController maxController = + TextEditingController(text: Constant.propertyFilter?.maxPrice); + + //String properyType = Constant.valSellBuy; + String properyType = Constant.propertyFilter?.propertyType ?? ""; + String postedOn = Constant.propertyFilter?.postedSince ?? + Constant.filterAll; // = 2; // 0: last_week 1: yesterday + dynamic defaultCategoryID = currentVisitingCategoryId; + dynamic defaultCategory = currentVisitingCategory; + + @override + void dispose() { + minController.dispose(); + maxController.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + + setDefaultVal(isrefresh: false); + } + + void setDefaultVal({bool isrefresh = true}) { + if (isrefresh) { + postedOn = Constant.filterAll; + Constant.propertyFilter = null; + searchbody[Api.postedSince] = Constant.filterAll; + properyType = ""; + selectedcategoryId = "0"; + city = ""; + _state = ""; + country = ""; + selectedcategoryName = ""; + selectedCategory = defaultCategory; + + minController.clear(); + maxController.clear(); + checkFilterValSet(); + } + } + + bool checkFilterValSet() { + if (postedOn != Constant.filterAll || + properyType.isNotEmpty || + minController.text.trim().isNotEmpty || + maxController.text.trim().isNotEmpty || + selectedCategory != defaultCategory) { + return true; + } + + return false; + } + + void _onTapChooseLocation() async { + FocusManager.instance.primaryFocus?.unfocus(); + var result = await showModalBottomSheet( + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20), topRight: Radius.circular(20))), + context: context, + builder: (context) { + return const ChooseLocatonBottomSheet(); + }, + ); + if (result != null) { + GooglePlaceModel place = (result as GooglePlaceModel); + + city = place.city; + country = place.country; + _state = place.state; + } + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + checkFilterValSet(); + return true; + }, + child: Scaffold( + backgroundColor: Theme.of(context).colorScheme.primaryColor, + appBar: UiUtils.buildAppBar( + context, + onbackpress: () { + checkFilterValSet(); + }, + showBackButton: true, + title: UiUtils.translate(context, "filterTitle"), + actions: [ + if ((checkFilterValSet() == true)) ...[ + FittedBox( + fit: BoxFit.none, + child: UiUtils.buildButton( + context, + onPressed: () { + setDefaultVal(isrefresh: true); + setState(() {}); + }, + width: 100, + height: 50, + fontSize: context.font.normal, + buttonColor: context.color.secondaryColor, + showElevation: false, + textColor: context.color.textColorDark, + buttonTitle: UiUtils.translate( + context, + "clearfilter", + ), + ), + ) + ] + ], + ), + bottomNavigationBar: BottomAppBar( + child: UiUtils.buildButton(context, + outerPadding: + const EdgeInsets.symmetric(horizontal: 13, vertical: 5), + height: 50.rh(context), onPressed: () { + //this will set name of previous screen app bar + + if (widget.showPropertyType ?? false) { + if (selectedCategory == null) { + selectedcategoryName = ""; + } else { + selectedcategoryName = + (selectedCategory as Category).category ?? ""; + } + } + + Constant.propertyFilter = PropertyFilterModel( + propertyType: properyType, + maxPrice: maxController.text, + minPrice: minController.text, + categoryId: ((selectedCategory is String) + ? selectedCategory + : selectedCategory?.id) ?? + "", + postedSince: postedOn, + city: city, + state: _state, + country: country, + ); + + Navigator.pop(context, true); + }, buttonTitle: UiUtils.translate(context, "applyFilter")), + ), + body: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag, + child: Padding( + padding: const EdgeInsets.all( + 20.0, + ), + child: SizedBox( + height: context.screenHeight, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + //mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + const SizedBox(height: 10), + buyORsellOption(), + const SizedBox(height: 15), + if (widget.showPropertyType ?? true) ...[ + Text(UiUtils.translate(context, "proeprtyType")) + .size(context.font.large), + const SizedBox(height: 15), + BlocBuilder( + builder: (context, state) { + if (state is FetchCategorySuccess) { + List categoriesList = + List.from(state.categories); + categoriesList.insert(0, Category(id: "")); + return SizedBox( + height: 50, + child: ListView( + physics: const BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + shrinkWrap: true, + children: List.generate( + categoriesList.length.clamp(0, 8), + (int index) { + if (index == 0) { + return allCategoriesFilterButton(context); + } + + if (index == 7) { + return Padding( + padding: const EdgeInsetsDirectional.only( + start: 5.0), + child: moreCategoriesButton(context), + ); + } + return GestureDetector( + onTap: () { + selectedCategory = categoriesList[index]; + setState(() {}); + }, + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Container( + alignment: Alignment.center, + decoration: BoxDecoration( + color: selectedCategory == + categoriesList[index] + ? context.color.tertiaryColor + : context.color.secondaryColor, + borderRadius: + BorderRadius.circular(10), + border: Border.all( + width: 1.5, + color: context.color.borderColor, + ), + ), + height: 30, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, vertical: 8), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + UiUtils.imageType( + categoriesList[index].image!, + height: 20.rh(context), + width: 20.rw(context), + color: selectedCategory == + categoriesList[index] + ? context + .color.secondaryColor + : context + .color.tertiaryColor, + ), + SizedBox( + width: 10.rw(context), + ), + Text( + categoriesList[index] + .category + .toString(), + ).color(selectedCategory == + categoriesList[index] + ? context.color.textAutoAdapt( + context + .color.tertiaryColor) + : context + .color.textColorDark), + ], + ), + ), + ), + ), + ); + }, + ), + ), + ); + } + return Container(); + }, + ), + const SizedBox( + height: 15, + ), + ], + Text(UiUtils.translate(context, 'budgetLbl')), + const SizedBox(height: 10), + budgetOption(), + const SizedBox(height: 10), + const SizedBox(height: 5), + postedSinceOption(), + const SizedBox(height: 15), + Text(UiUtils.translate(context, 'locationLbl')), + const SizedBox(height: 5), + locationWidget(context), + const SizedBox( + height: 15, + ), + const BannerAdWidget( + bannerSize: AdSize.banner, + ) + ], + ), + ), + ), + ), + ), + ); + } + + Widget locationWidget(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 10.0), + child: Row( + children: [ + Expanded( + child: Container( + height: 55, + decoration: BoxDecoration( + color: context.color.textLightColor.withOpacity(00.01), + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: context.color.borderColor, + width: 1.5, + )), + child: Row( + children: [ + Padding( + padding: const EdgeInsetsDirectional.only(start: 10.0), + child: Align( + alignment: Alignment.centerLeft, + child: (city != "" && city != null) + ? Text("$city,$_state,$country") + : Text(UiUtils.translate( + context, "selectLocationOptional"))), + ), + const Spacer(), + if (city != "" && city != null) + Padding( + padding: const EdgeInsetsDirectional.only(end: 10.0), + child: GestureDetector( + onTap: _onTapChooseLocation, + child: Icon( + Icons.close, + color: context.color.textColorDark, + ), + ), + ) + ], + ), + ), + ), + const SizedBox( + width: 10, + ), + GestureDetector( + onTap: _onTapChooseLocation, + child: Container( + height: 55, + width: 55, + decoration: BoxDecoration( + color: context.color.textLightColor.withOpacity(00.01), + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: context.color.borderColor, + width: 1.5, + )), + child: Icon( + Icons.location_searching_sharp, + color: context.color.tertiaryColor, + ), + ), + ), + ], + ), + ); + } + + Widget allCategoriesFilterButton(BuildContext context) { + return GestureDetector( + onTap: () { + selectedCategory = null; + setState(() {}); + }, + child: Container( + width: 50, + margin: const EdgeInsetsDirectional.only(end: 5), + alignment: Alignment.center, + decoration: BoxDecoration( + color: selectedCategory == null + ? context.color.tertiaryColor + : context.color.secondaryColor, + borderRadius: BorderRadius.circular(10), + border: Border.all(width: 1.5, color: context.color.borderColor), + ), + height: 25, + child: Text(UiUtils.translate(context, "lblall")).color( + selectedCategory == null + ? context.color.textAutoAdapt(context.color.tertiaryColor) + : context.color.textColorDark), + ), + ); + } + + GestureDetector moreCategoriesButton(BuildContext context) { + return GestureDetector( + onTap: () { + Navigator.pushNamed(context, Routes.categories, + arguments: {"from": Routes.filterScreen}).then( + (dynamic value) { + if (value != null) { + selectedCategory = value; + setState(() {}); + } + }, + ); + }, + child: Container( + height: 25, + width: 100, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: context.color.secondaryColor, + border: Border.all( + color: context.color.borderColor, + width: 1.5, + )), + alignment: Alignment.center, + child: Text(UiUtils.translate(context, "more")), + ), + ); + } + + Widget saveFilter() { + //save prefs & validate fields & call API + return IconButton( + onPressed: () { + Constant.propertyFilter = PropertyFilterModel( + propertyType: properyType, + maxPrice: maxController.text, + city: city, + state: _state, + country: country, + minPrice: minController.text, + categoryId: selectedCategory?.id ?? "", + postedSince: postedOn, + ); + + Navigator.pop(context, true); + }, + icon: const Icon(Icons.check)); + } + + Widget buyORsellOption() { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity(0.2), + borderRadius: BorderRadius.circular(15)), + child: SizedBox( + width: MediaQuery.of(context).size.width - 40.rw(context), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + //buttonSale + Expanded( + child: SizedBox( + height: 46.rh(context), + child: UiUtils.buildButton(context, onPressed: () { + if (properyType == Constant.valSellBuy) { + searchbody[Api.propertyType] = ""; + properyType = ""; + setState(() {}); + } else { + setPropertyType(Constant.valSellBuy); + } + }, + showElevation: false, + textColor: properyType == Constant.valSellBuy + ? context.color.buttonColor + : context.color.textColorDark, + buttonColor: properyType == Constant.valSellBuy + ? Theme.of(context).colorScheme.tertiaryColor + : Theme.of(context) + .colorScheme + .tertiaryColor + .withOpacity(0.0), + fontSize: context.font.large, + buttonTitle: UiUtils.translate( + context, UiUtils.translate(context, "forSaleLbl"))), + ), + ), + //buttonRent + Expanded( + child: SizedBox( + height: 46.rh(context), + child: UiUtils.buildButton(context, onPressed: () { + if (properyType == Constant.valRent) { + searchbody[Api.propertyType] = ""; + properyType = ""; + setState(() {}); + } else { + setPropertyType(Constant.valRent); + } + }, + showElevation: false, + textColor: properyType == Constant.valRent + ? context.color.buttonColor + : context.color.textColorDark, + buttonColor: properyType == Constant.valRent + ? Theme.of(context).colorScheme.tertiaryColor + : Theme.of(context) + .colorScheme + .tertiaryColor + .withOpacity(0.0), + fontSize: context.font.large, + buttonTitle: UiUtils.translate(context, + UiUtils.translate(context, "forRentLbl")))), + ) + ], + ), + ), + ), + ], + ); + } + + void setPropertyType(String val) { + searchbody[Api.propertyType] = val; + + setState(() { + properyType = val; + }); + } + + Widget budgetOption() { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + minMaxTFF( + UiUtils.translate(context, "minLbl"), + ) + ], + ), + ), + const SizedBox(height: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + minMaxTFF(UiUtils.translate(context, "maxLbl")), + ], + ), + ), + ], + ); + } + + Widget minMaxTFF(String minMax) { + return Container( + padding: EdgeInsetsDirectional.only( + end: minMax == UiUtils.translate(context, "minLbl") ? 5 : 0), + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(10)), + color: Theme.of(context).colorScheme.backgroundColor), + child: TextFormField( + controller: (minMax == UiUtils.translate(context, "minLbl")) + ? minController + : maxController, + onChanged: ((value) { + bool isEmpty = value.trim().isEmpty; + if (minMax == UiUtils.translate(context, "minLbl")) { + if (isEmpty && searchbody.containsKey(Api.minPrice)) { + searchbody.remove(Api.minPrice); + } else { + searchbody[Api.minPrice] = value; + } + } else { + if (isEmpty && searchbody.containsKey(Api.maxPrice)) { + searchbody.remove(Api.maxPrice); + } else { + searchbody[Api.maxPrice] = value; + } + } + }), + textInputAction: TextInputAction.done, + decoration: InputDecoration( + isDense: true, + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: context.color.tertiaryColor)), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: context.color.tertiaryColor)), + labelStyle: TextStyle(color: context.color.tertiaryColor), + hintText: "00", + label: Text( + minMax, + ), + prefixText: '${Constant.currencySymbol} ', + prefixStyle: TextStyle( + color: Theme.of(context).colorScheme.tertiaryColor), + fillColor: Theme.of(context).colorScheme.secondaryColor, + border: const OutlineInputBorder()), + keyboardType: TextInputType.number, + style: + TextStyle(color: Theme.of(context).colorScheme.tertiaryColor), + /* onSubmitted: () */ + inputFormatters: [FilteringTextInputFormatter.digitsOnly])); + } + + Widget postedSinceOption() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // // Text( + // setMessageText( + // titleTxt: UiUtils.getTranslatedLabel(context, "postedSinceLbl"), + // txtColor: Theme.of(context).colorScheme.blackColor, + // txtStyle: Theme.of(context).textTheme.titleMedium, + // fontWeight: FontWeight.w500, + // context: context), + // Container( + // color: Theme.of(context).colorScheme.blackColor.withOpacity(0.5), + // height: 1, + // width: MediaQuery.of(context).size.width * 0.45, + // ), + // ], + // ), + Text(UiUtils.translate(context, "postedSinceLbl")) + .size(context.font.large), + SizedBox( + height: 10.rh(context), + ), + + SizedBox( + height: 45, + child: ListView( + shrinkWrap: true, + scrollDirection: Axis.horizontal, + children: [ + UiUtils.buildButton( + context, + fontSize: context.font.small, + showElevation: false, + autoWidth: true, + border: + BorderSide(color: context.color.borderColor, width: 1.5), + buttonColor: searchbody[Api.postedSince] == Constant.filterAll + ? context.color.tertiaryColor + : context.color.tertiaryColor.withOpacity(0.05), + textColor: searchbody[Api.postedSince] == Constant.filterAll + ? context.color.secondaryColor + : context.color.textColorDark, + buttonTitle: UiUtils.translate(context, "anytimeLbl"), + onPressed: () { + onClickPosted( + Constant.filterAll, + ); + setState(() {}); + }, + ), + SizedBox( + width: 5.rw(context), + ), + UiUtils.buildButton( + fontSize: context.font.small, + context, + autoWidth: true, + border: + BorderSide(color: context.color.borderColor, width: 1.5), + textColor: + searchbody[Api.postedSince] == Constant.filterLastWeek + ? context.color.secondaryColor + : context.color.textColorDark, + showElevation: false, + buttonColor: + searchbody[Api.postedSince] == Constant.filterLastWeek + ? context.color.tertiaryColor + : context.color.tertiaryColor.withOpacity(0.05), + buttonTitle: UiUtils.translate(context, "lastWeekLbl"), + onPressed: () { + onClickPosted( + Constant.filterLastWeek, + ); + }, + ), + SizedBox( + width: 5.rw(context), + ), + UiUtils.buildButton( + fontSize: context.font.small, + context, + autoWidth: true, + border: + BorderSide(color: context.color.borderColor, width: 1.5), + showElevation: false, + textColor: + searchbody[Api.postedSince] == Constant.filterYesterday + ? context.color.secondaryColor + : context.color.textColorDark, + buttonColor: + searchbody[Api.postedSince] == Constant.filterYesterday + ? context.color.tertiaryColor + : context.color.tertiaryColor.withOpacity(0.05), + buttonTitle: UiUtils.translate(context, "yesterdayLbl"), + onPressed: () { + onClickPosted( + Constant.filterYesterday, + ); + }, + ), + ], + ), + ) + ], + ); + } + + void onClickPosted(String val) { + if (val == Constant.filterAll && searchbody.containsKey(Api.postedSince)) { + searchbody[Api.postedSince] = ""; + } else { + searchbody[Api.postedSince] = val; + } + + postedOn = val; + setState(() {}); + } +} diff --git a/lib/Ui/screens/home/HomeSegments/sections.dart b/lib/Ui/screens/home/HomeSegments/sections.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/Ui/screens/home/Widgets/category_card.dart b/lib/Ui/screens/home/Widgets/category_card.dart new file mode 100644 index 0000000..b48a13d --- /dev/null +++ b/lib/Ui/screens/home/Widgets/category_card.dart @@ -0,0 +1,73 @@ +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:flutter/material.dart'; + +import '../../../../data/helper/design_configs.dart'; +import '../../../../data/model/category.dart'; +import '../../../../utils/constant.dart'; +import '../../../../utils/ui_utils.dart'; + +class CategoryCard extends StatelessWidget { + final bool? frontSpacing; + final Function(Category category) onTapCategory; + final Category category; + const CategoryCard( + {super.key, + required this.frontSpacing, + required this.onTapCategory, + required this.category}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsetsDirectional.only( + start: frontSpacing == true ? 5.0 : 0, + end: .0, + ), + child: GestureDetector( + onTap: () { + onTapCategory.call(category); + }, + child: Row( + children: [ + Container( + constraints: BoxConstraints( + minWidth: 100.rw(context), + ), + height: 44.rh(context), + alignment: Alignment.center, + decoration: DesignConfig.boxDecorationBorder( + color: context.color.secondaryColor, + radius: 10, + borderWidth: 1.5, + borderColor: context.color.borderColor, + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + UiUtils.imageType(category.image!, + width: 20, + height: 20, + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null), + SizedBox(width: 12.rw(context)), + SizedBox( + child: Text(category.category!, + textAlign: TextAlign.center, + maxLines: 2, + overflow: TextOverflow.ellipsis) + .size(context.font.small), + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Ui/screens/home/Widgets/city_card.dart b/lib/Ui/screens/home/Widgets/city_card.dart new file mode 100644 index 0000000..770e697 --- /dev/null +++ b/lib/Ui/screens/home/Widgets/city_card.dart @@ -0,0 +1,15 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +class CityCard extends StatelessWidget { + const CityCard({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: Color.fromRGBO(Random().nextInt(255), Random().nextInt(255), + Random().nextInt(255), 1), + ); + } +} diff --git a/lib/Ui/screens/home/Widgets/city_heading_card.dart b/lib/Ui/screens/home/Widgets/city_heading_card.dart new file mode 100644 index 0000000..e272c15 --- /dev/null +++ b/lib/Ui/screens/home/Widgets/city_heading_card.dart @@ -0,0 +1,62 @@ +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; + +class CityHeadingCard extends StatelessWidget { + const CityHeadingCard({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + height: 211, + width: MediaQuery.of(context).size.width, + decoration: const BoxDecoration(), + child: Stack( + fit: StackFit.expand, + children: [ + Image.asset("assets/city.jpg", fit: BoxFit.cover), + Directionality( + textDirection: Directionality.of(context), + child: Container( + decoration: BoxDecoration( + gradient: RadialGradient( + center: Alignment.centerLeft, + radius: 3, + focalRadius: 1, + colors: [ + Colors.black.withOpacity(0.97), + Colors.black.withOpacity(0), + ], + ), + ), + ), + ), + PositionedDirectional( + top: 50, + start: 11, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 5, + height: 34, + color: Colors.white, + ), + const SizedBox( + width: 5, + ), + const Text("Popular cities").color(Colors.white).size(32), + ], + ), + Text("${context.watch().getCount() ?? 0}+ Properties") + .color(Colors.white) + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/Ui/screens/home/Widgets/header_card.dart b/lib/Ui/screens/home/Widgets/header_card.dart new file mode 100644 index 0000000..8fd06a1 --- /dev/null +++ b/lib/Ui/screens/home/Widgets/header_card.dart @@ -0,0 +1,43 @@ +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; + +import '../../../../utils/ui_utils.dart'; +import '../home_screen.dart'; + +class TitleHeader extends StatelessWidget { + final String title; + final VoidCallback? onSeeAll; + bool? enableShowAll; + TitleHeader( + {super.key, required this.title, this.onSeeAll, this.enableShowAll}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsetsDirectional.only( + top: 20.0, bottom: 16, start: sidePadding, end: sidePadding), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text(title) + .bold(weight: FontWeight.w700) + .color(context.color.textColorDark) + .size(context.font.large) + .setMaxLines(lines: 1), + ), + if (enableShowAll ?? true) + GestureDetector( + onTap: () { + onSeeAll?.call(); + }, + child: Text(UiUtils.translate(context, "seeAll")) + .size(context.font.small) + .color(context.color.textLightColor) + .bold(weight: FontWeight.w700), + ) + ], + ), + ); + } +} diff --git a/lib/Ui/screens/home/Widgets/homeListener.dart b/lib/Ui/screens/home/Widgets/homeListener.dart new file mode 100644 index 0000000..0fef559 --- /dev/null +++ b/lib/Ui/screens/home/Widgets/homeListener.dart @@ -0,0 +1,198 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../../data/cubits/category/fetch_category_cubit.dart'; +import '../../../../data/cubits/property/fetch_most_liked_properties.dart'; +import '../../../../data/cubits/property/fetch_most_viewed_properties_cubit.dart'; +import '../../../../data/cubits/property/fetch_nearby_property_cubit.dart'; +import '../../../../data/cubits/property/fetch_promoted_properties_cubit.dart'; +import '../../../../data/cubits/slider_cubit.dart'; + +class HomePageStateListener { + Connectivity connectivity = Connectivity(); + bool isNetworkAvailable = true; + bool isPromotedPropertyEmpty = false; + bool isCategoryEmpty = false; + bool isSliderEmpty = false; + bool isMostViewdPropertyEmpty = false; + bool isNearbyPropertiesEmpty = false; + bool isMostLikedPropertiesEmpty = false; + void init(setState, {required VoidCallback onNetAvailable}) { + connectivity.onConnectivityChanged.listen((event) { + if (event == ConnectivityResult.none) { + isNetworkAvailable = false; + setState(() {}); + } else { + onNetAvailable.call(); + isNetworkAvailable = true; + setState(() {}); + } + }); + } + + void setNetworkState(setState, isAvailable) { + isNetworkAvailable = isAvailable; + setState(() {}); + } + + HomeScreenDataBinding listen(BuildContext context) { + bool hasPromotedPropertyError = false; + bool hasMostViewdPropertyError = false; + bool hasCategoryError = false; + + bool hasNearbyPropertyError = false; + bool hasMostLikedPropertyError = false; + + bool categorySuccess = false, + promotedSuccess = false, + mostVSuccess = false, + sliderSuccess = false; + var fetchPromotedPropertiesWatch = + context.watch().state; + var fetchMostViewedPropertiesWatch = + context.watch().state; + var mostLikedPropertiesWatch = + context.watch().state; + var nearbyPropertiesWatch = + context.watch().state; + + ///Watching if data is available or not + if ((fetchPromotedPropertiesWatch is FetchPromotedPropertiesSuccess)) { + promotedSuccess = true; + isPromotedPropertyEmpty = + (fetchPromotedPropertiesWatch).properties.isEmpty; + } + if ((fetchMostViewedPropertiesWatch is FetchMostViewedPropertiesSuccess)) { + mostVSuccess = true; + isMostViewdPropertyEmpty = + (fetchMostViewedPropertiesWatch).properties.isEmpty; + } + + if ((mostLikedPropertiesWatch is FetchMostLikedPropertiesSuccess)) { + isMostLikedPropertiesEmpty = + (mostLikedPropertiesWatch).properties.isEmpty; + } + + if ((nearbyPropertiesWatch is FetchNearbyPropertiesSuccess)) { + isNearbyPropertiesEmpty = (nearbyPropertiesWatch).properties.isEmpty; + } + + ///End: Listning to data availability + + ///Listning to realtime state change + if ((context.watch().state is FetchCategorySuccess)) { + categorySuccess = true; + } + if ((context.watch().state is SliderFetchSuccess)) { + sliderSuccess = true; + } + + ///End: Listning to realtime state change + + ///Listning to Error + if ((fetchPromotedPropertiesWatch is FetchPromotedPropertiesFailure)) { + hasPromotedPropertyError = true; + } + if ((fetchMostViewedPropertiesWatch is FetchMostViewedPropertiesFailure)) { + hasMostViewdPropertyError = true; + } + if ((context.watch().state is FetchCategoryFailure)) { + hasCategoryError = true; + } + + if (nearbyPropertiesWatch is FetchNearbyPropertiesFailure) { + hasNearbyPropertyError = true; + } + if ((mostLikedPropertiesWatch is FetchMostLikedPropertiesFailure)) { + hasMostLikedPropertyError = true; + } + + var dataAvailability = DataAvailibility( + isPromotedPropertyEmpty: isPromotedPropertyEmpty, + isCategoryEmpty: isCategoryEmpty, + isSliderEmpty: isSliderEmpty, + isMostViewdPropertyEmpty: isMostViewdPropertyEmpty, + isNearbyPropertiesEmpty: isNearbyPropertiesEmpty, + isMostLikedPropertiesEmpty: isMostLikedPropertiesEmpty, + ); + + if ((hasCategoryError || + hasMostViewdPropertyError || + hasPromotedPropertyError || + hasMostLikedPropertyError || + hasNearbyPropertyError) && + isNetworkAvailable) { + var x = { + "hasCategoryError": hasCategoryError, + "hasMostViewdPropertyError": hasMostViewdPropertyError, + "hasPromotedPropertyError": hasPromotedPropertyError, + "hasMostLikedPropertyError": hasMostLikedPropertyError, + "hasNearbyPropertyError": hasNearbyPropertyError + }..mlog("HomeScreenState"); + + return HomeScreenDataBinding( + state: HomeScreenDataState.fail, dataAvailability: dataAvailability); + } else if (sliderSuccess && + categorySuccess && + promotedSuccess && + mostVSuccess) { + return HomeScreenDataBinding( + state: HomeScreenDataState.success, + dataAvailability: dataAvailability); + } else if (isCategoryEmpty == true && + isMostViewdPropertyEmpty == true && + isSliderEmpty == true && + isPromotedPropertyEmpty == true) { + return HomeScreenDataBinding( + state: HomeScreenDataState.nodata, + dataAvailability: dataAvailability); + } else if (isNetworkAvailable == false) { + return HomeScreenDataBinding( + state: HomeScreenDataState.nointernet, + dataAvailability: dataAvailability); + } else { + return HomeScreenDataBinding( + state: HomeScreenDataState.normal, + dataAvailability: dataAvailability); + } + } +} + +enum HomeScreenDataState { normal, success, nodata, nointernet, fail } + +class DataAvailibility { + final bool isPromotedPropertyEmpty; + final bool isCategoryEmpty; + final bool isSliderEmpty; + final bool isMostViewdPropertyEmpty; + final bool isNearbyPropertiesEmpty; + final bool isMostLikedPropertiesEmpty; + DataAvailibility({ + required this.isPromotedPropertyEmpty, + required this.isCategoryEmpty, + required this.isSliderEmpty, + required this.isMostViewdPropertyEmpty, + required this.isNearbyPropertiesEmpty, + required this.isMostLikedPropertiesEmpty, + }); + + @override + String toString() { + return 'DataAvailibility(isPromotedPropertyEmpty: $isPromotedPropertyEmpty, isCategoryEmpty: $isCategoryEmpty, isSliderEmpty: $isSliderEmpty, isMostViewdPropertyEmpty: $isMostViewdPropertyEmpty, isNearbyPropertiesEmpty: $isNearbyPropertiesEmpty, isMostLikedPropertiesEmpty: $isMostLikedPropertiesEmpty)'; + } +} + +class HomeScreenDataBinding { + final HomeScreenDataState state; + final DataAvailibility dataAvailability; + HomeScreenDataBinding({ + required this.state, + required this.dataAvailability, + }); + + @override + String toString() => + 'HomeScreenDataBinding(state: $state, dataAvailability: $dataAvailability)'; +} diff --git a/lib/Ui/screens/home/Widgets/home_profile_image_card.dart b/lib/Ui/screens/home/Widgets/home_profile_image_card.dart new file mode 100644 index 0000000..2d1b270 --- /dev/null +++ b/lib/Ui/screens/home/Widgets/home_profile_image_card.dart @@ -0,0 +1,102 @@ +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../../data/cubits/system/user_details.dart'; +import '../../../../utils/AppIcon.dart'; +import '../../../../utils/ui_utils.dart'; + +class CircularProfileImageWidget extends StatelessWidget { + const CircularProfileImageWidget({super.key}); + Widget buildDefaultPersonSVG(BuildContext context) { + return Container( + width: 90, + height: 90, + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity(0.1), + shape: BoxShape.circle), + child: Center( + child: UiUtils.getSvg( + AppIcons.defaultPersonLogo, + color: context.color.tertiaryColor, + width: 40, + height: 40, + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + UiUtils.showFullScreenImage( + context, + provider: NetworkImage( + context.read().state.user?.profile ?? "", + ), + ); + }, + child: Container( + decoration: const BoxDecoration(shape: BoxShape.circle), + margin: const EdgeInsetsDirectional.only(end: 10), + padding: const EdgeInsets.only(bottom: 5), + child: FittedBox( + fit: BoxFit.cover, + child: GestureDetector( + onTap: () { + if (context.read().state.user?.profile != + null) { + UiUtils.showFullScreenImage(context, + provider: NetworkImage( + context.read().state.user?.profile ?? + "")); + } + + // MainActivityState.pageCntrlr.jumpToPage(4); + }, + child: (context.watch().state.user?.profile ?? "") + .trim() + .isEmpty + ? FittedBox( + fit: BoxFit.none, + child: buildDefaultPersonSVG( + context, + ), + ) + : Container( + decoration: const BoxDecoration( + shape: BoxShape.circle, + ), + width: 50, + height: 50, + clipBehavior: Clip.antiAlias, + child: Image.network( + context.watch().state.user?.profile ?? + "", + fit: BoxFit.cover, + width: 50, + height: 50, + errorBuilder: (BuildContext context, Object exception, + StackTrace? stackTrace) { + return FittedBox( + fit: BoxFit.none, + child: buildDefaultPersonSVG(context), + ); + }, + loadingBuilder: (BuildContext context, Widget? child, + ImageChunkEvent? loadingProgress) { + if (loadingProgress == null) return child!; + return FittedBox( + fit: BoxFit.none, + child: buildDefaultPersonSVG(context), + ); + }, + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/home/Widgets/home_search.dart b/lib/Ui/screens/home/Widgets/home_search.dart new file mode 100644 index 0000000..794f971 --- /dev/null +++ b/lib/Ui/screens/home/Widgets/home_search.dart @@ -0,0 +1,93 @@ +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:flutter/material.dart'; + +import '../../../../app/routes.dart'; +import '../../../../utils/AppIcon.dart'; +import '../../../../utils/ui_utils.dart'; +import '../home_screen.dart'; + +class HomeSearchField extends StatelessWidget { + const HomeSearchField({super.key}); + + @override + Widget build(BuildContext context) { + Widget buildSearchIcon() { + return Padding( + padding: const EdgeInsets.all(8.0), + child: UiUtils.getSvg(AppIcons.search, + color: context.color.tertiaryColor)); + } + + return Padding( + padding: + const EdgeInsets.symmetric(horizontal: sidePadding, vertical: 15), + child: Row( + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.pushNamed(context, Routes.searchScreenRoute, + arguments: {"autoFocus": true, "openFilterScreen": false}); + }, + child: AbsorbPointer( + absorbing: true, + child: Container( + width: 285.rw( + context, + ), + height: 50.rh( + context, + ), + alignment: Alignment.center, + decoration: BoxDecoration( + border: Border.all( + width: 1.5, color: context.color.borderColor), + borderRadius: const BorderRadius.all(Radius.circular(10)), + color: context.color.secondaryColor), + child: TextFormField( + readOnly: true, + decoration: InputDecoration( + border: InputBorder.none, //OutlineInputBorder() + fillColor: Theme.of(context).colorScheme.secondaryColor, + hintText: UiUtils.translate(context, "searchHintLbl"), + prefixIcon: buildSearchIcon(), + prefixIconConstraints: + const BoxConstraints(minHeight: 5, minWidth: 5), + ), + enableSuggestions: true, + onEditingComplete: () { + FocusScope.of(context).unfocus(); + }, + onTap: () { + //change prefix icon color to primary + })), + ), + ), + const Spacer(), + GestureDetector( + onTap: () { + Navigator.pushNamed(context, Routes.propertyMapScreen); + }, + child: Container( + width: 50.rw(context), + height: 50.rh(context), + decoration: BoxDecoration( + border: + Border.all(width: 1.5, color: context.color.borderColor), + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(10), + ), + child: Center( + child: UiUtils.getSvg( + AppIcons.propertyMap, + color: context.color.tertiaryColor, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/Ui/screens/home/Widgets/home_shimmers.dart b/lib/Ui/screens/home/Widgets/home_shimmers.dart new file mode 100644 index 0000000..de8ba2d --- /dev/null +++ b/lib/Ui/screens/home/Widgets/home_shimmers.dart @@ -0,0 +1,163 @@ +import 'package:ebroker/Ui/screens/home/home_screen.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:flutter/material.dart'; + +import '../../widgets/shimmerLoadingContainer.dart'; + +class SliderShimmer extends StatelessWidget { + const SliderShimmer({super.key}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: sidePadding, + ), + child: Column( + children: [ + const SizedBox( + height: 10, + ), + CustomShimmer( + height: 130.rh(context), + width: context.screenWidth, + ), + const SizedBox( + height: 10, + ), + ], + ), + ); + } +} + +class PromotedPropertiesShimmer extends StatelessWidget { + const PromotedPropertiesShimmer({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 261, + child: ListView.builder( + itemCount: 5, + shrinkWrap: true, + padding: const EdgeInsets.symmetric( + horizontal: sidePadding, + ), + scrollDirection: Axis.horizontal, + physics: const BouncingScrollPhysics(), + itemBuilder: (context, index) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: index == 0 ? 0 : 8), + child: CustomShimmer( + height: 272.rh(context), + width: 250.rw(context), + ), + ); + })); + } +} + +class MostLikedPropertiesShimmer extends StatelessWidget { + const MostLikedPropertiesShimmer({super.key}); + + @override + Widget build(BuildContext context) { + return GridView.builder( + shrinkWrap: true, + padding: const EdgeInsets.symmetric( + horizontal: sidePadding, + ), + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + childAspectRatio: 162.rw(context) / 274.rh(context), + mainAxisSpacing: 8, + crossAxisSpacing: 8, + crossAxisCount: 2), + itemCount: 5, + itemBuilder: (context, index) { + return const Padding( + padding: EdgeInsets.symmetric(horizontal: 0), + child: CustomShimmer(), + ); + }, + ); + } +} + +class NearbyPropertiesShimmer extends StatelessWidget { + const NearbyPropertiesShimmer({super.key}); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 200, + child: ListView.builder( + itemCount: 5, + shrinkWrap: true, + padding: const EdgeInsets.symmetric( + horizontal: sidePadding, + ), + scrollDirection: Axis.horizontal, + physics: const BouncingScrollPhysics(), + itemBuilder: (context, index) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: index == 0 ? 0 : 8), + child: const CustomShimmer( + height: 200, + width: 300, + ), + ); + })); + } +} + +class MostViewdPropertiesShimmer extends StatelessWidget { + const MostViewdPropertiesShimmer({super.key}); + + @override + Widget build(BuildContext context) { + return GridView.builder( + shrinkWrap: true, + padding: const EdgeInsets.symmetric( + horizontal: sidePadding, + ), + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + childAspectRatio: 162.rw(context) / 274.rh(context), + mainAxisSpacing: 15, + crossAxisCount: 2), + itemCount: 5, + itemBuilder: (context, index) { + return const Padding( + padding: EdgeInsets.all(8.0), + child: CustomShimmer(), + ); + }); + } +} + +class CategoryShimmer extends StatelessWidget { + const CategoryShimmer({super.key}); + + @override + Widget build(BuildContext context) { + return ListView.builder( + shrinkWrap: true, + scrollDirection: Axis.horizontal, + itemCount: 4, + padding: const EdgeInsets.symmetric( + horizontal: sidePadding, + ), + itemBuilder: (context, index) { + return CustomShimmer( + width: 100.rw(context), + height: 44.rh(context), + margin: const EdgeInsetsDirectional.only(end: 10, bottom: 5), + ); + }); + } +} diff --git a/lib/Ui/screens/home/Widgets/location_widget.dart b/lib/Ui/screens/home/Widgets/location_widget.dart new file mode 100644 index 0000000..0edc6c3 --- /dev/null +++ b/lib/Ui/screens/home/Widgets/location_widget.dart @@ -0,0 +1,153 @@ +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/hive_keys.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hive_flutter/hive_flutter.dart'; + +import '../../../../data/cubits/auth/auth_cubit.dart'; +import '../../../../data/cubits/property/fetch_nearby_property_cubit.dart'; +import '../../../../data/model/google_place_model.dart'; +import '../../../../utils/AppIcon.dart'; +import '../../../../utils/hive_utils.dart'; +import '../../../../utils/ui_utils.dart'; +import '../../widgets/BottomSheets/choose_location_bottomsheet.dart'; + +class LocationWidget extends StatefulWidget { + const LocationWidget({super.key}); + + @override + State createState() => _LocationWidgetState(); +} + +class _LocationWidgetState extends State { + String city = "", state = "", country = ""; + @override + void initState() { + Hive.box(HiveKeys.userDetailsBox) + .listenable(keys: ["city", "state", "country"]).addListener(() { + city = HiveUtils.getCityName().toString().trim(); + state = HiveUtils.getStateName().toString().trim(); + country = HiveUtils.getCountryName().toString().trim(); + setState(() {}); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + city = HiveUtils.getCityName().toString().trim(); + state = HiveUtils.getStateName().toString().trim(); + country = HiveUtils.getCountryName().toString().trim(); + + List locationList = [city, state, country]; + locationList.removeWhere((element) => element.isEmpty); + + String joinedLocation = locationList.join(','); + return FittedBox( + fit: BoxFit.none, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 16.rw(context), + ), + GestureDetector( + onTap: () async { + var result = await showModalBottomSheet( + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20))), + context: context, + builder: (context) { + return const ChooseLocatonBottomSheet(); + }, + ); + if (result != null) { + GooglePlaceModel place = (result as GooglePlaceModel); + HiveUtils.setLocation( + city: place.city, + state: place.state, + latitude: double.parse(place.latitude), + longitude: double.parse(place.longitude), + country: place.country, + placeId: place.placeId); + context.read().updateUserData(context, + city: place.city, + state: place.state, + country: place.country, + latitude: double.parse(place.latitude), + longitude: double.parse(place.longitude)); + Future.delayed( + Duration.zero, + () { + // context + // .read() + // .fetch(); + // context + // .read() + // .fetch(); + // context.read().fetchSlider(context); + context + .read() + .fetch(forceRefresh: true); + }, + ); + + // city = place.city; + // country = place.country; + // _state = place.state; + } + + // const ChooseLocatonBottomSheet(); + }, + child: Container( + width: 40.rw(context), + height: 40.rh(context), + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(10)), + child: UiUtils.getSvg( + AppIcons.location, + fit: BoxFit.none, + color: context.color.tertiaryColor, + ), + ), + ), + SizedBox( + width: 10.rw(context), + ), + ValueListenableBuilder( + valueListenable: Hive.box(HiveKeys.userDetailsBox).listenable(), + builder: (context, value, child) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(UiUtils.translate(context, "locationLbl")) + .color(context.color.textColorDark) + .size( + context.font.small, + ), + SizedBox( + width: 150, + child: Text( + joinedLocation, + maxLines: 1, + softWrap: true, + overflow: TextOverflow.ellipsis, + ) + .color(context.color.textColorDark) + .size(context.font.small) + .bold(weight: FontWeight.w600), + ), + ], + ); + }), + ], + ), + ); + } +} diff --git a/lib/Ui/screens/home/Widgets/project_card_horizontal.dart b/lib/Ui/screens/home/Widgets/project_card_horizontal.dart new file mode 100644 index 0000000..309e0bc --- /dev/null +++ b/lib/Ui/screens/home/Widgets/project_card_horizontal.dart @@ -0,0 +1,184 @@ +import 'package:ebroker/data/model/project_model.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:flutter/material.dart'; + +import '../../../../exports/main_export.dart'; +import '../../../../utils/ui_utils.dart'; + +class ProjectHorizontalCard extends StatelessWidget { + final ProjectModel project; + final List? addBottom; + final double? additionalHeight; + final bool? useRow; + final double? additionalImageWidth; + const ProjectHorizontalCard( + {super.key, + required this.project, + this.useRow, + this.addBottom, + this.additionalHeight, + this.additionalImageWidth}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4.5), + child: GestureDetector( + onTap: () { + Navigator.pushNamed(context, Routes.projectDetailsScreen, + arguments: {"project": project}); + }, + child: Container( + height: addBottom == null ? 115 : (115 + (additionalHeight ?? 0)), + decoration: BoxDecoration( + border: Border.all(width: 1.5, color: context.color.borderColor), + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(18)), + child: Stack( + fit: StackFit.expand, + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: Row( + children: [ + Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(15), + child: Stack( + children: [ + UiUtils.getImage( + project.image ?? "", + height: 111, + width: 100 + (additionalImageWidth ?? 0), + fit: BoxFit.cover, + ), + // Text(property.promoted.toString()), + ], + ), + ), + ], + ), + Expanded( + child: Padding( + padding: const EdgeInsets.only( + top: 5, + left: 12, + bottom: 5, + right: 12, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + children: [ + UiUtils.imageType( + project.category!.image ?? "", + width: 18, + height: 18, + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null), + const SizedBox( + width: 5, + ), + Expanded( + flex: 3, + child: const Text('project.category!.category!') + .setMaxLines(lines: 1) + .size( + context.font.small.rf(context), + ) + .bold( + weight: FontWeight.w400, + ) + .color( + context.color.textLightColor, + ), + ), + Spacer(), + Container( + height: 19, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: context.color.buttonColor + .withOpacity(0.5), + borderRadius: + BorderRadius.circular(4)), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0), + child: Center( + child: Text( + project.type!.translate(context), + ) + .color( + context.color.textColorDark, + ) + .bold() + .size(context.font.smaller), + ), + ), + ) + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + project.title!.firstUpperCase(), + ) + .setMaxLines(lines: 1) + .size(context.font.large) + .color(context.color.textColorDark), + Text( + project.description!.firstUpperCase(), + ) + .setMaxLines(lines: 1) + .size(context.font.small) + .color(context.color.textColorDark + .withOpacity(0.80)), + ], + ), + if (project.city != "") + Row( + children: [ + Icon( + Icons.location_on, + size: 16, + color: context.color.textLightColor, + ), + Expanded( + child: Text(project.city?.trim() ?? "") + .setMaxLines(lines: 1) + .color( + context.color.textLightColor), + ) + ], + ) + ], + ), + ), + ), + ], + ), + ), + + if (useRow == false || useRow == null) ...addBottom ?? [], + + if (useRow == true) ...{Row(children: addBottom ?? [])} + + // ...addBottom ?? [] + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/home/Widgets/property_card_big.dart b/lib/Ui/screens/home/Widgets/property_card_big.dart new file mode 100644 index 0000000..b1a8f6f --- /dev/null +++ b/lib/Ui/screens/home/Widgets/property_card_big.dart @@ -0,0 +1,275 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +import '../../../../data/cubits/favorite/add_to_favorite_cubit.dart'; +import '../../../../data/model/property_model.dart'; +import '../../../../utils/AppIcon.dart'; +import '../../../../utils/Extensions/extensions.dart'; +import '../../../../utils/constant.dart'; +import '../../../../utils/helper_utils.dart'; +import '../../../../utils/string_extenstion.dart'; +import '../../../../utils/ui_utils.dart'; +import '../../proprties/property_details.dart'; +import '../../widgets/like_button_widget.dart'; +import '../../widgets/promoted_widget.dart'; + +class PropertyCardBig extends StatelessWidget { + final PropertyModel property; + final bool? isFirst; + final bool? showEndPadding; + final Function(FavoriteType type)? onLikeChange; + const PropertyCardBig( + {super.key, + this.onLikeChange, + required this.property, + this.isFirst, + this.showEndPadding}); + + @override + Widget build(BuildContext context) { + String rentPrice = (property.price! + .priceFormate( + disabled: false, + isCurrency: true + ) + .toString() + .formatAmount(prefix: true)); + if (property.rentduration != "" && property.rentduration != null) { + rentPrice = + ("$rentPrice / ") + (rentDurationMap[property.rentduration] ?? ""); + } + + return Padding( + padding: EdgeInsetsDirectional.only( + start: (isFirst ?? false) ? 0 : 5.0, + end: (showEndPadding ?? true) ? 5.0 : 0, + ), + child: GestureDetector( + onLongPress: () { + HelperUtils.share(context, property.id!, property?.slugId ?? ""); + }, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + color: context.color.secondaryColor, + border: Border.all( + width: 1.5, + color: context.color.borderColor, + ), + ), + height: 272, + width: 250, + child: Stack( + children: [ + Column( + children: [ + SizedBox( + height: 147, + child: Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(15), + child: UiUtils.getImage( + property.titleImage!, + height: 147, + width: double.infinity, + fit: BoxFit.cover, + blurHash: property.titleimagehash, + ), + ), + PositionedDirectional( + start: 10, + bottom: 10, + child: Container( + height: 24, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: context.color.secondaryColor.withOpacity( + 0.7, + ), + borderRadius: BorderRadius.circular( + 4, + ), + ), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 2, sigmaY: 3), + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8.0), + child: Center( + child: Text( + property.properyType == 'Rent' + ? 'Sewa' + : 'Jual', + ) + .color( + context.color.textColorDark, + ) + .bold() + .size(context.font.smaller), + ), + ), + ), + ), + ) + ], + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.only( + top: 5, bottom: 5, left: 12, right: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + UiUtils.imageType(property.category!.image!, + width: 18, + height: 18, + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null), + const SizedBox( + width: 5, + ), + Text(property.category?.category ?? "") + .size( + context.font.small, + ) + .bold( + weight: FontWeight.w400, + ) + .color( + context.color.textLightColor, + ) + ], + ), + const SizedBox( + height: 7, + ), + if (property.properyType.toString().toLowerCase() == + "rent") ...[ + Text(rentPrice) + .size( + context.font.large, + ) + .color( + context.color.tertiaryColor, + ) + .bold( + weight: FontWeight.w700, + ), + ] else ...[ + Text(property.price! + .priceFormate( + disabled: false, + isCurrency: true + ) + .toString() + .formatAmount(prefix: true)) + .size(context.font.large) + .color(context.color.tertiaryColor) + .bold( + weight: FontWeight.w700, + ), + ], + const SizedBox( + height: 5, + ), + Text( + property.title ?? "", + ) + .setMaxLines(lines: 1) + .size(context.font.large) + .color(context.color.textColorDark), + if (property.city != "") ...[ + const Spacer(), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + UiUtils.getSvg(AppIcons.location, + color: context.color.textLightColor), + const SizedBox( + width: 5, + ), + Expanded( + child: Text(property.city!) + .color(context.color.textLightColor) + .setMaxLines(lines: 1), + ) + ], + ) + ] + ], + ), + ), + ) + ], + ), + PositionedDirectional( + end: 25, + top: 128, + child: Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: context.color.secondaryColor, + shape: BoxShape.circle, + boxShadow: const [ + BoxShadow( + color: Color.fromARGB(33, 0, 0, 0), + offset: Offset(0, 2), + blurRadius: 15, + spreadRadius: 0) + ], + ), + child: LikeButtonWidget( + property: property, + onLikeChanged: (type) { + onLikeChange?.call(type); + }, + ), + ), + ), + PositionedDirectional( + start: 10, + top: 10, + child: Row( + children: [ + Visibility( + visible: property.promoted ?? false, + child: const PromotedCard(type: PromoteCardType.text)), + // const SizedBox( + // width: 2, + // ), + // Container( + // height: 24, + // decoration: BoxDecoration( + // color: context.color.secondaryColor.withOpacity(0.9), + // borderRadius: BorderRadius.circular(4)), + // child: Padding( + // padding: const EdgeInsets.symmetric(horizontal: 8.0), + // child: Center( + // child: Text( + // UiUtils.getTranslatedLabel(context, "sell"), + // ) + // .color( + // context.color.textColorDark, + // ) + // .bold() + // .size(context.font.smaller), + // ), + // ), + // ) + ], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/home/Widgets/property_gradient_card.dart b/lib/Ui/screens/home/Widgets/property_gradient_card.dart new file mode 100644 index 0000000..531a8b8 --- /dev/null +++ b/lib/Ui/screens/home/Widgets/property_gradient_card.dart @@ -0,0 +1,275 @@ +import 'package:ebroker/Ui/screens/widgets/promoted_widget.dart'; +import 'package:ebroker/app/routes.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/string_extenstion.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +import '../../../../data/model/property_model.dart'; +import '../../../../utils/AppIcon.dart'; +import '../../../../utils/constant.dart'; +import '../../../../utils/ui_utils.dart'; + +class PropertyGradiendCard extends StatefulWidget { + final PropertyModel model; + final bool? isFirst; + final bool? showEndPadding; + const PropertyGradiendCard( + {super.key, required this.model, this.isFirst, this.showEndPadding}); + + @override + State createState() => _PropertyGradiendCardState(); +} + +class _PropertyGradiendCardState extends State { + List paramterList(PropertyModel propertie) { + List? parameters = propertie.parameters; + + List? icons = parameters?.map((e) { + return Padding( + padding: const EdgeInsets.all(2.0), + child: SizedBox( + width: 15, + height: 15, + child: SvgPicture.network( + e.image!, + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null, + ), + ), + ); + }).toList(); + + Iterable? filterd = icons?.take(4); + + return filterd?.toList() ?? []; + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + Navigator.pushNamed(context, Routes.propertyDetails, arguments: { + 'propertyData': widget.model, + 'propertiesList': [], + 'fromMyProperty': false, + }); + }, + child: Padding( + padding: EdgeInsetsDirectional.only( + start: (widget.isFirst ?? false) ? 0 : 5.0, + end: (widget.showEndPadding ?? true) ? 5.0 : 0, + ), + child: Container( + height: 200, + width: 300, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + ), + child: LayoutBuilder(builder: (context, c) { + PropertyModel propertie = widget.model; + return Stack( + children: [ + UiUtils.getImage( + propertie.titleImage ?? "", + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ), + Container( + width: c.maxWidth, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Colors.black.withOpacity(0.72), + Colors.black.withOpacity(0.3), + Colors.transparent, + ], + stops: const [ + 0.2, + 0.4, + 0.7 + ]), + ), + ), + + Padding( + padding: const EdgeInsets.all(10.0), + child: SizedBox( + height: c.maxHeight, + width: c.maxWidth, + child: Stack( + children: [ + PositionedDirectional( + top: 0, + start: 0, + child: Row( + children: [ + Container( + height: 19, + decoration: BoxDecoration( + color: secondaryColorDark.withOpacity(0.9), + borderRadius: BorderRadius.circular(4)), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0), + child: Center( + child: Text( + propertie.properyType!.translate(context), + ) + .color( + context.color.buttonColor, + ) + .bold() + .size(context.font.smaller), + ), + ), + ), + const SizedBox( + width: 2, + ), + if (propertie.promoted ?? false) + Container( + height: 19, + decoration: BoxDecoration( + color: context.color.tertiaryColor, + borderRadius: BorderRadius.circular(4)), + child: const Padding( + padding: + EdgeInsets.symmetric(horizontal: 8.0), + child: Center( + child: PromotedCard( + color: Colors.transparent, + type: PromoteCardType.icon), + ), + ), + ) + ], + ), + ), + PositionedDirectional( + bottom: 0, + start: 0, + child: SizedBox( + height: c.maxHeight * 0.35, + width: c.maxWidth - 20, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Spacer(), + Row( + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + UiUtils.imageType( + propertie.category?.image ?? "", + color: + Constant.adaptThemeColorSvg + ? context + .color.tertiaryColor + : null, + width: 20, + height: 20), + const SizedBox( + width: 3, + ), + Expanded( + child: Text((propertie.category! + .category) ?? + "") + .setMaxLines(lines: 1) + .color(context + .color.buttonColor), + ), + ], + ), + const Spacer(), + Text(((propertie.title) ?? "")) + .setMaxLines(lines: 1) + .size(context.font.large) + .color(context.color.buttonColor), + const Spacer(), + Row( + children: [ + SvgPicture.asset( + AppIcons.location, + color: context.color.buttonColor + .withOpacity(0.8), + width: 12, + height: 12, + ), + Expanded( + child: SizedBox( + child: Text(propertie.address! + .toString()) + .setMaxLines(lines: 1) + .size(context.font.small) + .color(context + .color.buttonColor), + ), + ), + ], + ) + ], + ), + ), + Expanded( + flex: 2, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.end, + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + const Spacer(), + FittedBox( + fit: BoxFit.fitWidth, + child: Text(propertie.price! + .priceFormate( + disabled: Constant + .isNumberWithSuffix == + false) + .toString() + .formatAmount( + prefix: true, + )) + .bold() + .setMaxLines(lines: 1) + .size(context.font.extraLarge) + .color(context.color.buttonColor), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: paramterList(propertie), + ) + ], + ), + ) + ], + ), + )) + ], + ), + ), + ), + + // const PositionedDirectional( + // child: PromotedCard(type: PromoteCardType.icon)) + ], + ); + }), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/home/Widgets/property_horizontal_card.dart b/lib/Ui/screens/home/Widgets/property_horizontal_card.dart new file mode 100644 index 0000000..6767dc2 --- /dev/null +++ b/lib/Ui/screens/home/Widgets/property_horizontal_card.dart @@ -0,0 +1,363 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:ui'; + +import 'package:ebroker/Ui/screens/widgets/like_button_widget.dart'; +import 'package:ebroker/Ui/screens/widgets/promoted_widget.dart'; +import 'package:ebroker/data/cubits/favorite/add_to_favorite_cubit.dart'; +import 'package:ebroker/utils/AppIcon.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:ebroker/utils/string_extenstion.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; + +import '../../../../data/model/property_model.dart'; +import '../../../../utils/constant.dart'; +import '../../../../utils/helper_utils.dart'; +import '../../../../utils/ui_utils.dart'; +import '../../proprties/property_details.dart'; + +class PropertyHorizontalCard extends StatelessWidget { + final PropertyModel property; + final List? addBottom; + final double? additionalHeight; + final StatusButton? statusButton; + final Function(FavoriteType type)? onLikeChange; + final bool? useRow; + final bool? showDeleteButton; + final VoidCallback? onDeleteTap; + final double? additionalImageWidth; + final bool? showLikeButton; + const PropertyHorizontalCard( + {super.key, + required this.property, + this.useRow, + this.addBottom, + this.additionalHeight, + this.onLikeChange, + this.statusButton, + this.showDeleteButton, + this.onDeleteTap, + this.showLikeButton, + this.additionalImageWidth}); + + @override + Widget build(BuildContext context) { + String rentPrice = (property.price! + .priceFormate( + disabled: false + ) + .toString() + .formatAmount(prefix: true)); + + if (property.rentduration != "" && property.rentduration != null) { + rentPrice = + ("$rentPrice / ") + (rentDurationMap[property.rentduration] ?? ""); + } + + return BlocProvider( + create: (context) => AddToFavoriteCubitCubit(), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 4.5), + child: GestureDetector( + onLongPress: () { + HelperUtils.share(context, property.id!, property?.slugId ?? ""); + }, + child: Container( + height: addBottom == null ? 124 : (124 + (additionalHeight ?? 0)), + decoration: BoxDecoration( + border: + Border.all(width: 1.5, color: context.color.borderColor), + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(18)), + child: Stack( + fit: StackFit.expand, + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: Row( + children: [ + Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(15), + child: Stack( + children: [ + UiUtils.getImage( + property.titleImage ?? "", + height: statusButton != null ? 90 : 120, + width: 100 + (additionalImageWidth ?? 0), + fit: BoxFit.cover, + ), + // Text(property.promoted.toString()), + if (property.promoted ?? false) + const PositionedDirectional( + start: 5, + top: 5, + child: PromotedCard( + type: PromoteCardType.icon)), + // if (false) + // Container( + // width: 24, + // height: 24, + // decoration: BoxDecoration( + // color: context.color.tertiaryColor, + // borderRadius: + // BorderRadius.circular(4)), + // child: FittedBox( + // fit: BoxFit.none, + // child: Container( + // width: 14, + // height: 14, + // child: SvgPicture.asset( + // AppIcons.promoted, + // color: context.color.buttonColor, + // width: 10, + // height: 10, + // fit: BoxFit.fitWidth, + // ), + // ), + // ), + // ), + + PositionedDirectional( + bottom: 6, + start: 6, + child: Container( + height: 19, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: context.color.secondaryColor + .withOpacity(0.7), + borderRadius: + BorderRadius.circular(4)), + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 2, sigmaY: 3), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0), + child: Center( + child: Text( + property.properyType == 'Rent' ? 'Sewa' : 'Jual', + ) + .color( + context.color.textColorDark, + ) + .bold() + .size(context.font.smaller), + ), + ), + ), + ), + ), + ], + ), + ), + if (statusButton != null) + Padding( + padding: const EdgeInsets.symmetric( + vertical: 3.0, horizontal: 3.0), + child: Container( + decoration: BoxDecoration( + color: statusButton!.color, + borderRadius: BorderRadius.circular(4)), + width: 80, + height: 120 - 90 - 8, + child: Center( + child: Text(statusButton!.lable) + .size(context.font.small) + .bold() + .color(statusButton?.textColor ?? + Colors.black)), + ), + ) + ], + ), + Expanded( + child: Padding( + padding: const EdgeInsets.only( + top: 5, + left: 12, + bottom: 5, + right: 12, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + Row( + children: [ + UiUtils.imageType( + property.category!.image ?? "", + width: 18, + height: 18, + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null), + const SizedBox( + width: 5, + ), + Expanded( + child: + Text(property.category!.category!) + .setMaxLines(lines: 1) + .size( + context.font.small + .rf(context), + ) + .bold( + weight: FontWeight.w400, + ) + .color( + context.color.textLightColor, + ), + ), + if (showLikeButton ?? true) + Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: context.color.secondaryColor, + shape: BoxShape.circle, + boxShadow: const [ + BoxShadow( + color: + Color.fromARGB(12, 0, 0, 0), + offset: Offset(0, 2), + blurRadius: 15, + spreadRadius: 0, + ) + ], + ), + child: LikeButtonWidget( + property: property, + onLikeChanged: onLikeChange, + ), + ), + ], + ), + if (property.properyType + .toString() + .toLowerCase() == + "rent") ...[ + Text( + rentPrice, + ) + .size(context.font.large) + .color(context.color.tertiaryColor) + .bold(weight: FontWeight.w700), + ] else ...[ + Text( + property.price! + .priceFormate( + disabled: false) + .toString() + .formatAmount( + prefix: true, + ), + ) + .size(context.font.large) + .color(context.color.tertiaryColor) + .bold(weight: FontWeight.w700), + ], + Text( + property.title!.firstUpperCase(), + ) + .setMaxLines(lines: 1) + .size(context.font.large) + .color(context.color.textColorDark), + if (property.city != "") + Row( + children: [ + Icon( + Icons.location_on, + size: 16, + color: context.color.textLightColor, + ), + Expanded( + child: Text( + property.city?.trim() ?? "" + ) + .setMaxLines(lines: 1) + .color( + context.color.textLightColor), + ) + ], + ) + ], + ), + ), + ), + ], + ), + ), + + if (useRow == false || useRow == null) ...addBottom ?? [], + + if (useRow == true) ...{Row(children: addBottom ?? [])} + + // ...addBottom ?? [] + ], + ), + if (showDeleteButton ?? false) + PositionedDirectional( + top: 32 * 2, + end: 12, + child: InkWell( + onTap: () { + onDeleteTap?.call(); + }, + child: Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: context.color.secondaryColor, + shape: BoxShape.circle, + boxShadow: const [ + BoxShadow( + color: Color.fromARGB(33, 0, 0, 0), + offset: Offset(0, 2), + blurRadius: 15, + spreadRadius: 0) + ], + ), + child: SizedBox( + height: 24, + width: 24, + child: FittedBox( + fit: BoxFit.none, + child: SvgPicture.asset( + AppIcons.bin, + color: context.color.tertiaryColor, + width: 18, + height: 18, + ), + ), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} + +class StatusButton { + final String lable; + final Color color; + final Color? textColor; + StatusButton({ + required this.lable, + required this.color, + this.textColor, + }); +} diff --git a/lib/Ui/screens/home/category_list.dart b/lib/Ui/screens/home/category_list.dart new file mode 100644 index 0000000..f1ef2a1 --- /dev/null +++ b/lib/Ui/screens/home/category_list.dart @@ -0,0 +1,162 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; + +import '../../../app/routes.dart'; +import '../../../data/cubits/category/fetch_category_cubit.dart'; +import '../../../data/model/category.dart'; +import '../../../utils/AdMob/bannerAdLoadWidget.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; + +class CategoryList extends StatefulWidget { + final String? from; + const CategoryList({Key? key, this.from}) : super(key: key); + + @override + State createState() => _CategoryListState(); + + static Route route(RouteSettings routeSettings) { + Map? args = routeSettings.arguments as Map?; + return BlurredRouter( + builder: (_) => CategoryList(from: args?['from']), + ); + } +} + +class _CategoryListState extends State + with TickerProviderStateMixin { + final ScrollController _pageScrollController = ScrollController(); + + @override + void initState() { + _pageScrollController.addListener(() { + if (_pageScrollController.isEndReached()) { + if (context.read().hasMoreData()) { + context.read().fetchCategoriesMore(); + } + } + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: UiUtils.buildAppBar( + context, + showBackButton: true, + title: UiUtils.translate(context, "categoriesLbl"), + ), + bottomNavigationBar: const BottomAppBar( + child: BannerAdWidget(bannerSize: AdSize.banner), + ), + body: BlocConsumer( + listener: ((context, state) { + // if (state is FetchCategorySuccess) {} + }), + builder: (context, state) { + if (state is FetchCategoryInProgress) { + return UiUtils.progress(); + } + if (state is FetchCategorySuccess) { + return Column( + children: [ + Expanded( + child: GridView.builder( + controller: _pageScrollController, + padding: const EdgeInsets.symmetric( + horizontal: 15, + vertical: 15, + ), + itemCount: state.categories.length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: MediaQuery.of(context).size.width / + (MediaQuery.of(context).size.height / 3.5)), + itemBuilder: (context, index) { + Category category = state.categories[index]; + return Padding( + padding: const EdgeInsets.all(1.5), + child: InkWell( + onTap: () { + if (widget.from == Routes.filterScreen) { + Navigator.pop(context, category); + } else { + Constant.propertyFilter = null; + HelperUtils.goToNextPage( + Routes.propertiesList, + context, + false, + args: { + 'catID': category.id, + 'catName': category.category + }, + ); //pass current index category id & name here + } + }, + child: Container( + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(10), + border: Border.all( + width: 1.5, + color: context.color.borderColor)), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 50, + height: 50, + alignment: Alignment.center, + child: UiUtils.imageType(category.image!, + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null)), + const SizedBox(height: 5), + Text(category.category!, + textAlign: TextAlign.center, + maxLines: 1, + overflow: TextOverflow.ellipsis) + ]), + ), + ), + ); + }, + ), + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ); + } + + return Container(); + }, + ), + ); + // body: + // BlocBuilder(builder: (context, state) { + // if (state is CategoryFetchProgress) { + // return const Center( + // child: CircularProgressIndicator(), + // ); + // } else if (state is CategoryFetchSuccess) { + // initCategoryAnimations(state.categorylist); + // categorieslist.clear(); + // categorieslist.addAll(state.categorylist); + + // return gridWidget(); + // } else if (state is ChangeSelectedCategory) { + // return gridWidget(); + // } else { + // return const SizedBox.shrink(); + // } + // }), + // ); + } +} diff --git a/lib/Ui/screens/home/change_language_screen.dart b/lib/Ui/screens/home/change_language_screen.dart new file mode 100644 index 0000000..1f55eef --- /dev/null +++ b/lib/Ui/screens/home/change_language_screen.dart @@ -0,0 +1,98 @@ +import 'package:ebroker/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import 'package:ebroker/data/cubits/system/fetch_language_cubit.dart'; +import 'package:ebroker/data/cubits/system/fetch_system_settings_cubit.dart'; +import 'package:ebroker/data/cubits/system/language_cubit.dart'; +import 'package:ebroker/data/helper/widgets.dart'; +import 'package:ebroker/data/model/system_settings_model.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/hive_utils.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +// import 'package:bloc/src/bloc_base.dart'; + +class LanguagesListScreen extends StatelessWidget { + const LanguagesListScreen({super.key}); + static Route route(RouteSettings settings) { + return BlurredRouter( + builder: (context) => const LanguagesListScreen(), + ); + } + + @override + Widget build(BuildContext context) { + if (context + .watch() + .getSetting(SystemSetting.language) == + null) { + return Scaffold( + backgroundColor: context.color.primaryColor, + appBar: UiUtils.buildAppBar(context, + showBackButton: true, + title: UiUtils.translate(context, "chooseLanguage")), + body: Center(child: UiUtils.progress()), + ); + } + + List setting = context + .watch() + .getSetting(SystemSetting.language) as List; + + var language = context.watch().state; + return Scaffold( + backgroundColor: context.color.primaryColor, + appBar: UiUtils.buildAppBar(context, + showBackButton: true, + title: UiUtils.translate(context, "chooseLanguage")), + body: BlocListener( + listener: (context, state) { + if (state is FetchLanguageInProgress) { + Widgets.showLoader(context); + } + if (state is FetchLanguageSuccess) { + Widgets.hideLoder(context); + Map map = state.toMap(); + var data = map['file_name']; + map['data'] = data; + map.remove("file_name"); + + HiveUtils.storeLanguage(map); + context.read().emit(LanguageLoader(state.code)); + } + }, + child: ListView.builder( + physics: const BouncingScrollPhysics(), + itemCount: setting.length, + padding: const EdgeInsets.symmetric(horizontal: 20), + itemBuilder: (context, index) { + Color color = (language as LanguageLoader).languageCode == + setting[index]['code'] + ? context.color.tertiaryColor + : context.color.textLightColor.withOpacity(0.03); + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Container( + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(10), + ), + child: ListTile( + onTap: () { + context + .read() + .getLanguage(setting[index]['code']); + }, + title: Text(setting[index]['name']) + .color( + (language).languageCode == setting[index]['code'] + ? context.color.buttonColor + : context.color.textColorDark) + .bold()), + ), + ); + }), + ), + ); + } +} diff --git a/lib/Ui/screens/home/city_properties_screen.dart b/lib/Ui/screens/home/city_properties_screen.dart new file mode 100644 index 0000000..6723e1b --- /dev/null +++ b/lib/Ui/screens/home/city_properties_screen.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +class CityPropertiesScreen extends StatefulWidget { + final String cityName; + + const CityPropertiesScreen({super.key, required this.cityName}); + + @override + State createState() => _CityPropertiesScreenState(); +} + +class _CityPropertiesScreenState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container(), + ); + } +} diff --git a/lib/Ui/screens/home/home_screen.dart b/lib/Ui/screens/home/home_screen.dart new file mode 100644 index 0000000..ca5f902 --- /dev/null +++ b/lib/Ui/screens/home/home_screen.dart @@ -0,0 +1,1441 @@ +// import 'dart:developer' as developer; +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:ebroker/Ui/screens/home/Widgets/category_card.dart'; +import 'package:ebroker/Ui/screens/home/Widgets/property_gradient_card.dart'; +import 'package:ebroker/Ui/screens/home/Widgets/property_horizontal_card.dart'; +import 'package:ebroker/Ui/screens/project/view/project_list_screen.dart'; +import 'package:ebroker/Ui/screens/proprties/viewAll.dart'; +import 'package:ebroker/app/default_app_setting.dart'; +import 'package:ebroker/data/cubits/Personalized/fetch_personalized_properties.dart'; +import 'package:ebroker/data/cubits/Utility/proeprty_edit_global.dart'; +import 'package:ebroker/data/cubits/category/fetch_cities_category.dart'; +import 'package:ebroker/data/cubits/project/fetch_projects.dart'; +import 'package:ebroker/data/cubits/property/fetch_city_property_list.dart'; +import 'package:ebroker/data/cubits/property/fetch_nearby_property_cubit.dart'; +import 'package:ebroker/data/cubits/property/fetch_recent_properties.dart'; +import 'package:ebroker/data/model/category.dart'; +import 'package:ebroker/data/model/project_model.dart'; +import 'package:ebroker/utils/AdMob/bannerAdLoadWidget.dart'; +import 'package:ebroker/utils/guestChecker.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:uni_links/uni_links.dart'; + +import '../../../app/app.dart'; +import '../../../app/routes.dart'; +import '../../../data/cubits/category/fetch_category_cubit.dart'; +import '../../../data/cubits/favorite/add_to_favorite_cubit.dart'; +import '../../../data/cubits/favorite/fetch_favorites_cubit.dart'; +import '../../../data/cubits/property/fetch_home_properties_cubit.dart'; +import '../../../data/cubits/property/fetch_most_liked_properties.dart'; +import '../../../data/cubits/property/fetch_most_viewed_properties_cubit.dart'; +import '../../../data/cubits/property/fetch_promoted_properties_cubit.dart'; +import '../../../data/cubits/slider_cubit.dart'; +import '../../../data/cubits/system/fetch_system_settings_cubit.dart'; +import '../../../data/cubits/system/get_api_keys_cubit.dart'; +import '../../../data/helper/design_configs.dart'; +import '../../../data/model/property_model.dart'; +import '../../../data/model/system_settings_model.dart'; +import '../../../settings.dart'; +import '../../../utils/AppIcon.dart'; +import '../../../utils/DeepLink/nativeDeepLinkManager.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/api.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/deeplinkManager.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/hive_utils.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/sliver_grid_delegate_with_fixed_cross_axis_count_and_fixed_height.dart'; +import '../../../utils/ui_utils.dart'; +import '../main_activity.dart'; +import '../widgets/Erros/no_data_found.dart'; +import '../widgets/Erros/no_internet.dart'; +import '../widgets/Erros/something_went_wrong.dart'; +import '../widgets/blurred_dialoge_box.dart'; +import '../widgets/shimmerLoadingContainer.dart'; +import 'Widgets/city_heading_card.dart'; +import 'Widgets/header_card.dart'; +import 'Widgets/homeListener.dart'; +import 'Widgets/home_profile_image_card.dart'; +import 'Widgets/home_search.dart'; +import 'Widgets/home_shimmers.dart'; +import 'Widgets/location_widget.dart'; +import 'Widgets/property_card_big.dart'; +import 'slider_widget.dart'; + +const double sidePadding = 18; + +class HomeScreen extends StatefulWidget { + final String? from; + const HomeScreen({Key? key, this.from}) : super(key: key); + @override + HomeScreenState createState() => HomeScreenState(); +} + +class HomeScreenState extends State + with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { + @override + bool get wantKeepAlive => true; + List propertyLocalList = []; + bool isCategoryEmpty = false; + HomePageStateListener homeStateListener = HomePageStateListener(); + + @override + void initState() { + DeepLinkManager.initDeepLinks(context); + + getInitialLink().then((value) { + print("GOT THE LINK IN BACKGROUND $value"); + if (value == null) return; + + Navigator.push( + Constant.navigatorKey.currentContext!, + NativeLinkWidget.render( + RouteSettings(name: value), + ), + ); + }); + linkStream.listen((event) { + print("GOT THE LINK IN Forground $event"); + + Navigator.push( + Constant.navigatorKey.currentContext!, + NativeLinkWidget.render( + RouteSettings(name: event), + ), + ); + }); + + initializeSettings(); + addPageScrollListener(); + notificationPermissionChecker(); + fetchApiKeys(); + loadInitialData(context); + initializeHomeStateListener(); + + super.initState(); + } + + void initializeSettings() { + final settingsCubit = context.read(); + if (!const bool.fromEnvironment("force-disable-demo-mode", + defaultValue: false)) { + Constant.isDemoModeOn = + settingsCubit.getSetting(SystemSetting.demoMode) ?? false; + } + } + + void addPageScrollListener() { + homeScreenController.addListener(pageScrollListener); + } + + void initializeHomeStateListener() { + homeStateListener.init( + setState, + onNetAvailable: () { + loadInitialData(context); + }, + ); + } + + void fetchApiKeys() { + context.read().fetch(); + } + + void pageScrollListener() { + ///This will load data on page end + if (homeScreenController.isEndReached()) { + if (mounted) { + if (context.read().hasMoreData()) { + context.read().fetchMoreProperty(); + } + } + } + } + + void _onTapPromotedSeeAll() { + // Navigator.pushNamed(context, Routes.promotedPropertiesScreen); + StateMap stateMap = StateMap< + FetchPromotedPropertiesInitial, + FetchPromotedPropertiesInProgress, + FetchPromotedPropertiesSuccess, + FetchPromotedPropertiesFailure>(); + + ViewAllScreen( + title: "promotedProperties".translate( + context, + ), + map: stateMap, + ).open(context); + } + + void _onTapNearByPropertiesAll() { + StateMap stateMap = StateMap< + FetchNearbyPropertiesInitial, + FetchNearbyPropertiesInProgress, + FetchNearbyPropertiesSuccess, + FetchNearbyPropertiesFailure>(); + + ViewAllScreen( + title: "nearByProperties".translate(context), + map: stateMap, + ).open(context); + } + + void _onTapMostLikedAll() { + ///Navigator.pushNamed(context, Routes.mostLikedPropertiesScreen); + StateMap stateMap = StateMap< + FetchMostLikedPropertiesInitial, + FetchMostLikedPropertiesInProgress, + FetchMostLikedPropertiesSuccess, + FetchMostLikedPropertiesFailure>(); + + ViewAllScreen( + title: "mostLikedProperties".translate(context), + map: stateMap, + ).open(context); + } + + void _onTapMostViewedSeelAll() { + StateMap stateMap = StateMap< + FetchMostViewedPropertiesInitial, + FetchMostViewedPropertiesInProgress, + FetchMostViewedPropertiesSuccess, + FetchMostViewedPropertiesFailure>(); + + ViewAllScreen( + title: "mostViewed".translate(context), + map: stateMap, + ).open(context); + } + + void _onRefresh() { + context.read().fetch(forceRefresh: true); + context.read().fetchSlider(context, forceRefresh: true); + context.read().fetchCategories(forceRefresh: true); + context.read().fetch(forceRefresh: true); + context.read().fetch(forceRefresh: true); + context.read().fetch(forceRefresh: true); + context.read().fetch(forceRefresh: true); + context.read().fetchProjects(); + + context + .read() + .fetchCityCategory(forceRefresh: true); + context.read().fetch(forceRefresh: true); + } + + @override + Widget build(BuildContext context) { + super.build(context); + + HomeScreenDataBinding homeScreenState = homeStateListener.listen(context); + + // FirebaseMessaging.instance + // .getToken() + // .then((value) => log(value!, name: "FCM")); + // + HiveUtils.getJWT()?.log("JWT"); + // HiveUtils.getUserId()?.log("USER ID"); + return SafeArea( + child: RefreshIndicator( + color: context.color.tertiaryColor, + triggerMode: RefreshIndicatorTriggerMode.onEdge, + onRefresh: () async { + _onRefresh(); + }, + child: Scaffold( + appBar: AppBar( + elevation: 0, + leadingWidth: (HiveUtils.getCityName() != null && + HiveUtils.getCityName().toString().isNotEmpty) + ? 200.rw(context) + : 130, + leading: Padding( + padding: EdgeInsetsDirectional.only( + start: sidePadding.rw(context), + ), + child: (HiveUtils.getCityName() != null && + HiveUtils.getCityName().toString().isNotEmpty) + ? const LocationWidget() + : SizedBox( + child: LoadAppSettings().svg(appSettings.appHomeScreen!), + ), + ), + backgroundColor: const Color.fromARGB(0, 0, 0, 0), + actions: [ + GuestChecker.updateUI( + onChangeStatus: (bool? isGuest) { + Widget buildDefaultPersonSVG(BuildContext context) { + return Container( + width: 90, + height: 90, + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity(0.1), + shape: BoxShape.circle), + child: Center( + child: UiUtils.getSvg( + AppIcons.defaultPersonLogo, + color: context.color.tertiaryColor, + // fit: BoxFit.none, + width: 30, + height: 30, + ), + ), + ); + } + + if (isGuest == null) { + return buildDefaultPersonSVG(context); + } else if (isGuest == true) { + return SizedBox( + width: 90, + ); + } else { + return const CircularProfileImageWidget(); + } + }, + ) + ], + ), + backgroundColor: context.color.primaryColor, + body: Builder(builder: (context) { + if (homeScreenState.state == HomeScreenDataState.fail) { + return const SomethingWentWrong(); + } + + return BlocConsumer( + listener: (context, state) { + if (state is FetchCategoryInProgress) { + homeStateListener.setNetworkState(setState, true); + setState(() {}); + } + if (state is FetchSystemSettingsSuccess) { + homeStateListener.setNetworkState(setState, true); + + setState(() {}); + // var setting = context + // .read() + // .getSetting(SystemSetting.subscription); + // if (setting.length != 0) { + // String packageId = setting[0]['package_id'].toString(); + // Constant.subscriptionPackageId = packageId; + // } + } + }, + builder: (context, state) { + if (homeScreenState.state == HomeScreenDataState.success) { + } else if (homeScreenState.state == + HomeScreenDataState.nointernet) { + return NoInternet( + onRetry: () { + context.read().fetchSlider(context); + context.read().fetchCategories(); + context.read().fetch(); + context.read().fetch(); + context.read().fetchProperty(); + }, + ); + } + + if (homeScreenState.state == HomeScreenDataState.nodata) { + return Center( + child: NoDataFound( + onTap: () { + context.read().fetchSlider(context); + context.read().fetchCategories(); + + context.read().fetch(); + context.read().fetch(); + context + .read() + .fetchProperty(); + }, + ), + ); + } + + return SingleChildScrollView( + controller: homeScreenController, + physics: const BouncingScrollPhysics(), + padding: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).padding.top, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ///Looping through sections so arrange it + ...List.generate( + AppSettings.sections.length, + (index) { + HomeScreenSections section = + AppSettings.sections[index]; + if (section == HomeScreenSections.Search) { + return const HomeSearchField(); + } else if (section == HomeScreenSections.Slider) { + return sliderWidget(); + } else if (section == HomeScreenSections.Category) { + return categoryWidget(); + } else if (section == + HomeScreenSections.NearbyProperties) { + return buildNearByProperties(); + } else if (section == + HomeScreenSections.FeaturedProperties) { + return featuredProperties(homeScreenState, context); + } else if (section == + HomeScreenSections.PersonalizedFeed) { + return const PersonalizedPropertyWidget(); + } else if (section == + HomeScreenSections.RecentlyAdded) { + return const RecentPropertiesSectionWidget(); + } else if (section == + HomeScreenSections.MostLikedProperties) { + return mostLikedProperties( + homeScreenState, context); + } else if (section == HomeScreenSections.MostViewed) { + return mostViewedProperties( + homeScreenState, context); + } else if (section == + HomeScreenSections.PopularCities) { + return Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, + ), + child: Column( + children: [ + const BannerAdWidget(), + const SizedBox( + height: 10, + ), + popularCityProperties(), + ], + ), + ); + } else if (section == HomeScreenSections.project) { + return buildProjects(); + } else { + return const SizedBox.shrink(); + } + }, + ), + const SizedBox( + height: 30, + ), + ], + ), + ); + }, + ); + }), + ), + ), + ); + } + + bool cityEmpty() { + if (context.watch().state + is FetchCityCategorySuccess) { + return (context.watch().state + as FetchCityCategorySuccess) + .cities + .isEmpty; + } + return true; + } + + Widget buildProjects() { + return Column( + children: [ + if (!context.watch().isProjectEmpty()) + TitleHeader( + title: "Project section".translate(context), + onSeeAll: () { + Navigator.pushNamed(context, Routes.allProjectsScreen); + }, + ), + BlocBuilder( + builder: (context, state) { + if (state is FetchProjectsInProgress) { + return SizedBox( + height: 220, + child: ListView.separated( + itemCount: 4, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(horizontal: 14), + scrollDirection: Axis.horizontal, + shrinkWrap: true, + itemBuilder: (context, index) { + return CustomShimmer( + height: 220, + width: context.screenWidth * 0.9, + ); + }, + separatorBuilder: (context, index) { + return const SizedBox( + width: 8, + ); + }, + ), + ); + } + if (state is FetchProjectsSuccess) { + if (state.projects.isEmpty) return const SizedBox.shrink(); + return SizedBox( + height: 220, + child: ListView.separated( + itemCount: state.projects.length, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(horizontal: 14), + scrollDirection: Axis.horizontal, + shrinkWrap: true, + separatorBuilder: (context, index) { + return const SizedBox( + width: 8, + ); + }, + itemBuilder: (context, index) { + ProjectModel project = state.projects[index]; + return GestureDetector( + onTap: () { + GuestChecker.check( + onNotGuest: () { + if (context + .read() + .getRawSettings()['is_premium'] ?? + false) { + Navigator.pushNamed( + context, Routes.projectDetailsScreen, + arguments: { + "project": project, + }); + } else { + UiUtils.showBlurredDialoge(context, + dialoge: BlurredDialogBox( + title: "Subscription needed", + isAcceptContainesPush: true, + onAccept: () async { + Navigator.popAndPushNamed(context, + Routes.subscriptionPackageListRoute, + arguments: {"from": "home"}); + }, + content: const Text( + "Subscribe to package if you want to use this feature"))); + } + }, + ); + }, + child: ProjectCard( + title: project.title ?? "", + categoryIcon: project.category?.image ?? "", + url: project.image ?? "", + categoryName: project.category?.category ?? "", + description: project.description ?? "", + status: project.type ?? "", + ), + ); + }, + ), + ); + } + + return Container(); + }, + ), + ], + ); + } + + Widget popularCityProperties() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (!cityEmpty()) const CityHeadingCard(), + const SizedBox( + height: 8, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: BlocBuilder( + builder: (context, FetchCityCategoryState state) { + if (state is FetchCityCategorySuccess) { + return StaggeredGrid.count( + crossAxisCount: 2, + mainAxisSpacing: 4, + crossAxisSpacing: 4, + children: [ + ...List.generate(state.cities.length, (index) { + if ((index % 4 == 0 || index % 5 == 0)) { + return StaggeredGridTile.count( + crossAxisCellCount: 1, + mainAxisCellCount: 2, + child: buildCityCard(state, index), + ); + } else { + return StaggeredGridTile.count( + crossAxisCellCount: 1, + mainAxisCellCount: 1, + child: buildCityCard(state, index), + ); + } + }), + ], + ); + } + return Container(); + }, + ), + ), + ], + ); + } + + Widget mostViewedProperties( + HomeScreenDataBinding homeScreenState, BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (!homeScreenState.dataAvailability.isMostViewdPropertyEmpty) + TitleHeader( + onSeeAll: _onTapMostViewedSeelAll, + title: UiUtils.translate(context, "mostViewed")), + if (!homeScreenState.dataAvailability.isMostViewdPropertyEmpty) + buildMostViewedProperties(), + ], + ); + } + + Widget mostLikedProperties( + HomeScreenDataBinding homeScreenState, BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (!homeScreenState.dataAvailability.isMostLikedPropertiesEmpty) ...[ + TitleHeader( + onSeeAll: _onTapMostLikedAll, + title: UiUtils.translate( + context, + "mostLikedProperties", + ), + ), + buildMostLikedProperties(), + const SizedBox( + height: 15, + ), + ], + ], + ); + } + + Widget featuredProperties( + HomeScreenDataBinding homeScreenState, BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (!homeScreenState.dataAvailability.isPromotedPropertyEmpty) + TitleHeader( + onSeeAll: _onTapPromotedSeeAll, + title: UiUtils.translate( + context, + "promotedProperties", + ), + ), + if (!homeScreenState.dataAvailability.isPromotedPropertyEmpty) + buildPromotedProperites(), + ], + ); + } + + Widget sliderWidget() { + return BlocConsumer( + listener: (context, state) { + if (state is SliderFetchSuccess) { + homeStateListener.setNetworkState(setState, true); + setState(() {}); + } + }, + builder: (context, state) { + if (state is SliderFetchInProgress) { + return const SliderShimmer(); + } + if (state is SliderFetchFailure) { + return Container(); + } + if (state is SliderFetchSuccess) { + if (state.sliderlist.isNotEmpty) { + return const SliderWidget(); + } + } + return Container(); + }, + ); + } + + Widget buildCityCard(FetchCityCategorySuccess state, int index) { + return GestureDetector( + onTap: () { + context.read().fetch( + cityName: state.cities[index].name.toString(), + forceRefresh: true, + ); + + var stateMap = StateMap< + FetchCityPropertyInitial, + FetchCityPropertyInProgress, + FetchCityPropertySuccess, + FetchCityPropertyFail>(); + + ViewAllScreen( + title: state.cities[index].name.firstUpperCase(), + map: stateMap, + ).open(context); + }, + child: Container( + decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)), + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Stack( + fit: StackFit.expand, + children: [ + CachedNetworkImage( + imageUrl: state.cities[index].image, + filterQuality: FilterQuality.high, + fit: BoxFit.cover, + ), + Container( + width: double.infinity, + height: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Colors.black.withOpacity(0.76), + Colors.black.withOpacity(0.68), + Colors.black.withOpacity(0) + ], + ), + ), + ), + PositionedDirectional( + bottom: 8, + start: 8, + child: Text( + "${state.cities[index].name.toString().firstUpperCase()} (${state.cities[index].count})") + .color(context.color.buttonColor), + ), + ], + ), + ), + ), + ); + } + + Widget buildPromotedProperites() { + return BlocBuilder( + builder: (context, state) { + if (state is FetchPromotedPropertiesInProgress) { + return const PromotedPropertiesShimmer(); + } + if (state is FetchPromotedPropertiesFailure) { + return Text(state.error); + } + + if (state is FetchPromotedPropertiesSuccess) { + return SizedBox( + height: 261, + child: ListView.builder( + itemCount: state.properties.length.clamp(0, 6), + shrinkWrap: true, + padding: const EdgeInsets.symmetric( + horizontal: sidePadding, + ), + physics: const BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) { + GlobalKey thisITemkye = GlobalKey(); + + ///Model + PropertyModel propertymodel = state.properties[index]; + propertymodel = + context.watch().get(propertymodel); + return GestureDetector( + onTap: () { + FirebaseAnalytics.instance + .logEvent(name: "preview_property", parameters: { + "user_ids": HiveUtils.getUserId(), + "from_section": "featured", + "property_id": propertymodel.id, + "category_id": propertymodel.category!.id + }); + + HelperUtils.goToNextPage( + Routes.propertyDetails, + context, + false, + args: { + 'propertyData': propertymodel, + 'propertiesList': state.properties, + 'fromMyProperty': false, + }, + ); + }, + child: BlocProvider( + create: (context) { + return AddToFavoriteCubitCubit(); + }, + child: PropertyCardBig( + key: thisITemkye, + isFirst: index == 0, + property: propertymodel, + onLikeChange: (type) { + if (type == FavoriteType.add) { + context + .read() + .add(propertymodel); + } else { + context + .read() + .remove(state.properties[index].id); + } + }, + ), + )); + }, + ), + ); + } + + return Container(); + }, + ); + } + + Widget buildMostLikedProperties() { + return BlocConsumer( + listener: (context, state) { + if (state is FetchMostLikedPropertiesFailure) { + if (state.error is ApiException) { + homeStateListener.setNetworkState( + setState, !(state.error.errorMessage == "no-internet")); + } + setState(() {}); + } + if (state is FetchMostLikedPropertiesSuccess) { + homeStateListener.setNetworkState(setState, true); + setState(() {}); + } + }, + builder: (context, state) { + if (state is FetchMostLikedPropertiesInProgress) { + return const MostLikedPropertiesShimmer(); + } + + if (state is FetchMostLikedPropertiesFailure) { + return Text(state.error.error.toString()); + } + if (state is FetchMostLikedPropertiesSuccess) { + return GridView.builder( + shrinkWrap: true, + padding: const EdgeInsets.symmetric( + horizontal: sidePadding, + ), + physics: const NeverScrollableScrollPhysics(), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight( + mainAxisSpacing: 15, crossAxisCount: 2, height: 260), + itemCount: state.properties.length.clamp(0, 4), + itemBuilder: (context, index) { + PropertyModel property = state.properties[index]; + + property = context.watch().get(property); + + return GestureDetector( + onTap: () { + HelperUtils.goToNextPage( + Routes.propertyDetails, context, false, + args: { + 'propertyData': property, + 'propertiesList': state.properties, + 'fromMyProperty': false, + }); + }, + child: BlocProvider( + create: (context) => AddToFavoriteCubitCubit(), + child: PropertyCardBig( + showEndPadding: false, + isFirst: index == 0, + onLikeChange: (type) { + if (type == FavoriteType.add) { + context.read().add(property); + } else { + context.read().remove(property.id); + } + }, + property: property, + ), + ), + ); + }, + ); + } + + return Container(); + }, + ); + } + + Widget buildNearByProperties() { + return BlocConsumer( + listener: (context, state) { + if (state is FetchNearbyPropertiesFailure) { + if (state.error is ApiException) { + homeStateListener.setNetworkState( + setState, !(state.error.error == "no-internet")); + } + + setState(() {}); + } + if (state is FetchNearbyPropertiesSuccess) { + setState(() {}); + } + }, + builder: (context, state) { + if (state is FetchNearbyPropertiesInProgress) { + return Column( + children: [ + TitleHeader( + onSeeAll: _onTapNearByPropertiesAll, + title: "${UiUtils.translate( + context, + "nearByProperties", + )}(${HiveUtils.getCityName()})", + ), + const NearbyPropertiesShimmer(), + ], + ); + } + + if (state is FetchNearbyPropertiesFailure) { + return Text(state.error.error.toString()); + } + if (state is FetchNearbyPropertiesSuccess) { + if (state.properties.isEmpty) { + return Container(); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TitleHeader( + onSeeAll: _onTapNearByPropertiesAll, + title: "${UiUtils.translate( + context, + "nearByProperties", + )}(${HiveUtils.getCityName()})", + ), + SizedBox( + height: 200, + child: ListView.builder( + shrinkWrap: true, + padding: const EdgeInsets.symmetric( + horizontal: sidePadding, + ), + physics: const BouncingScrollPhysics(), + itemCount: state.properties.length.clamp(0, 6), + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) { + PropertyModel model = state.properties[index]; + model = context.watch().get(model); + return PropertyGradiendCard( + model: model, + isFirst: index == 0, + showEndPadding: false, + ); + }), + ), + ], + ); + } + + return Container(); + }, + ); + } + + Widget buildMostViewedProperties() { + return BlocConsumer( + listener: (context, state) { + if (state is FetchMostViewedPropertiesFailure) { + if (state.error is ApiException) { + homeStateListener.setNetworkState( + setState, !(state.error.error == "no-internet")); + } + setState(() {}); + } + if (state is FetchMostViewedPropertiesSuccess) { + homeStateListener.setNetworkState(setState, true); + setState(() {}); + } + }, + builder: (context, state) { + if (state is FetchMostViewedPropertiesInProgress) { + return const MostViewdPropertiesShimmer(); + } + + if (state is FetchMostViewedPropertiesFailure) { + return Text(state.error.error.toString()); + } + if (state is FetchMostViewedPropertiesSuccess) { + return GridView.builder( + shrinkWrap: true, + padding: const EdgeInsets.symmetric( + horizontal: sidePadding, + ), + physics: const NeverScrollableScrollPhysics(), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight( + mainAxisSpacing: 15, crossAxisCount: 2, height: 260), + itemCount: state.properties.length.clamp(0, 4), + itemBuilder: (context, index) { + PropertyModel property = state.properties[index]; + property = context.watch().get(property); + return GestureDetector( + onTap: () { + HelperUtils.goToNextPage( + Routes.propertyDetails, context, false, + args: { + 'propertyData': property, + 'propertiesList': state.properties, + 'fromMyProperty': false, + }); + }, + child: BlocProvider( + create: (context) => AddToFavoriteCubitCubit(), + child: PropertyCardBig( + showEndPadding: false, + isFirst: index == 0, + onLikeChange: (type) { + if (type == FavoriteType.add) { + context.read().add(property); + } else { + context.read().remove(property.id); + } + }, + property: property, + ), + ), + ); + }, + ); + } + + return Container(); + }, + ); + } + + Widget categoryWidget() { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 44.rh(context), + child: BlocConsumer( + listener: (context, state) { + if (state is FetchCategoryFailure) { + if (state.errorMessage == "auth-expired") { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "authExpired")); + + HiveUtils.logoutUser( + context, + onLogout: () {}, + ); + } + } + + if (state is FetchCategorySuccess) { + isCategoryEmpty = state.categories.isEmpty; + setState(() {}); + } + }, + builder: (context, state) { + if (state is FetchCategoryInProgress) { + return const CategoryShimmer(); + } + if (state is FetchCategoryFailure) { + return Center( + child: Text(state.errorMessage.toString()), + ); + } + if (state is FetchCategorySuccess) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + horizontal: sidePadding, + ), + physics: const BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + itemCount: state.categories.length + .clamp(0, Constant.maxCategoryLength), + itemBuilder: (context, index) { + Category category = state.categories[index]; + Constant.propertyFilter = null; + if (index == (Constant.maxCategoryLength - 1)) { + return Padding( + padding: const EdgeInsetsDirectional.only(start: 5.0), + child: GestureDetector( + onTap: () { + Navigator.pushNamed(context, Routes.categories); + }, + child: Container( + constraints: BoxConstraints( + minWidth: 100.rw(context), + ), + height: 44.rh(context), + alignment: Alignment.center, + decoration: DesignConfig.boxDecorationBorder( + color: context.color.secondaryColor, + radius: 10, + borderWidth: 1.5, + borderColor: context.color.borderColor, + ), + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 10), + child: Text(UiUtils.translate(context, "more")), + ), + ), + ), + ); + } + + return buildCategoryCard(context, category, index != 0); + }, + ); + } + return Container(); + }, + ), + ), + ], + ); + } + + Widget buildCategoryCard( + BuildContext context, Category category, bool? frontSpacing) { + return CategoryCard( + frontSpacing: frontSpacing, + onTapCategory: (category) { + currentVisitingCategoryId = category.id; + currentVisitingCategory = category; + Navigator.of(context).pushNamed(Routes.propertiesList, + arguments: {'catID': category.id, 'catName': category.category}); + }, + category: category); + } +} + +class RecentPropertiesSectionWidget extends StatefulWidget { + const RecentPropertiesSectionWidget({Key? key}) : super(key: key); + + @override + State createState() => + _RecentPropertiesSectionWidgetState(); +} + +class _RecentPropertiesSectionWidgetState + extends State { + void _onRecentlyAddedSeeAll() { + dynamic statemap = StateMap< + FetchRecentProepertiesInitial, + FetchRecentPropertiesInProgress, + FetchRecentPropertiesSuccess, + FetchRecentPropertiesFailur>(); + ViewAllScreen( + title: "recentlyAdded".translate(context), + map: statemap, + ).open(context); + } + + @override + Widget build(BuildContext context) { + bool isRecentEmpty() { + if (context.watch().state + is FetchRecentPropertiesSuccess) { + return (context.watch().state + as FetchRecentPropertiesSuccess) + .properties + .isEmpty; + } + + return true; + } + + return Column( + children: [ + if (!isRecentEmpty()) + TitleHeader( + enableShowAll: false, + title: "Baru Ditambahkan", + onSeeAll: () { + _onRecentlyAddedSeeAll(); + }, + ), + LayoutBuilder(builder: (context, c) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: sidePadding), + child: BlocBuilder(builder: (context, state) { + if (state is FetchRecentPropertiesInProgress) { + return ListView.builder( + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Row( + children: [ + const ClipRRect( + clipBehavior: Clip.antiAliasWithSaveLayer, + borderRadius: BorderRadius.all(Radius.circular(15)), + child: CustomShimmer(height: 90, width: 90), + ), + const SizedBox( + width: 10, + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 10, + ), + const SizedBox( + height: 10, + ), + const CustomShimmer( + height: 10, + ), + const SizedBox( + height: 10, + ), + CustomShimmer( + height: 10, + width: c.maxWidth / 1.2, + ), + const SizedBox( + height: 10, + ), + CustomShimmer( + height: 10, + width: c.maxWidth / 4, + ), + ], + ), + ), + ], + ), + ); + }, + shrinkWrap: true, + itemCount: 5, + ); + } + + if (state is FetchRecentPropertiesSuccess) { + return ListView.builder( + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + PropertyModel modal = state.properties[index]; + modal = context.watch().get(modal); + // developer.log("API RESULT IS $modal"); + return GestureDetector( + onTap: () { + HelperUtils.goToNextPage( + Routes.propertyDetails, + context, + false, + args: { + 'propertyData': modal, + 'propertiesList': state.properties, + 'fromMyProperty': false, + }, + ); + }, + child: PropertyHorizontalCard( + property: modal, + additionalImageWidth: 10, + )); + }, + itemCount: state.properties.length.clamp(0, 4), + shrinkWrap: true, + ); + } + if (state is FetchRecentPropertiesFailur) { + return Container(); + } + + return Container(); + }), + ); + }), + ], + ); + } +} + +class PersonalizedPropertyWidget extends StatelessWidget { + const PersonalizedPropertyWidget({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is FetchPersonalizedPropertyInProgress) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TitleHeader( + onSeeAll: () {}, + title: "personalizedFeed".translate(context), + ), + const PromotedPropertiesShimmer(), + ], + ); + } + + if (state is FetchPersonalizedPropertySuccess) { + if (state.properties.isEmpty) return const SizedBox.shrink(); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TitleHeader( + onSeeAll: () { + StateMap stateMap = StateMap< + FetchPersonalizedPropertyInitial, + FetchPersonalizedPropertyInProgress, + FetchPersonalizedPropertySuccess, + FetchPersonalizedPropertyFail>(); + + ViewAllScreen( + title: "personalizedFeed".translate(context), + map: stateMap, + ).open(context); + }, + title: "personalizedFeed".translate(context), + ), + SizedBox( + height: 261, + child: ListView.builder( + itemCount: state.properties.length.clamp(0, 6), + shrinkWrap: true, + padding: const EdgeInsets.symmetric( + horizontal: sidePadding, + ), + physics: const BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) { + GlobalKey thisITemkye = GlobalKey(); + + PropertyModel propertymodel = state.properties[index]; + propertymodel = + context.watch().get(propertymodel); + return GestureDetector( + onTap: () { + FirebaseAnalytics.instance + .logEvent(name: "preview_property", parameters: { + "user_ids": HiveUtils.getUserId(), + "from_section": "featured", + "property_id": propertymodel.id, + "category_id": propertymodel.category!.id + }); + + HelperUtils.goToNextPage( + Routes.propertyDetails, + context, + false, + args: { + 'propertyData': propertymodel, + 'propertiesList': state.properties, + 'fromMyProperty': false, + }, + ); + }, + child: BlocProvider( + create: (context) { + return AddToFavoriteCubitCubit(); + }, + child: PropertyCardBig( + key: thisITemkye, + isFirst: index == 0, + property: propertymodel, + onLikeChange: (type) { + if (type == FavoriteType.add) { + context + .read() + .add(propertymodel); + } else { + context + .read() + .remove(state.properties[index].id); + } + }, + ), + )); + }, + ), + ), + ], + ); + } + + return Container(); + }, + ); + } +} + +Future notificationPermissionChecker() async { + if (!(await Permission.notification.isGranted)) { + await Permission.notification.request(); + } +} + +extension mMap on Map { + Map get(String key) { + var m = this[key]; + if (m is Map) { + return m; + } else { + throw "Child is not map"; + } + } + + Map where(String query) { + final parts = query.split('='); + if (parts.length == 2) { + final key = parts[0].trim(); + final value = parts[1].replaceAll(RegExp(r"^\s*'|\s*'$"), '').trim(); + + return Map.fromEntries(entries.where((entry) { + final entryKey = entry.key.toString(); + final entryValue = entry.value.toString(); + + return entryKey == key && entryValue == value; + })); + } else { + throw "Invalid query format"; + } + } +} diff --git a/lib/Ui/screens/home/projects.dart b/lib/Ui/screens/home/projects.dart new file mode 100644 index 0000000..a6c12ab --- /dev/null +++ b/lib/Ui/screens/home/projects.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class ProjectsWidget extends StatelessWidget { + const ProjectsWidget({super.key}); + + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} diff --git a/lib/Ui/screens/home/search_screen.dart b/lib/Ui/screens/home/search_screen.dart new file mode 100644 index 0000000..22e3692 --- /dev/null +++ b/lib/Ui/screens/home/search_screen.dart @@ -0,0 +1,379 @@ +import 'dart:async'; + +import 'package:ebroker/Ui/screens/widgets/Erros/no_internet.dart'; +import 'package:ebroker/utils/AdMob/bannerAdLoadWidget.dart'; +import 'package:ebroker/utils/api.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; + +import '../../../app/routes.dart'; +import '../../../data/cubits/property/search_property_cubit.dart'; +import '../../../data/model/property_model.dart'; +import '../../../utils/AppIcon.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/Erros/something_went_wrong.dart'; +import 'Widgets/property_horizontal_card.dart'; + +class SearchScreen extends StatefulWidget { + final bool autoFocus; + final bool openFilterScreen; + const SearchScreen( + {Key? key, required this.autoFocus, required this.openFilterScreen}) + : super(key: key); + static Route route(RouteSettings settings) { + Map? arguments = settings.arguments as Map?; + return BlurredRouter( + builder: (context) { + return SearchScreen( + autoFocus: arguments?['autoFocus'], + openFilterScreen: arguments?['openFilterScreen'], + ); + }, + ); + } + + @override + SearchScreenState createState() => SearchScreenState(); +} + +class SearchScreenState extends State + with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { + @override + bool get wantKeepAlive => true; + bool isFocused = false; + String previouseSearchQuery = ""; + static TextEditingController searchController = TextEditingController(); + int offset = 0; + late ScrollController controller; + List propertylist = []; + List idlist = []; + Timer? _searchDelay; + bool showContent = true; + @override + void initState() { + super.initState(); + if (widget.openFilterScreen) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + Navigator.pushNamed(context, Routes.filterScreen); + }); + } + // context.read().fetchProperty(context, {}); + context.read().searchProperty("", offset: 0); + searchController = TextEditingController(); + searchController.addListener(searchPropertyListener); + controller = ScrollController()..addListener(pageScrollListen); + } + + void pageScrollListen() { + if (controller.isEndReached()) { + if (context.read().hasMoreData()) { + context.read().fetchMoreSearchData(); + } + } + } + +//this will listen and manage search + void searchPropertyListener() { + _searchDelay?.cancel(); + searchCallAfterDelay(); + } + +//This will create delay so we don't face rapid api call + void searchCallAfterDelay() { + _searchDelay = Timer(const Duration(milliseconds: 500), propertySearch); + } + + ///This will call api after some delay + void propertySearch() { + // if (searchController.text.isNotEmpty) { + if (previouseSearchQuery != searchController.text) { + context + .read() + .searchProperty(searchController.text, offset: 0); + previouseSearchQuery = searchController.text; + } + // } else { + // context.read().clearSearch(); + // } + } + + Widget filterOptionsBtn() { + return IconButton( + onPressed: () { + Navigator.pushNamed(context, Routes.filterScreen).then((value) { + if (value == true && searchController.text != "") { + context + .read() + .searchProperty(searchController.text, offset: 0); + } + }); + }, + icon: Icon( + Icons.filter_list_rounded, + color: Theme.of(context).colorScheme.blackColor, + )); + } + + @override + Widget build(BuildContext context) { + super.build(context); + return Scaffold( + backgroundColor: context.color.primaryColor, + appBar: AppBar( + automaticallyImplyLeading: false, + leading: BackButton( + color: context.color.tertiaryColor, + ), + elevation: 0, + backgroundColor: context.color.primaryColor, + title: searchTextField(), + ), + bottomNavigationBar: const BottomAppBar( + child: BannerAdWidget(bannerSize: AdSize.banner), + ), + body: Column( + children: [ + // BlocBuilder( + // builder: (context, state) { + // log("state isss $state"); + // if (state is PropertyFetchSuccess) { + // return SingleChildScrollView( + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // const Padding( + // padding: EdgeInsets.symmetric(horizontal: 16.0), + // child: Text("Latest properties"), + // ), + // ListView.builder( + // shrinkWrap: true, + // physics: const NeverScrollableScrollPhysics(), + // padding: const EdgeInsets.symmetric(horizontal: 16), + // itemCount: state.propertylist.length, + // itemBuilder: (context, index) { + // return PropertyHorizontalCard( + // property: state.propertylist[index]); + // }, + // ), + // ], + // ), + // ); + // } + // if (state is PropertyFetchFailure) { + // log(state.errmsg); + // return Container( + // child: Text(state.errmsg.toString()), + // ); + // } + // return Container(); + // }, + // ), + const SizedBox(height: 10), + Expanded( + child: BlocBuilder( + builder: (context, state) { + return listWidget(state); + }, + ), + ), + ], + ), + ); + } + + Widget listWidget(SearchPropertyState state) { + if (state is SearchPropertyFetchProgress) { + return Center( + child: + UiUtils.progress(normalProgressColor: context.color.tertiaryColor), + ); + } + if (state is SearchPropertyFailure) { + if (state.errorMessage is ApiException) { + return NoInternet( + onRetry: () { + context.read().searchProperty("", offset: 0); + }, + ); + } + return const SomethingWentWrong(); + } + + if (state is SearchPropertySuccess) { + if (state.searchedroperties.isEmpty) { + return Center( + child: Text( + UiUtils.translate(context, "nodatafound"), + ), + ); + } + // if (searchController.text == "") { + // return Center( + // child: Text( + // UiUtils.getTranslatedLabel(context, "nodatafound"), + // ), + // ); + // } + return SingleChildScrollView( + controller: controller, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric( + vertical: 10, + ), + child: SizedBox( + width: MediaQuery.of(context).size.width, + child: Column( + children: [ + Wrap( + direction: Axis.horizontal, + children: + List.generate(state.searchedroperties.length, (index) { + PropertyModel property = state.searchedroperties[index]; + List propertiesList = state.searchedroperties; + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 0, + ), + child: GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); + HelperUtils.goToNextPage( + Routes.propertyDetails, context, false, args: { + 'propertyData': property, + 'propertiesList': propertiesList + }); + }, + child: PropertyHorizontalCard(property: property), + ), + ); + }), + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ), + ), + ); + } + return Container(); + } + + Widget setSearchIcon() { + return Padding( + padding: const EdgeInsets.all(8.0), + child: UiUtils.getSvg(AppIcons.search, + color: context.color.tertiaryColor)); + } + + Widget setSuffixIcon() { + return GestureDetector( + onTap: () { + searchController.clear(); + isFocused = false; //set icon color to black back + FocusScope.of(context).unfocus(); //dismiss keyboard + setState(() {}); + }, + child: Icon( + Icons.close_rounded, + color: Theme.of(context).colorScheme.blackColor, + size: 30, + ), + ); + } + + Widget searchTextField() { + return LayoutBuilder(builder: (context, c) { + return SizedBox( + width: c.maxWidth, + child: FittedBox( + fit: BoxFit.none, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 270.rw(context), + height: 50.rh(context), + alignment: Alignment.center, + decoration: BoxDecoration( + border: Border.all( + width: 1.5, color: context.color.borderColor), + borderRadius: const BorderRadius.all(Radius.circular(10)), + color: context.color.secondaryColor), + child: TextFormField( + autofocus: widget.autoFocus, + controller: searchController, + decoration: InputDecoration( + border: InputBorder.none, //OutlineInputBorder() + fillColor: Theme.of(context).colorScheme.secondaryColor, + hintText: UiUtils.translate(context, "searchHintLbl"), + prefixIcon: setSearchIcon(), + prefixIconConstraints: + const BoxConstraints(minHeight: 5, minWidth: 5), + ), + enableSuggestions: true, + onEditingComplete: () { + setState( + () { + isFocused = false; + }, + ); + FocusScope.of(context).unfocus(); + }, + onTap: () { + //change prefix icon color to primary + setState(() { + isFocused = true; + }); + })), + SizedBox( + width: 5, + ), + GestureDetector( + onTap: () { + Navigator.pushNamed( + context, + Routes.filterScreen, + ).then((value) { + if (value == true) { + context + .read() + .searchProperty(searchController.text, offset: 0); + } + }); + }, + child: Container( + width: 50.rw(context), + height: 50.rh(context), + decoration: BoxDecoration( + border: Border.all( + width: 1.5, color: context.color.borderColor), + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(10), + ), + child: Center( + child: UiUtils.getSvg(AppIcons.filter, + color: context.color.tertiaryColor), + ), + ), + ), + SizedBox( + width: c.maxWidth * 0.06, + ) + ], + ), + ), + ); + }); + } + + @override + void dispose() { + searchController.dispose(); + super.dispose(); + } +} diff --git a/lib/Ui/screens/home/slider_widget.dart b/lib/Ui/screens/home/slider_widget.dart new file mode 100644 index 0000000..2aed2cb --- /dev/null +++ b/lib/Ui/screens/home/slider_widget.dart @@ -0,0 +1,203 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../app/routes.dart'; +import '../../../data/Repositories/property_repository.dart'; +import '../../../data/cubits/slider_cubit.dart'; +import '../../../data/helper/widgets.dart'; +import '../../../data/model/data_output.dart'; +import '../../../data/model/home_slider.dart'; +import '../../../data/model/property_model.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/promoted_widget.dart'; +import 'home_screen.dart'; + +class SliderWidget extends StatefulWidget { + const SliderWidget({Key? key}) : super(key: key); + + @override + State createState() => _SliderWidgetState(); +} + +class _SliderWidgetState extends State + with AutomaticKeepAliveClientMixin { + final ValueNotifier _bannerIndex = ValueNotifier(0); + int bannersLength = 0; + late Timer _timer; + final PageController _pageController = PageController( + initialPage: 0, + ); + + @override + bool get wantKeepAlive => true; + + @override + void initState() { + super.initState(); + _timer = Timer.periodic(const Duration(seconds: 5), (Timer timer) { + if (_bannerIndex.value < bannersLength - 1) { + _bannerIndex.value++; + } else { + _bannerIndex.value = 0; + } + if (_pageController.hasClients) { + _pageController.animateToPage( + _bannerIndex.value, + duration: const Duration(milliseconds: 1000), + curve: Curves.easeIn, + ); + } + }); + } + + @override + void dispose() { + super.dispose(); + _bannerIndex.dispose(); + _timer.cancel(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + return BlocConsumer( + listener: (context, state) { + if ((state is SliderFetchFailure && !state.isUserDeactivated) || + state is SliderFetchSuccess) { + // context.read().fetchSlider(context); + } + }, + builder: (context, SliderState state) { + if (state is SliderFetchSuccess) { + bannersLength = state.sliderlist.length; + + return Column( + children: [ + SizedBox( + height: 15.rh(context), + ), + SizedBox( + height: 130.rh(context), + child: PageView.builder( + controller: _pageController, + clipBehavior: Clip.antiAlias, + physics: const BouncingScrollPhysics( + decelerationRate: ScrollDecelerationRate.fast, + ), + itemCount: state.sliderlist.length, + onPageChanged: (index) { + _bannerIndex.value = index; + }, + itemBuilder: (context, index) => _buildBanner( + state.sliderlist[index], + ), + ), + ), + const SizedBox( + height: 10, + ), + ], + ); + /* } else if (state is SliderFetchFailure && + state.isUserDeactivated == true) { + isUserDeactivated = true; */ + } + return const SizedBox.shrink(); + }, + ); + } + + Widget _buildBanner(HomeSlider banner) { + return GestureDetector( + onTap: () async { + try { + PropertyRepository fetch = PropertyRepository(); + + Widgets.showLoader(context); + + DataOutput dataOutput = + await fetch.fetchPropertyFromPropertyId(banner.propertysId); + + Future.delayed( + Duration.zero, + () { + Widgets.hideLoder(context); + HelperUtils.goToNextPage( + Routes.propertyDetails, + context, + false, + args: { + 'propertyData': dataOutput.modelList[0], + 'propertiesList': dataOutput.modelList, + 'fromMyProperty': false, + }, + ); + }, + ); + } catch (e) { + Widgets.hideLoder(context); + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "somethingWentWrng")); + } + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: sidePadding), + child: Stack( + clipBehavior: Clip.antiAlias, + children: [ + Container( + clipBehavior: Clip.antiAlias, + width: context.screenWidth, + height: context.screenHeight * 0.3, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(11), + border: Border.all( + color: Colors.transparent, + ), + ), + child: ClipRRect( + clipBehavior: Clip.antiAlias, + borderRadius: BorderRadius.circular(11), + child: UiUtils.getImage( + banner.image.toString(), + width: context.screenWidth, + fit: BoxFit.cover, + ), + ), + ), + PositionedDirectional( + top: 10, + start: 10, + child: Visibility( + visible: banner.promoted ?? false, + child: const PromotedCard(type: PromoteCardType.icon))) + ], + ), + ), + ); + } + + Row pageindicator({required int index, required int length}) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate(length, (indexDots) { + return AnimatedContainer( + duration: const Duration(microseconds: 300), + margin: const EdgeInsets.symmetric(horizontal: 5), + width: index == indexDots ? 8 : 6, + height: index == indexDots ? 8 : 6, + decoration: BoxDecoration( + color: index == indexDots + ? Theme.of(context).primaryColor + : Colors.transparent, + borderRadius: const BorderRadius.all(Radius.circular(40)), + border: Border.all( + color: Theme.of(context).primaryColor, width: 1))); + })); + } +} diff --git a/lib/Ui/screens/home/view_most_liked_properties.dart b/lib/Ui/screens/home/view_most_liked_properties.dart new file mode 100644 index 0000000..f37ffe5 --- /dev/null +++ b/lib/Ui/screens/home/view_most_liked_properties.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../app/routes.dart'; +import '../../../data/cubits/property/fetch_most_liked_properties.dart'; +import '../../../data/model/property_model.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/Erros/no_data_found.dart'; +import '../widgets/Erros/something_went_wrong.dart'; +import 'Widgets/property_horizontal_card.dart'; + +class MostLikedPropertiesScreen extends StatefulWidget { + const MostLikedPropertiesScreen({super.key}); + + static Route route(RouteSettings routeSettings) { + return BlurredRouter( + builder: (context) { + return const MostLikedPropertiesScreen(); + }, + ); + } + + @override + State createState() => + _MostLikedPropertiesScreenState(); +} + +class _MostLikedPropertiesScreenState extends State { + ///This Scroll controller for listen page end + final ScrollController _pageScollController = ScrollController(); + @override + void initState() { + _pageScollController.addListener(onPageEnd); + super.initState(); + } + + ///This method will listen page scroll changes + void onPageEnd() { + ///This is exetension which will check if we reached end or not + if (_pageScollController.isEndReached()) { + if (context.read().hasMoreData()) { + context.read().fetchMore(); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.primaryColor, + appBar: AppBar( + backgroundColor: context.color.secondaryColor, + elevation: 0, + iconTheme: IconThemeData(color: context.color.tertiaryColor), + title: Text( + UiUtils.translate(context, "mostLiked"), + ).color(context.color.tertiaryColor).size( + context.font.large, + ), + ), + body: BlocBuilder( + builder: (context, state) { + if (state is FetchMostLikedPropertiesInProgress) { + return Center( + child: UiUtils.progress( + normalProgressColor: context.color.tertiaryColor), + ); + } + if (state is FetchMostLikedPropertiesFailure) { + return const SomethingWentWrong(); + } + if (state is FetchMostLikedPropertiesSuccess) { + if (state.properties.isEmpty) { + return Center( + child: NoDataFound( + onTap: () { + context.read().fetch(); + }, + ), + ); + } + return Column( + children: [ + Expanded( + child: ScrollConfiguration( + behavior: RemoveGlow(), + child: ListView.builder( + controller: _pageScollController, + padding: const EdgeInsets.all(16), + itemCount: state.properties.length, + itemBuilder: (context, index) { + PropertyModel property = state.properties[index]; + return GestureDetector( + onTap: () { + HelperUtils.goToNextPage( + Routes.propertyDetails, context, false, + args: { + 'propertyData': property, + 'propertiesList': state.properties, + 'fromMyProperty': false, + }); + }, + child: PropertyHorizontalCard( + property: property, + )); + }, + ), + ), + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ); + } + return Container(); + }, + ), + ); + } +} diff --git a/lib/Ui/screens/home/view_most_viewed_properties.dart b/lib/Ui/screens/home/view_most_viewed_properties.dart new file mode 100644 index 0000000..f7f4c97 --- /dev/null +++ b/lib/Ui/screens/home/view_most_viewed_properties.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../app/routes.dart'; +import '../../../data/cubits/property/fetch_most_viewed_properties_cubit.dart'; +import '../../../data/model/property_model.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/Erros/no_data_found.dart'; +import '../widgets/Erros/something_went_wrong.dart'; +import 'Widgets/property_horizontal_card.dart'; + +class MostViewedPropertiesScreen extends StatefulWidget { + const MostViewedPropertiesScreen({super.key}); + + static Route route(RouteSettings routeSettings) { + return BlurredRouter( + builder: (context) { + return const MostViewedPropertiesScreen(); + }, + ); + } + + @override + State createState() => + _MostViewedPropertiesScreenState(); +} + +class _MostViewedPropertiesScreenState + extends State { + ///This Scroll controller for listen page end + final ScrollController _pageScollController = ScrollController(); + @override + void initState() { + _pageScollController.addListener(onPageEnd); + super.initState(); + } + + ///This method will listen page scroll changes + void onPageEnd() { + ///This is exetension which will check if we reached end or not + if (_pageScollController.isEndReached()) { + if (context.read().hasMoreData()) { + context.read().fetchMore(); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.primaryColor, + appBar: AppBar( + backgroundColor: context.color.secondaryColor, + elevation: 0, + iconTheme: IconThemeData(color: context.color.tertiaryColor), + title: Text(UiUtils.translate(context, "mostViewed")) + .color(context.color.tertiaryColor) + .size(context.font.large), + ), + body: BlocBuilder( + builder: (context, state) { + if (state is FetchMostViewedPropertiesInProgress) { + return Center( + child: UiUtils.progress( + normalProgressColor: context.color.tertiaryColor), + ); + } + if (state is FetchMostViewedPropertiesFailure) { + return const SomethingWentWrong(); + } + if (state is FetchMostViewedPropertiesSuccess) { + if (state.properties.isEmpty) { + return Center( + child: NoDataFound( + onTap: () { + context.read().fetch(); + }, + ), + ); + } + return Column( + children: [ + Expanded( + child: ScrollConfiguration( + behavior: RemoveGlow(), + child: ListView.builder( + controller: _pageScollController, + padding: const EdgeInsets.all(16), + itemCount: state.properties.length, + itemBuilder: (context, index) { + PropertyModel property = state.properties[index]; + return GestureDetector( + onTap: () { + HelperUtils.goToNextPage( + Routes.propertyDetails, context, false, + args: { + 'propertyData': property, + 'propertiesList': state.properties, + 'fromMyProperty': false, + }); + }, + child: PropertyHorizontalCard( + property: property, + )); + }, + ), + ), + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ); + } + return Container(); + }, + ), + ); + } +} diff --git a/lib/Ui/screens/home/view_nearby_properties.dart b/lib/Ui/screens/home/view_nearby_properties.dart new file mode 100644 index 0000000..178165d --- /dev/null +++ b/lib/Ui/screens/home/view_nearby_properties.dart @@ -0,0 +1,113 @@ +import 'package:ebroker/Ui/screens/home/Widgets/property_gradient_card.dart'; +import 'package:ebroker/data/cubits/property/fetch_nearby_property_cubit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../data/model/property_model.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/hive_utils.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/Erros/no_data_found.dart'; +import '../widgets/Erros/something_went_wrong.dart'; + +class NearbyAllPropertiesScreen extends StatefulWidget { + const NearbyAllPropertiesScreen({super.key}); + + static Route route(RouteSettings routeSettings) { + return BlurredRouter( + builder: (context) { + return const NearbyAllPropertiesScreen(); + }, + ); + } + + @override + State createState() => + _NearbyAllPropertiesScreenState(); +} + +class _NearbyAllPropertiesScreenState extends State { + ///This Scroll controller for listen page end + final ScrollController _pageScollController = ScrollController(); + @override + void initState() { + _pageScollController.addListener(onPageEnd); + super.initState(); + } + + ///This method will listen page scroll changes + void onPageEnd() { + ///This is exetension which will check if we reached end or not + if (_pageScollController.isEndReached()) { + if (context.read().hasMoreData()) { + context.read().fetchMore(); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.primaryColor, + appBar: AppBar( + backgroundColor: context.color.secondaryColor, + elevation: 0, + iconTheme: IconThemeData(color: context.color.tertiaryColor), + title: Text( + "${UiUtils.translate(context, "NearbyProperties")}(${HiveUtils.getCityName()})") + .color(context.color.tertiaryColor) + .size(context.font.large), + ), + body: BlocBuilder( + builder: (context, state) { + if (state is FetchNearbyPropertiesInProgress) { + return Center( + child: UiUtils.progress( + normalProgressColor: context.color.tertiaryColor), + ); + } + if (state is FetchNearbyPropertiesFailure) { + return const SomethingWentWrong(); + } + if (state is FetchNearbyPropertiesSuccess) { + if (state.properties.isEmpty) { + return Center( + child: NoDataFound( + onTap: () { + context.read().fetch(); + }, + ), + ); + } + return Column( + children: [ + Expanded( + child: ScrollConfiguration( + behavior: RemoveGlow(), + child: ListView.builder( + controller: _pageScollController, + padding: const EdgeInsets.all(16), + itemCount: state.properties.length, + itemBuilder: (context, index) { + PropertyModel property = state.properties[index]; + return Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: PropertyGradiendCard( + model: property, + ), + ); + }, + ), + ), + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ); + } + return Container(); + }, + ), + ); + } +} diff --git a/lib/Ui/screens/home/view_promoted_properties.dart b/lib/Ui/screens/home/view_promoted_properties.dart new file mode 100644 index 0000000..acd23a3 --- /dev/null +++ b/lib/Ui/screens/home/view_promoted_properties.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../app/routes.dart'; +import '../../../data/cubits/property/fetch_promoted_properties_cubit.dart'; +import '../../../data/model/property_model.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/Erros/no_data_found.dart'; +import '../widgets/Erros/something_went_wrong.dart'; +import 'Widgets/property_horizontal_card.dart'; + +class PromotedPropertiesScreen extends StatefulWidget { + const PromotedPropertiesScreen({super.key}); + + static Route route(RouteSettings routeSettings) { + return BlurredRouter( + builder: (context) { + return const PromotedPropertiesScreen(); + }, + ); + } + + @override + State createState() => + _PromotedPropertiesScreenState(); +} + +class _PromotedPropertiesScreenState extends State { + ///This Scroll controller for listen page end + final ScrollController _pageScrollController = ScrollController(); + @override + void initState() { + _pageScrollController.addListener(onPageEnd); + super.initState(); + } + + ///This method will listen page scroll changes + void onPageEnd() { + // / / /This is extensions which will check if we reached end or not + if (_pageScrollController.isEndReached()) { + if (context.read().hasMoreData()) { + context.read().fetchMore(); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.primaryColor, + appBar: AppBar( + backgroundColor: context.color.secondaryColor, + elevation: 0, + iconTheme: IconThemeData(color: context.color.tertiaryColor), + title: Text( + UiUtils.translate(context, "promotedProperties"), + ).color(context.color.tertiaryColor).size(context.font.large), + ), + body: BlocBuilder( + builder: (context, state) { + if (state is FetchPromotedPropertiesInProgress) { + return Center( + child: UiUtils.progress( + normalProgressColor: context.color.tertiaryColor, + ), + ); + } + if (state is FetchPromotedPropertiesFailure) { + return const SomethingWentWrong(); + } + if (state is FetchPromotedPropertiesSuccess) { + if (state.properties.isEmpty) { + return Center( + child: NoDataFound( + onTap: () { + context.read().fetch(); + }, + ), + ); + } + return Column( + children: [ + Expanded( + child: ScrollConfiguration( + behavior: RemoveGlow(), + child: ListView.builder( + controller: _pageScrollController, + padding: const EdgeInsets.all(20), + itemCount: state.properties.length, + itemBuilder: (context, index) { + PropertyModel property = state.properties[index]; + return GestureDetector( + onTap: () { + HelperUtils.goToNextPage( + Routes.propertyDetails, + context, + false, + args: { + 'propertyData': property, + 'propertiesList': state.properties, + 'fromMyProperty': false, + }, + ); + }, + child: PropertyHorizontalCard( + property: property, + ), + ); + }, + ), + ), + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ); + } + return Container(); + }, + ), + ); + } +} diff --git a/lib/Ui/screens/main_activity.dart b/lib/Ui/screens/main_activity.dart new file mode 100644 index 0000000..a673ff9 --- /dev/null +++ b/lib/Ui/screens/main_activity.dart @@ -0,0 +1,867 @@ +// ignore_for_file: invalid_use_of_protected_member + +import 'dart:convert'; +import 'dart:io'; + +import 'package:ebroker/Ui/screens/proprties/AddProperyScreens/select_type_of_property.dart'; +import 'package:ebroker/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import 'package:ebroker/data/cubits/outdoorfacility/fetch_outdoor_facility_list.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:rive/components.dart'; +import 'package:rive/rive.dart'; +import 'package:rive/src/rive_core/component.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../../app/analytics_routes.dart'; +import '../../app/app.dart'; +import '../../app/routes.dart'; +import '../../data/cubits/company_cubit.dart'; +import '../../data/cubits/property/search_property_cubit.dart'; +import '../../data/cubits/system/fetch_system_settings_cubit.dart'; +import '../../data/model/property_model.dart'; +import '../../data/model/system_settings_model.dart'; +import '../../settings.dart'; +import '../../utils/AppIcon.dart'; +import '../../utils/Extensions/extensions.dart'; +import '../../utils/api.dart'; +import '../../utils/constant.dart'; +import '../../utils/errorFilter.dart'; +import '../../utils/guestChecker.dart'; +import '../../utils/helper_utils.dart'; +import '../../utils/hive_utils.dart'; +import '../../utils/responsiveSize.dart'; +import '../../utils/ui_utils.dart'; +import 'chat/chat_list_screen.dart'; +import 'home/home_screen.dart'; +import 'home/search_screen.dart'; +import 'proprties/my_properties_screen.dart'; +import 'userprofile/profile_screen.dart'; +import 'widgets/blurred_dialoge_box.dart'; + +List myPropertylist = []; +Map searchbody = {}; +String selectedcategoryId = "0"; +String selectedcategoryName = ""; +dynamic selectedCategory; + +//this will set when i will visit in any category +dynamic currentVisitingCategoryId = ""; +dynamic currentVisitingCategory = ""; + +List navigationStack = [0]; + +ScrollController homeScreenController = ScrollController(); +ScrollController chatScreenController = ScrollController(); +ScrollController sellScreenController = ScrollController(); +ScrollController rentScreenController = ScrollController(); +ScrollController profileScreenController = ScrollController(); + +List controllerList = [ + homeScreenController, + chatScreenController, + if (propertyScreenCurrentPage == 0) ...[ + sellScreenController + ] else ...[ + rentScreenController + ], + profileScreenController +]; + +// +class MainActivity extends StatefulWidget { + final String from; + const MainActivity({Key? key, required this.from}) : super(key: key); + + @override + State createState() => MainActivityState(); + + static Route route(RouteSettings routeSettings) { + Map arguments = routeSettings.arguments as Map; + return BlurredRouter( + builder: (_) => MainActivity(from: arguments['from'] as String)); + } +} + +class MainActivityState extends State + with TickerProviderStateMixin { + int currtab = 0; + static final FirebaseMessaging firebaseMessaging = FirebaseMessaging.instance; + final List _pageHistory = []; + late PageController pageController; + DateTime? currentBackPressTime; + //This is rive file artboards and setting you can check rive package's documentation at [pub.dev] + Artboard? artboard; + SMIBool? isReverse; + StateMachineController? _controller; + bool isAddMenuOpen = false; + int rotateAnimationDurationMs = 2000; + bool showSellRentButton = false; + + ///Animation for sell and rent button + late final AnimationController _forSellAnimationController = + AnimationController( + vsync: this, + duration: const Duration( + milliseconds: 400, + ), + reverseDuration: const Duration( + milliseconds: 400, + ), + ); + late final AnimationController _forRentController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 300), + reverseDuration: const Duration(milliseconds: 300), + ); + + ///END: Animation for sell and rent button + late final Animation _sellTween = Tween(begin: -50, end: 80) + .animate(CurvedAnimation( + parent: _forSellAnimationController, curve: Curves.easeIn)); + late final Animation _rentTween = Tween(begin: -50, end: 30) + .animate( + CurvedAnimation(parent: _forRentController, curve: Curves.easeIn)); + + Map riveConfig = AppSettings.riveAnimationConfigurations; + late var addButtonConfig = riveConfig['add_button']; + late var artboardName = addButtonConfig['artboard_name']; + late var stateMachine = addButtonConfig['state_machine']; + late var booleanName = addButtonConfig['boolean_name']; + late var booleanInitialValue = addButtonConfig['boolean_initial_value']; + late var addButtonShapeName = addButtonConfig['add_button_shape_name']; + + bool isChecked = false; + + @override + void initState() { + super.initState(); + if (appSettings.isUserActive == false) { + Future.delayed( + Duration.zero, + () { + HiveUtils.logoutUser(context, onLogout: () {}); + }, + ); + } + + GuestChecker.setContext(context); + GuestChecker.set(isGuest: HiveUtils.isGuest()); + FetchSystemSettingsCubit settings = + context.read(); + if (!const bool.fromEnvironment("force-disable-demo-mode", + defaultValue: false)) { + Constant.isDemoModeOn = + settings.getSetting(SystemSetting.demoMode) ?? false; + } + var numberWithSuffix = settings.getSetting(SystemSetting.numberWithSuffix); + Constant.isNumberWithSuffix = numberWithSuffix == "1" ? true : false; + + if (Constant.isDemoModeOn) { + HiveUtils.setLocation( + city: "Bhuj", + state: "Gujrat", + country: "India", + longitude: 69.666931, + latitude: 23.242001, + placeId: "ChIJF28LAAniUDkRpnQHr1jzd3A", + ); + } + + ///this will check if your profile is complete or not if it is incomplete it will redirect you to the edit profile page + // completeProfileCheck(); + + ///This will check for update + versionCheck(settings); + + ///This will check if location is set or not , If it is not set it will show popup dialoge so you can set for better result + if (GuestChecker.value == false) { + locationSetCheck(); + } + +//Initializing rive animations + initRiveAddButtonAnimation(); + +//This will init page controller + initPageController(); + context.read().fetch(); + context.read().fetchCompany( + context, + ); //getCompanyData @ Start [specially for contact number] + } + + void addHistory(int index) { + List stack = navigationStack; + // if (stack.length > 5) { + // stack.removeAt(0); + // } else { + if (stack.last != index) { + stack.add(index); + navigationStack = stack; + } + + setState(() {}); + } + + void initPageController() { + pageController = PageController(initialPage: 0) + ..addListener(() { + _pageHistory.insert(0, pageController.page); + }); + } + + void initRiveAddButtonAnimation() { + ///Open file + rootBundle + .load("assets/riveAnimations/${Constant.riveAnimation}") + .then((value) { + ///Import that data to this method below + RiveFile riveFile = RiveFile.import(value); + + ///Artboard by name you can check https://rive.app and learn it for more information + /// Here Add is artboard name from that workspace + artboard = riveFile.artboardByName(artboardName); + artboard?.forEachComponent((child) { + if (child.name == "plus") { + for (Component element in (child as Node).children) { + if (element.name == "Path_49") { + if (element is Shape) { + final Shape shape = element; + + shape.fills.first.paint.color = Colors.white; + } + } + } + } + if (child is Shape && child.name == addButtonShapeName) { + final Shape shape = child; + shape.fills.first.paint.color = context.color.tertiaryColor; + } + }); + + ///in rive there is state machine to control states of animation, like. walking,running, and more + ///click is state machine name + _controller = + StateMachineController.fromArtboard(artboard!, stateMachine); + // _controller. + if (_controller != null) { + artboard?.addController(_controller!); + + //this SMI means State machine input, we can create conditions in rive , so isReverse is boolean value name from there + isReverse = _controller?.findSMI(booleanName); + + ///this is optional it depends on your conditions you can change this whole conditions and values, + ///for this animation isReverse =true means it will play its idle animation + isReverse?.value = booleanInitialValue; + + ///here we can change color of any shape, here 'shape' is name in rive.app file + } + setState(() {}); + }); + } + + void completeProfileCheck() { + if (HiveUtils.getUserDetails().name == "" || + HiveUtils.getUserDetails().email == "") { + Future.delayed( + const Duration(milliseconds: 100), + () { + Navigator.pushReplacementNamed(context, Routes.completeProfile, + arguments: {"from": "login"}); + }, + ); + } + } + + void versionCheck(settings) async { + var remoteVersion = settings.getSetting(Platform.isIOS + ? SystemSetting.iosVersion + : SystemSetting.androidVersion); + var remote = remoteVersion; + + var forceUpdate = settings.getSetting(SystemSetting.forceUpdate); + + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + + var current = packageInfo.version; + + int currentVersion = HelperUtils.comparableVersion(packageInfo.version); + if (remoteVersion == null) { + return; + } + remoteVersion = HelperUtils.comparableVersion( + remoteVersion, + ); + + if (remoteVersion > currentVersion) { + Constant.isUpdateAvailable = true; + Constant.newVersionNumber = settings.getSetting( + Platform.isIOS + ? SystemSetting.iosVersion + : SystemSetting.androidVersion, + ); + + Future.delayed( + Duration.zero, + () { + if (forceUpdate == "1") { + ///This is force update + UiUtils.showBlurredDialoge(context, + dialoge: BlurredDialogBox( + onAccept: () async { + if (Platform.isAndroid) { + await launchUrl( + Uri.parse( + Constant.playstoreURLAndroid, + ), + mode: LaunchMode.externalApplication); + } else { + await launchUrl( + Uri.parse( + Constant.appstoreURLios, + ), + mode: LaunchMode.externalApplication); + } + }, + backAllowedButton: false, + svgImagePath: AppIcons.update, + isAcceptContainesPush: true, + svgImageColor: context.color.tertiaryColor, + showCancleButton: false, + title: "updateAvailable".translate(context), + acceptTextColor: context.color.buttonColor, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text("$current>$remote"), + Text("newVersionAvailableForce".translate(context), + textAlign: TextAlign.center), + ], + ))); + } else { + UiUtils.showBlurredDialoge( + context, + dialoge: BlurredDialogBox( + onAccept: () async { + if (Platform.isAndroid) { + await launchUrl( + Uri.parse( + Constant.playstoreURLAndroid, + ), + mode: LaunchMode.externalApplication); + } else { + await launchUrl( + Uri.parse( + Constant.appstoreURLios, + ), + mode: LaunchMode.externalApplication); + } + }, + svgImagePath: AppIcons.update, + svgImageColor: context.color.tertiaryColor, + showCancleButton: true, + title: "updateAvailable".translate(context), + content: Text( + "newVersionAvailable".translate(context), + ), + ), + ); + } + }, + ); + } + } + + void locationSetCheck() { + if (HiveUtils.isShowChooseLocationDialoge() && + !HiveUtils.isLocationFilled()) { + Future.delayed( + Duration.zero, + () { + UiUtils.showBlurredDialoge( + context, + dialoge: BlurredDialogBox( + title: UiUtils.translate(context, "setLocation"), + content: StatefulBuilder(builder: (context, update) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + UiUtils.translate( + context, + "setLocationforBetter", + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Checkbox( + fillColor: MaterialStateProperty.resolveWith( + (Set states) { + if (states.contains(MaterialState.selected)) { + return context.color.tertiaryColor; + } else { + return context.color.primaryColor; + } + }, + // context.color.primaryColor, + ), + value: isChecked, + onChanged: (value) { + isChecked = value ?? false; + update(() {}); + }, + ), + const SizedBox( + width: 5, + ), + Text(UiUtils.translate(context, "dontshowagain")) + ], + ), + ], + ); + }), + isAcceptContainesPush: true, + onCancel: () { + if (isChecked == true) { + HiveUtils.dontShowChooseLocationDialoge(); + } + }, + onAccept: () async { + if (isChecked == true) { + HiveUtils.dontShowChooseLocationDialoge(); + } + Navigator.pop(context); + + Navigator.pushNamed(context, Routes.completeProfile, + arguments: { + "from": "chooseLocation", + "navigateToHome": true + }); + }, + ), + ); + }, + ); + } + } + + @override + void didChangeDependencies() { + ErrorFilter.setContext(context); + + artboard?.forEachComponent((child) { + if (child.name == "plus") { + for (Component element in (child as Node).children) { + if (element.name == "Path_49") { + if (element is Shape) { + final Shape shape = element; + + shape.fills.first.paint.color = Colors.white; + } + } + } + } + if (child is Shape && child.name == addButtonShapeName) { + final Shape shape = child; + + shape.fills.first.paint.color = context.color.tertiaryColor; + } + }); + super.didChangeDependencies(); + } + + @override + void dispose() { + pageController.dispose(); + super.dispose(); + } + + Future checkForMaintenanceMode() async { + Map body = { + Api.type: Api.maintenanceMode, + }; + + var response = await HelperUtils.sendApiRequest( + Api.apiGetSystemSettings, + body, + true, + context, + ); + var getdata = json.decode(response); + print("Setiing : $getdata"); + if (getdata != null) { + if (!getdata[Api.error]) { + Constant.maintenanceMode = getdata['data'].toString(); + if (Constant.maintenanceMode == "1") { + setState(() {}); + } + } + } + } + + late List pages = [ + HomeScreen(from: widget.from), + const ChatListScreen(), + const Text(""), + const PropertiesScreen(), + const ProfileScreen(), + ]; + + @override + Widget build(BuildContext context) { + return AnnotatedRegion( + value: UiUtils.getSystemUiOverlayStyle(context: context), + child: WillPopScope( + onWillPop: () async { + ///Navigation history + int length = navigationStack.length; + if (length == 1 && navigationStack[0] == 0) { + DateTime now = DateTime.now(); + if (currentBackPressTime == null || + now.difference(currentBackPressTime!) > + const Duration(seconds: 2)) { + currentBackPressTime = now; + Fluttertoast.showToast( + msg: "pressAgainToExit".translate(context), + ); + return Future.value(false); + } + } else { + //This will put our page on previous page. + int secondLast = navigationStack[length - 2]; + navigationStack.removeLast(); + pageController.jumpToPage(secondLast); + setState(() {}); + return false; + } + + return Future.value(true); + }, + child: Scaffold( + backgroundColor: context.color.primaryColor, + bottomNavigationBar: + Constant.maintenanceMode == "1" ? null : bottomBar(), + body: Stack( + children: [ + PageView( + physics: const NeverScrollableScrollPhysics(), + controller: pageController, + onPageChanged: onItemSwipe, + children: pages, + ), + if (Constant.maintenanceMode == "1") + Container( + color: Theme.of(context).colorScheme.primaryColor, + ), + SizedBox( + width: double.infinity, + height: context.screenHeight, + child: Stack( + children: [ + AnimatedBuilder( + animation: _forRentController, + builder: (context, c) { + return Positioned( + bottom: _rentTween.value, + left: (context.screenWidth / 2) - (181 / 2), + child: GestureDetector( + onTap: () { + GuestChecker.check(onNotGuest: () { + Navigator.pushNamed( + context, Routes.selectPropertyTypeScreen, + arguments: { + "type": PropertyAddType.property + }); + }); + }, + child: Container( + width: 181, + height: 44, + decoration: BoxDecoration( + border: Border.all( + color: context.color.borderColor, + width: 1.5, + ), + boxShadow: [ + BoxShadow( + color: context.color.tertiaryColor + .withOpacity(0.4), + offset: const Offset(0, 3), + blurRadius: 10, + spreadRadius: 0, + ) + ], + color: context.color.tertiaryColor, + borderRadius: BorderRadius.circular(22)), + alignment: Alignment.center, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + UiUtils.getSvg(AppIcons.propertiesIcon, + color: context.color.buttonColor, + width: 16, + height: 16), + SizedBox( + width: 7.rw(context), + ), + const Text("Properti") + .color(context.color.buttonColor), + ], + )), + ), + ); + }), + AnimatedBuilder( + animation: _forSellAnimationController, + builder: (context, c) { + return Positioned( + bottom: _sellTween.value, + left: (context.screenWidth / 2) - 128 / 2, + child: GestureDetector( + onTap: () { + GuestChecker.check(onNotGuest: () { + // Constant.addProperty.addAll( + // { + // "propertyType": PropertyType.sell, + // }, + // ); + + Navigator.pushNamed( + context, Routes.selectPropertyTypeScreen, + arguments: { + "type": PropertyAddType.project + }); + + }); + }, + child: Container( + width: 128, + height: 44, + decoration: BoxDecoration( + border: Border.all( + color: context.color.borderColor, + width: 1.5, + ), + boxShadow: [ + BoxShadow( + color: context.color.tertiaryColor + .withOpacity(0.4), + offset: const Offset(0, 3), + blurRadius: 10, + spreadRadius: 0, + ) + ], + color: context.color.tertiaryColor, + borderRadius: BorderRadius.circular(22)), + alignment: Alignment.center, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + UiUtils.getSvg(AppIcons.upcomingProject, + color: context.color.buttonColor, + width: 16, + height: 16), + SizedBox( + width: 7.rw(context), + ), + const Text( "Project ") + .color(context.color.buttonColor), + ], + ), + ), + ), + ); + }), + ], + ), + ) + + // + ], + ), + ), + ), + ); + } + + void onItemTapped(int index) { + addHistory(index); + + if (index == currtab) { + var xIndex = index; + + if (xIndex == 3) { + xIndex = 2; + } else if (xIndex == 4) { + xIndex = 3; + } + if (controllerList[xIndex].hasClients) { + controllerList[xIndex].animateTo(0, + duration: const Duration(milliseconds: 200), + curve: Curves.bounceOut); + } + } + FocusManager.instance.primaryFocus?.unfocus(); + isReverse?.value = true; + _forSellAnimationController.reverse(); + _forRentController.reverse(); + + if (index != 1) { + context.read().clearSearch(); + + if (SearchScreenState.searchController.hasListeners) { + SearchScreenState.searchController.text = ""; + } + } + searchbody = {}; + if (index == 1 || index == 3) { + GuestChecker.check( + onNotGuest: () { + currtab = index; + pageController.jumpToPage(currtab); + setState( + () {}, + ); + }, + ); + } else { + currtab = index; + pageController.jumpToPage(currtab); + + setState(() {}); + } + } + + void onItemSwipe(int index) { + addHistory(index); + + if (index == 0) { + FirebaseAnalytics.instance + .setCurrentScreen(screenName: AnalyticsRoutes.home); + } else if (index == 1) { + FirebaseAnalytics.instance + .setCurrentScreen(screenName: AnalyticsRoutes.chatList); + } else if (index == 3) { + FirebaseAnalytics.instance + .setCurrentScreen(screenName: AnalyticsRoutes.properties); + } else if (index == 4) {} + FocusManager.instance.primaryFocus?.unfocus(); + isReverse?.value = true; + _forSellAnimationController.reverse(); + _forRentController.reverse(); + + if (index != 1) { + context.read().clearSearch(); + + if (SearchScreenState.searchController.hasListeners) { + SearchScreenState.searchController.text = ""; + } + } + searchbody = {}; + setState(() { + currtab = index; + }); + pageController.jumpToPage(currtab); + } + + BottomAppBar bottomBar() { + return BottomAppBar( + // notchMargin: 10.0, + + color: context.color.primaryColor, + shape: const CircularNotchedRectangle(), + child: Container( + color: context.color.primaryColor, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + buildBottomNavigationbarItem( + 0, AppIcons.home, UiUtils.translate(context, "homeTab")), + buildBottomNavigationbarItem( + 1, AppIcons.chat, UiUtils.translate(context, "chat")), + // const SizedBox( + // width: 100, + // height: 100, + // child: RiveAnimation.asset( + // "", + // artboard: "Add", + // ), + // ), + + Transform( + transform: Matrix4.identity()..translate(0.toDouble(), -20), + child: GestureDetector( + onTap: () async { + if (isReverse?.value == true) { + isReverse?.value = false; + showSellRentButton = true; + _forRentController.forward(); + _forSellAnimationController.forward(); + } else { + showSellRentButton = false; + isReverse?.value = true; + _forRentController.reverse(); + _forSellAnimationController.reverse(); + } + // setState(() {}); + }, + child: SizedBox( + width: 60.rw(context), + height: 66, + child: artboard == null + ? Container() + : Rive(artboard: artboard!)), + ), + ), + + buildBottomNavigationbarItem(3, AppIcons.properties, + UiUtils.translate(context, "properties")), + buildBottomNavigationbarItem( + 4, AppIcons.profile, UiUtils.translate(context, "profileTab")) + ]), + ), + ); + } + + Widget buildBottomNavigationbarItem( + int index, + String svgImage, + String title, + ) { + return Expanded( + child: Material( + type: MaterialType.transparency, + child: InkWell( + highlightColor: Colors.transparent, + splashColor: Colors.transparent, + onTap: () => onItemTapped(index), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (currtab == index) ...{ + UiUtils.getSvg(svgImage, color: context.color.tertiaryColor), + } else ...{ + UiUtils.getSvg(svgImage, color: context.color.textLightColor), + }, + Text( + title, + textAlign: TextAlign.center, + ).color(currtab == index + ? context.color.tertiaryColor + : context.color.textLightColor), + ], + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/map/choose_location_map.dart b/lib/Ui/screens/map/choose_location_map.dart new file mode 100644 index 0000000..870cde7 --- /dev/null +++ b/lib/Ui/screens/map/choose_location_map.dart @@ -0,0 +1,598 @@ +import 'dart:async'; + +import 'package:dio/dio.dart'; +import 'package:ebroker/data/Repositories/property_repository.dart'; +import 'package:ebroker/data/helper/widgets.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/helper_utils.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:geocoding/geocoding.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +import '../../../data/Repositories/location_repository.dart'; +import '../../../data/model/google_place_model.dart'; +import '../../../utils/AppIcon.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; + +class ChooseLocationMap extends StatefulWidget { + final num? latitude; + final num? longitude; + const ChooseLocationMap({super.key, this.latitude, this.longitude}); + static Route route(RouteSettings settings) { + Map? arguments = settings.arguments as Map?; + return BlurredRouter( + builder: (context) { + return ChooseLocationMap( + latitude: arguments?['latitude'], + longitude: arguments?['longitude'], + ); + }, + ); + } + + @override + State createState() => _ChooseLocationMapState(); +} + +class _ChooseLocationMapState extends State { + final TextEditingController _searchController = TextEditingController(); + String previouseSearchQuery = ""; + LatLng? citylatLong; + Timer? _timer; + Marker? marker; + Map map = {}; + GoogleMapController? _googleMapController; + Completer completer = Completer(); + final FocusNode _searchFocus = FocusNode(); + List? cities; + int selectedMarker = 999999999999999; + int? propertyId; + ValueNotifier isLoadingProperty = ValueNotifier(false); + PropertyModel? activePropertyModal; + ValueNotifier loadintCitiesInProgress = ValueNotifier(false); + bool showSellRentLables = false; + bool showGoogleMap = false; + Future searchDelayTimer() async { + if (_timer?.isActive ?? false) { + _timer?.cancel(); + } + print("open map"); + _timer = Timer( + const Duration(milliseconds: 500), + () async { + if (_searchController.text.isNotEmpty) { + if (previouseSearchQuery != _searchController.text) { + try { + loadintCitiesInProgress.value = true; + cities = await GooglePlaceRepository().serchCities( + _searchController.text, + ); + loadintCitiesInProgress.value = false; + } catch (e) { + loadintCitiesInProgress.value = false; + } + + setState(() {}); + previouseSearchQuery = _searchController.text; + } + } else { + cities = null; + } + }, + ); + setState(() {}); + } + + late var assigned = LatLng( + widget.latitude?.toDouble() ?? 42.42345651793833, + widget.longitude?.toDouble() ?? 23.906250000000004, + ); + late LatLng cameraPosition = assigned; + @override + void initState() { + _searchController.addListener(() { + searchDelayTimer(); + }); + if (widget.latitude != null && widget.longitude != null) { + marker = Marker(markerId: MarkerId("9999999"), position: assigned); + setState(() {}); + } + Future.delayed( + const Duration(milliseconds: 500), + () { + showGoogleMap = true; + setState(() {}); + }, + ); + + super.initState(); + } + + Future onTapCity(int index) async { + Widgets.showLoader(context); + // List pointList = + // await GMap.getNearByProperty(cities?.elementAt(0).city ?? ""); + + // if (pointList.isEmpty) { + // marker = {}; + // setState(() {}); + // } + + LatLng? latLng = await getCityLatLong(index); + //Animate camera to location + (await completer.future).animateCamera( + CameraUpdate.newCameraPosition( + CameraPosition(target: latLng!, zoom: 7), + ), + ); + // loopMarker(pointList); + + marker = (Marker( + markerId: MarkerId( + index.toString(), + ), + position: latLng)); + + _searchFocus.unfocus(); + HelperUtils.unfocus(); + Future.delayed( + Duration.zero, + () { + Widgets.hideLoder(context); + }, + ); + + cities = null; + setState(() {}); + } + + // loopMarker(List pointList) { + // for (var i = 0; i < pointList.length; i++) { + // var element = pointList[i]; + // //Add markers inside marker list + // marker + // .addLabelMarker(LabelMarker( + // label: r"$" + (element.price).toString().priceFormate(), + // markerId: MarkerId("$i"), + // onTap: () async { + // selectedMarker = i; + // propertyId = element.propertyId; + // marker.clear(); + // loopMarker(pointList); + // setState(() {}); + // fetchProperty(element.propertyId); + // }, + // position: LatLng( + // double.parse(element.latitude), double.parse(element.longitude)), + // backgroundColor: selectedMarker == i + // ? Colors.red + // : (element.propertyType.toLowerCase() == "sell" + // ? Colors.green + // : Colors.orange), + // )) + // .then( + // (value) { + // setState(() {}); + // }, + // ); + // } + // } + + Future fetchProperty(int id) async { + try { + isLoadingProperty.value = true; + DataOutput result = + await PropertyRepository().fetchPropertyFromPropertyId(id); + activePropertyModal = result.modelList.first; + setState(() {}); + isLoadingProperty.value = false; + } catch (e) { + isLoadingProperty.value = false; + + HelperUtils.showSnackBarMessage(context, "error".translate(context)); + } + } + + Future? getCityLatLong(index) async { + var rawCityLatLong = await GooglePlaceRepository() + .getPlaceDetailsFromPlaceId(cities?.elementAt(index).placeId ?? ""); + + var citylatLong = LatLng(rawCityLatLong['lat'], rawCityLatLong['lng']); + return citylatLong; + } + + @override + void dispose() async { + _googleMapController?.dispose(); + _searchController.dispose(); + super.dispose(); + } + + // Future _delayedPop(BuildContext context) async { + // unawaited( + // Navigator.of(context, rootNavigator: true).push( + // PageRouteBuilder( + // pageBuilder: (_, __, ___) => WillPopScope( + // onWillPop: () async => false, + // child: const Scaffold( + // backgroundColor: Colors.transparent, + // body: Center( + // child: CircularProgressIndicator.adaptive(), + // ), + // ), + // ), + // transitionDuration: Duration.zero, + // barrierDismissible: false, + // barrierColor: Colors.black45, + // opaque: false, + // ), + // ), + // ); + // await Future.delayed(const Duration(seconds: 1)); + + // Future.delayed( + // Duration.zero, + // () { + // Navigator.of(context) + // ..pop() + // ..pop(); + // }, + // ); + // } + + String? getComponent(List data, dynamic dm) { + // log("CALLED"); + try { + return data.where((element) { + return (element['types'] as List).contains(dm); + }).first['long_name']; + } catch (e) { + return null; + } + } + + @override + Widget build(BuildContext context) { + Widget buildSearchIcon() { + return Padding( + padding: const EdgeInsets.all(8.0), + child: UiUtils.getSvg(AppIcons.search, + color: context.color.tertiaryColor)); + } + + return WillPopScope( + onWillPop: () async { + _googleMapController?.dispose(); + (await completer.future).dispose(); + showGoogleMap = false; + setState(() {}); + + return true; + }, + child: SafeArea( + child: Scaffold( + bottomNavigationBar: SizedBox( + child: MaterialButton( + height: 50, + color: context.color.tertiaryColor, + onPressed: marker == null + ? null + : () async { + try { + String? state = ""; + String? city = ""; + String? country = ""; + String? sublocality = ""; + String? pointofinterest = ""; + Response response = await Dio().get( + "https://maps.googleapis.com/maps/api/geocode/json?key=${Constant.googlePlaceAPIkey}&latlng=${marker?.position.latitude},${marker?.position.longitude}"); + + if ((response.data as Map) + .containsKey("error_message")) { + throw response.data; + } + List component = List.from( + response.data['results'][0]['address_components']); + + city = getComponent( + component, + "locality", + ); + state = getComponent( + component, "administrative_area_level_1"); + country = getComponent(component, "country"); + sublocality = getComponent(component, "sublocality"); + + pointofinterest = + getComponent(component, "point_of_interest"); + + bool? startsWith = pointofinterest?.contains(","); + if (startsWith ?? false) { + pointofinterest = + pointofinterest?.replaceFirst(",", ""); + } + + Placemark place = Placemark( + locality: city, + administrativeArea: state, + country: country, + subLocality: sublocality, + street: pointofinterest); + + showGoogleMap = false; + setState(() {}); + + Future.delayed( + const Duration(milliseconds: 0), + () { + Navigator.pop(context, { + "latlng": LatLng(marker!.position.latitude, + marker!.position.longitude), + "place": place + }); + }, + ); + } catch (e) { + if (e is Map) { + if (e.containsKey("error_message")) { + HelperUtils.showSnackBarMessage( + context, e['error_message'], + messageDuration: 5); + } + } + + if (e.toString().contains("IO_ERROR")) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text("pleaseChangeNetwork" + .translate(context) + .toString()))); + } + } + }, + child: Text("proceed".translate(context)).color(marker == null + ? context.color.textColorDark + : context.color.buttonColor), + ), + ), + backgroundColor: context.color.backgroundColor, + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + automaticallyImplyLeading: false, + centerTitle: true, + titleSpacing: 0, + actions: [ + FittedBox( + fit: BoxFit.none, + child: SizedBox( + width: 24, + height: 24, + child: ValueListenableBuilder( + valueListenable: loadintCitiesInProgress, + builder: (context, va, c) { + if (va == false) { + return const SizedBox.shrink(); + } + return CircularProgressIndicator( + color: context.color.tertiaryColor, + strokeWidth: 1.5, + ); + }), + )) + ], + leading: cities != null + ? IconButton( + onPressed: () { + cities = null; + _searchController.text = ""; + setState(() {}); + }, + icon: Icon( + Icons.close, + color: context.color.tertiaryColor, + )) + : Material( + clipBehavior: Clip.antiAlias, + color: Colors.transparent, + type: MaterialType.circle, + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Padding( + padding: const EdgeInsets.all(18.0), + child: UiUtils.getSvg(AppIcons.arrowLeft, + fit: BoxFit.none, + color: context.color.tertiaryColor), + ), + ), + ), + title: Container( + width: 270.rw(context), + height: 50.rh(context), + alignment: Alignment.center, + decoration: BoxDecoration( + border: Border.all( + width: 1.5, color: context.color.borderColor), + borderRadius: const BorderRadius.all(Radius.circular(10)), + color: context.color.secondaryColor), + child: TextFormField( + focusNode: _searchFocus, + controller: _searchController, + decoration: InputDecoration( + border: InputBorder.none, //OutlineInputBorder() + fillColor: Theme.of(context).colorScheme.secondaryColor, + hintText: UiUtils.translate(context, "searhCity"), + prefixIcon: buildSearchIcon(), + prefixIconConstraints: + const BoxConstraints(minHeight: 5, minWidth: 5), + ), + enableSuggestions: true, + onEditingComplete: () { + FocusScope.of(context).unfocus(); + }, + onTap: () { + //change prefix icon color to primary + })), + ), + body: Stack( + children: [ + SizedBox( + height: context.screenHeight, + width: context.screenWidth, + child: showGoogleMap == true + ? GoogleMap( + markers: marker == null ? {} : {marker!}, + onMapCreated: (controller) { + completer.complete(controller); + showSellRentLables = true; + setState(() {}); + }, + onTap: (argument) { + activePropertyModal = null; + selectedMarker = 99999999999999; + + marker = Marker( + markerId: const MarkerId("0"), + position: LatLng( + argument.latitude, argument.longitude)); + setState(() {}); + }, + mapType: AppSettings.googleMapType, + compassEnabled: false, + scrollGesturesEnabled: true, + mapToolbarEnabled: false, + trafficEnabled: true, + myLocationButtonEnabled: true, + zoomControlsEnabled: false, + myLocationEnabled: true, + initialCameraPosition: + CameraPosition(target: cameraPosition, zoom: 7), + key: const Key("G-map"), + ) + : const SizedBox.shrink(), + ), + if (cities != null) + Container( + color: context.color.backgroundColor, + child: ListView.builder( + itemCount: cities?.length ?? 0, + itemBuilder: (context, index) { + return ListTile( + onTap: () async { + activePropertyModal = null; + setState(() {}); + onTapCity(index); + }, + leading: SvgPicture.asset( + AppIcons.location, + color: context.color.textColorDark, + ), + title: Text(cities?.elementAt(index).city ?? ""), + subtitle: Text( + "${cities?.elementAt(index).state ?? ""},${cities?.elementAt(index).country ?? ""}"), + ); + }, + ), + ), + // PositionedDirectional( + // bottom: 0, + // child: ValueListenableBuilder( + // valueListenable: isLoadingProperty, + // builder: (context, val, child) { + // if (cities != null) { + // return const SizedBox.shrink(); + // } + // if (val == true) { + // return SizedBox( + // width: MediaQuery.of(context).size.width, + // child: Padding( + // padding: const EdgeInsets.all(20.0), + // child: Row( + // children: const [ + // CustomShimmer( + // width: 100, + // height: 110, + // ), + // SizedBox( + // width: 5, + // ), + // Expanded( + // child: CustomShimmer( + // height: 110, + // ), + // ), + // ], + // ), + // ), + // ); + // } else { + // if (activePropertyModal != null) { + // return SizedBox( + // width: MediaQuery.of(context).size.width, + // child: Padding( + // padding: const EdgeInsets.all(20), + // child: GestureDetector( + // onTap: () { + // Navigator.pushNamed( + // context, Routes.propertyDetails, + // arguments: { + // 'propertyData': activePropertyModal, + // 'fromMyProperty': true, + // }); + // }, + // child: PropertyHorizontalCard( + // showLikeButton: false, + // property: activePropertyModal!), + // ), + // ), + // ); + // } else { + // return Container(); + // } + // } + // })) + ], + ), + ), + ), + ); + } + + Padding sellRentLable(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Container( + width: 20, + height: 20, + color: Colors.green, + ), + const SizedBox( + width: 3, + ), + const Text("Sell").color(context.color.buttonColor), + const SizedBox( + width: 10, + ), + Container( + width: 20, + height: 20, + color: Colors.orange, + ), + const SizedBox( + width: 3, + ), + const Text("Rent").color(context.color.buttonColor), + ], + ), + ); + } +} diff --git a/lib/Ui/screens/map/property_map_screen.dart b/lib/Ui/screens/map/property_map_screen.dart new file mode 100644 index 0000000..fd87ad0 --- /dev/null +++ b/lib/Ui/screens/map/property_map_screen.dart @@ -0,0 +1,508 @@ +import 'dart:async'; + +import 'package:ebroker/Ui/screens/home/Widgets/property_horizontal_card.dart'; +import 'package:ebroker/Ui/screens/widgets/shimmerLoadingContainer.dart'; +import 'package:ebroker/data/Repositories/map.dart'; +import 'package:ebroker/data/Repositories/property_repository.dart'; +import 'package:ebroker/data/helper/widgets.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/constant.dart'; +import 'package:ebroker/utils/helper_utils.dart'; +import 'package:ebroker/utils/hive_utils.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:ebroker/utils/string_extenstion.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:label_marker/label_marker.dart'; + +import '../../../app/routes.dart'; +import '../../../data/Repositories/location_repository.dart'; +import '../../../data/model/google_place_model.dart'; +import '../../../settings.dart'; +import '../../../utils/AppIcon.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; + +class PropertyMapScreen extends StatefulWidget { + const PropertyMapScreen({super.key}); + static Route route(RouteSettings settings) { + // Map? arguments = settings.arguments as Map?; + return BlurredRouter(builder: (context) { + return const PropertyMapScreen(); + }); + } + + @override + State createState() => _PropertyMapScreenState(); +} + +class _PropertyMapScreenState extends State { + final TextEditingController _searchController = TextEditingController(); + String previouseSearchQuery = ""; + LatLng? citylatLong; + Timer? _timer; + Set marker = {}; + Map map = {}; + GoogleMapController? _googleMapController; + Completer completer = Completer(); + final FocusNode _searchFocus = FocusNode(); + List? cities; + int selectedMarker = 999999999999999; + int? propertyId; + ValueNotifier isLoadingProperty = ValueNotifier(false); + PropertyModel? activePropertyModal; + ValueNotifier loadintCitiesInProgress = ValueNotifier(false); + bool showSellRentLables = false; + bool showGoogleMap = true; + + Future searchDelayTimer() async { + if (_timer?.isActive ?? false) { + _timer?.cancel(); + } + _timer = Timer( + const Duration(milliseconds: 500), + () async { + if (_searchController.text.isNotEmpty) { + if (previouseSearchQuery != _searchController.text) { + try { + loadintCitiesInProgress.value = true; + cities = await GooglePlaceRepository().serchCities( + _searchController.text, + ); + loadintCitiesInProgress.value = false; + } catch (e) { + loadintCitiesInProgress.value = false; + } + + setState(() {}); + previouseSearchQuery = _searchController.text; + } + } else { + cities = null; + } + }, + ); + setState(() {}); + } + + @override + void initState() { + loadDefaultCity(); + Fluttertoast.showToast( + msg: "Please search city", + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.TOP, + timeInSecForIosWeb: 1, + backgroundColor: Colors.red, + textColor: Colors.white, + fontSize: 16.0); + + _searchController.addListener(() { + searchDelayTimer(); + }); + super.initState(); + } + + LatLng cameraPosition = const LatLng( + 42.42345651793833, + 23.906250000000004, + ); + + Future loadDefaultCity() async { + if (HiveUtils.getCityName() == null) return; + List pointList = await GMap.getNearByProperty( + HiveUtils.getCityName() ?? "", HiveUtils.getStateName() ?? ""); + + if (pointList.isEmpty) { + marker = {}; + setState(() {}); + } + + LatLng? latLng = await getCityLatLong(HiveUtils.getCityPlaceId()); + //Animate camera to location + (await completer.future).animateCamera(CameraUpdate.newCameraPosition( + CameraPosition(target: latLng, zoom: 7))); + loopMarker(pointList); + } + + Future onTapCity(int index) async { + Widgets.showLoader(context); + List pointList = await GMap.getNearByProperty( + cities?.elementAt(index).city ?? "", + cities?.elementAt(index).state ?? ""); + + if (pointList.isEmpty) { + marker = {}; + setState(() {}); + } + + LatLng? latLng = await getCityLatLongByIndex(index); + //Animate camera to location + (await completer.future).animateCamera( + CameraUpdate.newCameraPosition( + CameraPosition(target: latLng!, zoom: 7), + ), + ); + loopMarker(pointList); + // for (var i = 0; i < pointList.length; i++) { + // var element = pointList[i]; + + // //Add markers inside marker list + // marker + // .addLabelMarker(LabelMarker( + // label: r"$" + (element.price).toString().priceFormate(), + // markerId: MarkerId("$i"), + // onTap: () async { + // selectedMarker = i; + // setState(() {}); + // }, + // position: LatLng( + // double.parse(element.latitude), double.parse(element.longitude)), + // backgroundColor: selectedMarker == i + // ? Colors.red + // : (element.propertyType == "sell" ? Colors.green : Colors.orange), + // )) + // .then( + // (value) { + // setState(() {}); + // }, + // ); + // } + + _searchFocus.unfocus(); + HelperUtils.unfocus(); + Future.delayed( + Duration.zero, + () { + Widgets.hideLoder(context); + }, + ); + + cities = null; + setState(() {}); + } + + void loopMarker(List pointList) { + for (var i = 0; i < pointList.length; i++) { + var element = pointList[i]; + print("element.propertyType ${element.propertyType}"); + marker + .addLabelMarker(LabelMarker( + label: + Constant.currencySymbol + (element.price).toString().priceFormate(), + markerId: MarkerId("$i"), + onTap: () async { + selectedMarker = i; + propertyId = element.propertyId; + marker.clear(); + loopMarker(pointList); + setState(() {}); + fetchProperty(element.propertyId); + }, + position: LatLng( + double.parse(element.latitude), double.parse(element.longitude)), + backgroundColor: selectedMarker == i + ? Colors.red + : (element.propertyType.toLowerCase() == "sell" + ? Colors.green + : Colors.orange), + )) + .then( + (value) { + setState(() {}); + }, + ); + } + } + + Future fetchProperty(int id) async { + try { + isLoadingProperty.value = true; + DataOutput result = + await PropertyRepository().fetchPropertyFromPropertyId(id); + + if (result.modelList.isNotEmpty) { + activePropertyModal = result.modelList.first; + } + setState(() {}); + isLoadingProperty.value = false; + } catch (e) { + isLoadingProperty.value = false; + + HelperUtils.showSnackBarMessage(context, "$e".translate(context)); + } + } + + Future? getCityLatLongByIndex(index) async { + // var rawCityLatLong = await GooglePlaceRepository() + // .getPlaceDetailsFromPlaceId(cities?.elementAt(index).placeId ?? ""); + + LatLng latLng = + await getCityLatLong(cities?.elementAt(index).placeId ?? ""); + return latLng; + } + + Future getCityLatLong(String placeId) async { + var rawCityLatLong = + await GooglePlaceRepository().getPlaceDetailsFromPlaceId(placeId); + + var citylatLong = LatLng(rawCityLatLong['lat'], rawCityLatLong['lng']); + return citylatLong; + } + + @override + void dispose() async { + _googleMapController?.dispose(); + _searchController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + Widget buildSearchIcon() { + return Padding( + padding: const EdgeInsets.all(8.0), + child: UiUtils.getSvg(AppIcons.search, + color: context.color.tertiaryColor)); + } + + return WillPopScope( + onWillPop: () async { + _googleMapController?.dispose(); + (await completer.future).dispose(); + showGoogleMap = false; + setState(() {}); + return true; + }, + child: SafeArea( + child: Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + automaticallyImplyLeading: false, + centerTitle: true, + titleSpacing: 0, + actions: [ + FittedBox( + fit: BoxFit.none, + child: SizedBox( + width: 24, + height: 24, + child: ValueListenableBuilder( + valueListenable: loadintCitiesInProgress, + builder: (context, va, c) { + if (va == false) { + return const SizedBox.shrink(); + } + return CircularProgressIndicator( + color: context.color.tertiaryColor, + strokeWidth: 1.5, + ); + }), + )) + ], + leading: cities != null + ? IconButton( + onPressed: () { + cities = null; + _searchController.text = ""; + setState(() {}); + }, + icon: Icon( + Icons.close, + color: context.color.tertiaryColor, + )) + : Material( + clipBehavior: Clip.antiAlias, + color: Colors.transparent, + type: MaterialType.circle, + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Padding( + padding: const EdgeInsets.all(18.0), + child: UiUtils.getSvg(AppIcons.arrowLeft, + fit: BoxFit.none, + color: context.color.tertiaryColor), + ), + ), + ), + title: Container( + width: 270.rw(context), + height: 50.rh(context), + alignment: Alignment.center, + decoration: BoxDecoration( + border: Border.all( + width: 1.5, color: context.color.borderColor), + borderRadius: const BorderRadius.all(Radius.circular(10)), + color: context.color.secondaryColor), + child: TextFormField( + focusNode: _searchFocus, + controller: _searchController, + decoration: InputDecoration( + border: InputBorder.none, //OutlineInputBorder() + fillColor: Theme.of(context).colorScheme.secondaryColor, + hintText: UiUtils.translate(context, "searchHintLbl"), + prefixIcon: buildSearchIcon(), + prefixIconConstraints: + const BoxConstraints(minHeight: 5, minWidth: 5), + ), + enableSuggestions: true, + onEditingComplete: () { + FocusScope.of(context).unfocus(); + }, + onTap: () { + //change prefix icon color to primary + })), + ), + body: Stack( + children: [ + if (showGoogleMap) + GoogleMap( + markers: marker, + onMapCreated: (controller) { + completer.complete(controller); + showSellRentLables = true; + setState(() {}); + }, + onTap: (argument) { + activePropertyModal = null; + selectedMarker = 99999999999999; + setState(() {}); + }, + mapType: AppSettings.googleMapType, + compassEnabled: false, + mapToolbarEnabled: false, + trafficEnabled: true, + myLocationButtonEnabled: false, + zoomControlsEnabled: false, + myLocationEnabled: true, + initialCameraPosition: CameraPosition( + target: cameraPosition, + ), + ), + sellRentLable(context), + if (cities != null) + Container( + color: context.color.backgroundColor, + child: ListView.builder( + itemCount: cities?.length ?? 0, + itemBuilder: (context, index) { + return ListTile( + onTap: () async { + activePropertyModal = null; + setState(() {}); + onTapCity(index); + }, + leading: SvgPicture.asset( + AppIcons.location, + color: context.color.textColorDark, + ), + title: Text(cities?.elementAt(index).city ?? ""), + subtitle: Text( + "${cities?.elementAt(index).state ?? ""},${cities?.elementAt(index).country ?? ""}"), + ); + }, + ), + ), + PositionedDirectional( + bottom: 0, + child: ValueListenableBuilder( + valueListenable: isLoadingProperty, + builder: (context, val, child) { + if (cities != null) { + return const SizedBox.shrink(); + } + if (val == true) { + return SizedBox( + width: MediaQuery.of(context).size.width, + child: const Padding( + padding: EdgeInsets.all(20.0), + child: Row( + children: [ + CustomShimmer( + width: 100, + height: 110, + ), + SizedBox( + width: 5, + ), + Expanded( + child: CustomShimmer( + height: 110, + ), + ), + ], + ), + ), + ); + } else { + if (activePropertyModal != null) { + return SizedBox( + width: MediaQuery.of(context).size.width, + child: Padding( + padding: const EdgeInsets.all(20), + child: GestureDetector( + onTap: () { + Navigator.pushNamed( + context, Routes.propertyDetails, + arguments: { + 'propertyData': activePropertyModal, + 'fromMyProperty': true, + }); + }, + child: PropertyHorizontalCard( + showLikeButton: false, + property: activePropertyModal!), + ), + ), + ); + } else { + return Container(); + } + } + })) + ], + ), + ), + ), + ); + } + + Padding sellRentLable(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Container( + width: 20, + height: 20, + color: Colors.green, + ), + const SizedBox( + width: 3, + ), + const Text("Sell").color(context.color.buttonColor), + const SizedBox( + width: 10, + ), + Container( + width: 20, + height: 20, + color: Colors.orange, + ), + const SizedBox( + width: 3, + ), + const Text("Rent").color(context.color.buttonColor), + ], + ), + ); + } +} diff --git a/lib/Ui/screens/onboarding/onboarding_screen.dart b/lib/Ui/screens/onboarding/onboarding_screen.dart new file mode 100644 index 0000000..88e1908 --- /dev/null +++ b/lib/Ui/screens/onboarding/onboarding_screen.dart @@ -0,0 +1,302 @@ +import 'package:ebroker/utils/Lottie/lottieEditor.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hive/hive.dart'; +import 'package:lottie/lottie.dart'; + +import '../../../app/routes.dart'; +import '../../../data/cubits/system/fetch_system_settings_cubit.dart'; +import '../../../data/model/system_settings_model.dart'; +import '../../../utils/AppIcon.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/hive_keys.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; + +class OnboardingScreen extends StatefulWidget { + const OnboardingScreen({Key? key}) : super(key: key); + + @override + State createState() => _OnboardingScreenState(); +} + +class _OnboardingScreenState extends State { + int currentPageIndex = 0; + int previousePageIndex = 0; + double changedOnPageScroll = 0.5; + double currentSwipe = 0; + late int totalPages; + + final LottieEditor _onBoardingOne = LottieEditor(); + final LottieEditor _onBoardingTwo = LottieEditor(); + final LottieEditor _onBoardingThree = LottieEditor(); + + dynamic onBoardingOneData; + dynamic onBoardingTwoData; + dynamic onBoardingThreeData; + + @override + void initState() { + _onBoardingOne.openAndLoad("assets/lottie/onbo_a.json"); + _onBoardingTwo.openAndLoad("assets/lottie/onbo_b.json"); + _onBoardingThree.openAndLoad("assets/lottie/onbo_c.json"); + + Future.delayed( + Duration.zero, + () { + _onBoardingOne.changeWholeLottieFileColor(context.color.tertiaryColor); + _onBoardingTwo.changeWholeLottieFileColor(context.color.tertiaryColor); + _onBoardingThree + .changeWholeLottieFileColor(context.color.tertiaryColor); + + onBoardingOneData = _onBoardingOne.convertToUint8List(); + onBoardingTwoData = _onBoardingTwo.convertToUint8List(); + onBoardingThreeData = _onBoardingThree.convertToUint8List(); + setState(() {}); + }, + ); + + Future.delayed( + Duration.zero, + () { + setState(() {}); + }, + ); + super.initState(); + } + + @override + Widget build(BuildContext context) { + List slidersList = [ + { + 'lottie': onBoardingOneData, + 'title': UiUtils.translate(context, "onboarding_1_title"), + 'description': UiUtils.translate(context, "onboarding_1_description"), + 'button': 'next_button.svg' + }, + { + 'lottie': onBoardingTwoData, + 'title': UiUtils.translate(context, "onboarding_2_title"), + 'description': UiUtils.translate(context, "onboarding_2_description"), + }, + { + 'lottie': onBoardingThreeData, + 'title': UiUtils.translate(context, "onboarding_3_title"), + 'description': UiUtils.translate(context, "onboarding_3_description"), + }, + ]; + + totalPages = slidersList.length; + return AnnotatedRegion( + value: const SystemUiOverlayStyle( + systemNavigationBarColor: Colors.transparent, + statusBarColor: Colors.transparent, + ), + child: Scaffold( + backgroundColor: context.color.backgroundColor, + body: Stack( + children: [ + Container( + color: context.color.tertiaryColor.withOpacity(0.25), + ), + Align( + alignment: Alignment.center.add(const Alignment(0, -.3)), + child: SizedBox( + height: 300, + child: (slidersList[currentPageIndex]['lottie'] != null) + ? Lottie.memory( + slidersList[currentPageIndex]['lottie'], + delegates: LottieDelegates( + values: [], + ), + ) + : Container(), + )), + PositionedDirectional( + top: kPagingTouchSlop, + start: 5, + child: TextButton( + onPressed: () async { + context + .read() + .fetchSettings(isAnonymouse: true); + Navigator.pushNamed( + context, Routes.languageListScreenRoute); + }, + child: StreamBuilder( + stream: Hive.box(HiveKeys.languageBox) + .watch(key: HiveKeys.currentLanguageKey), + builder: (context, AsyncSnapshot value) { + if (value.data?.value == null) { + if (context + .watch() + .getSetting(SystemSetting.defaultLanguage) + .toString() == + "null") { + return const Text(""); + } + return Text(context + .watch() + .getSetting(SystemSetting.defaultLanguage) + .toString() + .firstUpperCase()) + .color(context.color.textColorDark); + } else { + return Text(value.data!.value!['code'] + .toString() + .toString() + .firstUpperCase()) + .color(context.color.textColorDark); + } + }))), + PositionedDirectional( + top: kPagingTouchSlop, + end: 5, + child: GestureDetector( + onTap: () { + Navigator.pushReplacementNamed(context, Routes.login); + }, + child: Padding( + padding: const EdgeInsets.all(14.0), + child: Icon( + Icons.close, + color: context.color.tertiaryColor, + ), + ), + )), + Positioned( + bottom: 0, + child: GestureDetector( + onHorizontalDragUpdate: (DragUpdateDetails details) { + currentSwipe = details.localPosition.direction; + setState(() {}); + }, + onHorizontalDragEnd: (details) { + if (currentSwipe < 0.5) { + if (changedOnPageScroll == 1 || + changedOnPageScroll == 0.5) { + if (currentPageIndex > 0) { + currentPageIndex--; + changedOnPageScroll = 0; + } + } + setState(() {}); + } else { + if (currentPageIndex < totalPages) { + if (changedOnPageScroll == 0 || + changedOnPageScroll == 0.5) { + if (currentPageIndex < slidersList.length - 1) { + currentPageIndex++; + } else { + Navigator.of(context).pushNamedAndRemoveUntil( + Routes.login, (route) => false); + } + setState(() {}); + } + } + } + + changedOnPageScroll = 0.5; + setState(() {}); + }, + child: Container( + height: 304.rh(context), + width: context.screenWidth, + decoration: BoxDecoration( + color: context.color.primaryColor, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(40), + topRight: Radius.circular(40)), + ), + child: Padding( + padding: const EdgeInsets.only( + right: 20, left: 20, bottom: 20, top: 10), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: const EdgeInsets.all(15.0), + child: Text(slidersList[currentPageIndex]['title'], + key: const Key("onboarding_title")) + .size(context.font.extraLarge.rf(context)) + .color(context.color.tertiaryColor) + .bold(weight: FontWeight.w600), + ), + Text( + slidersList[currentPageIndex]['description'], + textAlign: TextAlign.center, + ) + .setMaxLines(lines: 3) + .size(context.font.larger.rf(context)) + .color(context.color.textColorDark), + const Spacer(), + Row( + children: [ + Row(children: [ + for (var i = 0; i < slidersList.length; i++) ...[ + buildIndicator(context, + selected: i == currentPageIndex) + ], + ]), + const Spacer(), + GestureDetector( + key: const ValueKey("next_screen"), + onTap: () { + if (currentPageIndex < slidersList.length - 1) { + currentPageIndex++; + } else { + Navigator.of(context).pushNamedAndRemoveUntil( + Routes.login, (route) => false); + } + setState(() {}); + }, + child: CircleAvatar( + radius: 35, + backgroundColor: context.color.tertiaryColor, + child: UiUtils.getSvg(AppIcons.iconArrowLeft), + ), + ) + ], + ) + ], + ), + ), + ), + ), + ) + ], + ), + ), + ); + } + + Widget buildIndicator(BuildContext context, {required bool selected}) { + if (selected) { + return Padding( + padding: const EdgeInsets.all(1.0), + child: Container( + width: 36, + height: 12, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(7), + color: context.color.tertiaryColor, + ), + ), + ); + } else { + return Padding( + padding: const EdgeInsets.all(1.0), + child: Container( + width: 12, + height: 12, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: + Border.all(color: context.color.textLightColor, width: 1.9)), + ), + ); + } + } +} diff --git a/lib/Ui/screens/openmap/mymaps.dart b/lib/Ui/screens/openmap/mymaps.dart new file mode 100644 index 0000000..5d397e9 --- /dev/null +++ b/lib/Ui/screens/openmap/mymaps.dart @@ -0,0 +1,5 @@ +import 'dart:async'; +import 'dart:io'; + + +class \ No newline at end of file diff --git a/lib/Ui/screens/project/create/add_project_details.dart b/lib/Ui/screens/project/create/add_project_details.dart new file mode 100644 index 0000000..6c37928 --- /dev/null +++ b/lib/Ui/screens/project/create/add_project_details.dart @@ -0,0 +1,650 @@ +import 'dart:developer'; +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:dotted_border/dotted_border.dart'; +import 'package:ebroker/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import 'package:ebroker/data/cubits/project/manage_project_cubit.dart'; +import 'package:ebroker/data/model/project_model.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/CloudState/cloud_state.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:ebroker/utils/string_extenstion.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:geocoding/geocoding.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:hive/hive.dart'; + +import '../../../../data/Repositories/location_repository.dart'; +import '../../../../data/model/google_place_model.dart'; +import '../../../../utils/AppIcon.dart'; +import '../../../../utils/hive_keys.dart'; +import '../../proprties/AddProperyScreens/add_property_details.dart'; +import '../../widgets/adaptive_image_picker.dart'; +import '../../widgets/custom_text_form_field.dart'; + +class AddProjectDetails extends StatefulWidget { + final Map? editData; + const AddProjectDetails({super.key, this.editData}); + static route(RouteSettings settings) { + return BlurredRouter(builder: (context) { + return BlocProvider( + create: (context) => ManageProjectCubit(), + child: AddProjectDetails( + editData: settings.arguments as Map?, + )); + }); + } + + @override + CloudState createState() => _AddProjectDetailsState(); +} + +class _AddProjectDetailsState extends CloudState { + late bool isEdit = widget.editData != null; + + late ProjectModel? project = getEditProjectData(widget.editData?['project']); + + late final TextEditingController _titleController = + TextEditingController(text: project?.title); + late final TextEditingController _descriptionController = + TextEditingController(text: project?.description); + late final TextEditingController _videoLinkController = + TextEditingController(text: project?.videoLink); + String selectedLocation = ""; + GooglePlaceModel? suggestion; + final GlobalKey _formKey = GlobalKey(); + + List documentFiles = []; + List removedDocumentId = []; + List removedGalleryImageId = []; + + GooglePlaceRepository googlePlaceRepository = GooglePlaceRepository(); + + late final TextEditingController _cityNameController = + TextEditingController(text: project?.city); + + late final TextEditingController _stateNameController = + TextEditingController(text: project?.state); + + late final TextEditingController _countryNameController = + TextEditingController(text: project?.country); + + late final TextEditingController _addressController = + TextEditingController(text: project?.location); + // final TextEditingController _main=TextEditingController(); + double? latitude; + double? longitude; + Map? floorPlans = {}; + List floorPlansRawData = []; + ImagePickerValue? titleImage; + ImagePickerValue? galleryImages; + String projectType = "upcoming"; + List removedPlansId = []; + ProjectModel? getEditProjectData(Map? data) { + if (data == null) { + return null; + } + return ProjectModel.fromMap(data); + } + + @override + void initState() { + //add documents in edit mode + List? list = project?.documents?.map((document) { + return UrlDocument(document.name!, document.id!); + }).toList(); + + if (list != null) { + documentFiles = List.from(list as List); + } + projectType = project?.type ?? "upcoming"; + if (project != null && project?.image != "") { + titleImage = UrlValue(project!.image!); + } + + if (project != null && project!.gallaryImages!.isNotEmpty) { + galleryImages = MultiValue( + project!.gallaryImages!.map((e) => UrlValue(e.name!)).toList()); + } + + ///add plans in edit mode + project?.plans?.forEach((plan) { + floorPlansRawData.add({ + "title": plan.title, + "id": plan.id, + "image": plan.document, + }); + }); + + setState(() {}); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: UiUtils.buildAppBar( + context, + title: "projectDetails".translate(context), + showBackButton: true, + ), + bottomNavigationBar: BottomAppBar( + color: context.color.backgroundColor, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 14.0, vertical: 5), + child: MaterialButton( + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + color: context.color.tertiaryColor, + onPressed: () { + if (_formKey.currentState!.validate()) { + Map documents = {}; + try { + documents = documentFiles.fold({}, (pr, el) { + if (el is FileDocument) { + pr.addAll({ + "documents[${pr.length}]": + MultipartFile.fromFileSync(el.value.path) + }); + } + return pr; + }); + } catch (e) { + log("issue is $e"); + } + addCloudData( + 'add_project_details', + { + "title": _titleController.text, + "description": _descriptionController.text, + "latitude": latitude, + "longitude": longitude, + "city": _cityNameController.text, + "state": _stateNameController.text, + "country": _countryNameController.text, + "location": _addressController.text, + "video_link": _videoLinkController.text, + if (titleImage != null && + titleImage is! UrlValue && + titleImage?.value != "") + "image": titleImage, + "gallery_images": galleryImages, + + ...documents, + "is_edit": isEdit, + "project": project, + "type": projectType, + "remove_gallery_images": removedGalleryImageId.join(","), + "remove_documents": removedDocumentId.join(","), + "remove_plans": removedPlansId.join(","), + + ////if there is data it will add into it + ...widget.editData ?? {} + }, + ); + + //this will create Map from List + + floorPlansRawData + .removeWhere((element) => element['image'] is String); + Map fold = floorPlansRawData.fold({}, (previousValue, element) { + previousValue.addAll({ + "plans[${previousValue.length ~/ 2}][id]": + (element['id'] is ValueKey) + ? (element['id'] as ValueKey).value + : "", + "plans[${previousValue.length ~/ 2}][document]": + element['image'], + "plans[${previousValue.length ~/ 2}][title]": + element['title'], + }); + return previousValue; + }); + + addCloudData("floor_plans", fold); + + Navigator.pushNamed(context, Routes.projectMetaDataScreens); + } + }, + height: 50, + child: Text("continue".translate(context)) + .color(context.color.secondaryColor), + ), + ), + ), + body: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Form( + key: _formKey, + child: Padding( + padding: const EdgeInsets.all(14.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("projectName".translate(context)), + height(), + CustomTextFormField( + controller: _titleController, + validator: CustomTextFieldValidator.nullCheck, + action: TextInputAction.next, + hintText: "projectName".translate(context), + ), + height(), + Text("Description".translate(context)), + height(), + CustomTextFormField( + action: TextInputAction.next, + controller: _descriptionController, + validator: CustomTextFieldValidator.nullCheck, + hintText: UiUtils.translate(context, "writeSomething"), + maxLine: 100, + minLine: 6, + ), + height(), + projectTypeField(context), + height(), + buildLocationChooseHeader(), + height(), + buildProjectLocationTextFields(), + height(), + Text("uploadMainPicture".translate(context)), + height(), + AdaptiveImagePickerWidget( + isRequired: true, + multiImage: false, + value: isEdit ? UrlValue(project!.image!) : null, + title: UiUtils.translate(context, "addMainPicture"), + onSelect: (ImagePickerValue? selected) { + titleImage = selected; + setState(() {}); + }, + ), + height(), + Text("uploadOtherImages".translate(context)), + height(), + AdaptiveImagePickerWidget( + title: UiUtils.translate(context, "addOtherImage"), + onRemove: (value) { + if (value is UrlValue) { + removedGalleryImageId.add(value.metaData['id']); + } + }, + multiImage: true, + value: MultiValue([ + ...project?.gallaryImages?.map((e) => UrlValue(e.name!, { + "id": e.id!, + })) ?? + [] + ]), + onSelect: (ImagePickerValue? selected) { + if (selected is MultiValue) { + galleryImages = selected; + setState(() {}); + } + }, + ), + height(), + Text("videoLink".translate(context)), + height(), + CustomTextFormField( + action: TextInputAction.next, + controller: _videoLinkController, + validator: CustomTextFieldValidator.link, + hintText: "http://example.com/video.mp4", + ), + height(), + Text("projectDocuments".translate(context)), + height(), + buildDocumentPicker(context), + ...documentsList(), + height(), + Row( + children: [ + Column( + children: [ + Text( + "floorPlans".translate(context), + ), + Text("${floorPlansRawData.length}").bold() + ], + ), + Spacer(), + MaterialButton( + elevation: 0, + color: context.color.tertiaryColor.withOpacity(0.1), + onPressed: () async { + Map? data = await Navigator.pushNamed( + context, Routes.manageFloorPlansScreen, + arguments: {"floorPlan": floorPlansRawData}) + as Map?; + if (data != null) { + floorPlansRawData = data['floorPlans'] ?? []; + + removedPlansId = data['removed']; + } + setState(() {}); + }, + child: const Text("Manage"), + ) + ], + ), + height(30) + ], + ), + ), + ), + ), + ); + } + + Widget projectTypeField(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("projectStatus".translate(context)), + height(), + InputDecorator( + decoration: InputDecoration( + hintStyle: TextStyle( + color: context.color.textColorDark.withOpacity(0.7), + fontSize: context.font.large), + filled: true, + fillColor: context.color.secondaryColor, + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + width: 1.5, color: context.color.tertiaryColor), + borderRadius: BorderRadius.circular(10)), + enabledBorder: OutlineInputBorder( + borderSide: + BorderSide(width: 1.5, color: context.color.borderColor), + borderRadius: BorderRadius.circular(10)), + border: OutlineInputBorder( + borderSide: + BorderSide(width: 1.5, color: context.color.borderColor), + borderRadius: BorderRadius.circular(10))), + child: DropdownButton( + isExpanded: true, + value: projectType, + isDense: true, + borderRadius: BorderRadius.zero, + padding: EdgeInsets.zero, + underline: const SizedBox.shrink(), + items: [ + DropdownMenuItem( + child: Text("Upcoming".translate(context)), + value: "upcoming", + ), + DropdownMenuItem( + child: Text("Under Construction".translate(context)), + value: "under_construction", + ), + ], + onChanged: (value) { + projectType = value!; + setState(() {}); + }, + ), + ), + ], + ); + } + + buildProjectLocationTextFields() { + return Column( + children: [ + CustomTextFormField( + action: TextInputAction.next, + controller: _cityNameController, + isReadOnly: false, + validator: CustomTextFieldValidator.nullCheck, + hintText: UiUtils.translate(context, "city"), + ), + SizedBox( + height: 10.rh(context), + ), + CustomTextFormField( + action: TextInputAction.next, + controller: _stateNameController, + isReadOnly: false, + validator: CustomTextFieldValidator.nullCheck, + hintText: UiUtils.translate(context, "state"), + ), + SizedBox( + height: 10.rh(context), + ), + CustomTextFormField( + action: TextInputAction.next, + controller: _countryNameController, + isReadOnly: false, + validator: CustomTextFieldValidator.nullCheck, + hintText: UiUtils.translate(context, "country"), + ), + SizedBox( + height: 10.rh(context), + ), + CustomTextFormField( + action: TextInputAction.next, + controller: _addressController, + hintText: UiUtils.translate(context, "addressLbl"), + maxLine: 100, + validator: CustomTextFieldValidator.nullCheck, + minLine: 4, + ) + ], + ); + } + + buildLocationChooseHeader() { + return SizedBox( + height: 35.rh(context), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded(flex: 3, child: Text("projectLocation".translate(context))), + // const Spacer(), + Expanded( + flex: 3, + child: ChooseLocationFormField( + initialValue: false, + validator: (bool? value) { + if (project != null) { + } + + if (value == true) { + return null; + } else { + return "Select location"; + } + }, + build: (state) { + return Container( + decoration: BoxDecoration( + // color: context.color.teritoryColor, + border: Border.all( + width: 1.5, + color: + state.hasError ? Colors.red : Colors.transparent), + borderRadius: BorderRadius.circular(9)), + child: MaterialButton( + height: 30, + onPressed: () { + _onTapChooseLocation.call(state); + }, + child: FittedBox( + fit: BoxFit.fitWidth, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + UiUtils.getSvg(AppIcons.location, + color: context.color.textLightColor), + const SizedBox( + width: 3, + ), + Text( + UiUtils.translate(context, "chooseLocation"), + ) + .size(context.font.normal) + .color(context.color.tertiaryColor) + .underline(), + ], + ), + )), + ); + }, + ), + ) + ], + ), + ); + } + + void _onTapChooseLocation(FormFieldState state) async { + try { + FocusManager.instance.primaryFocus?.unfocus(); + Map? placeMark = await Navigator.pushNamed( + context, Routes.chooseLocaitonMap, + arguments: { + "latitude": project != null + ? double.parse(project!.latitude!) + : Hive.box(HiveKeys.userDetailsBox) + .get("latitude") + .toString() + .toDouble(), + "longitude": project != null + ? double.parse(project!.longitude!) + : Hive.box(HiveKeys.userDetailsBox) + .get("longitude") + .toString() + .toDouble() + }) as Map?; + LatLng? latlng = placeMark?['latlng'] as LatLng?; + Placemark? place = placeMark?['place'] as Placemark?; + + if (latlng != null && place != null) { + latitude = latlng.latitude; + longitude = latlng.longitude; + // _latitudeController.text = latlng.latitude.toString(); + // _longitudeController.text = latlng.longitude.toString(); + _cityNameController.text = place.locality ?? ""; + _countryNameController.text = place.country ?? ""; + _stateNameController.text = place.administrativeArea ?? ""; + _addressController.text = + [place.locality, place.administrativeArea, place.country].join(","); + // _addressController.text = getAddress(place); + + state.didChange(true); + } else { + print('no action'); + // state.didChange(false); + } + } catch (e) { + log("THE ISSUE IS $e"); + } + } + + Widget height([double? h]) { + return SizedBox( + height: (h)?.rh(context) ?? 15.rh(context), + ); + } + + List documentsList() { + return documentFiles.map((document) { + String fileName = ""; + if (document is FileDocument) { + fileName = document.value.path.split("/").last; + } else { + fileName = document.value.toString().split("/").last; + } + + return ListTile( + title: Text(fileName).setMaxLines(lines: 2), + dense: true, + trailing: IconButton( + icon: const Icon(Icons.close), + onPressed: () { + if (document is UrlDocument) { + removedDocumentId.add(document.id); + } + documentFiles.remove(document); + setState(() {}); + }, + ), + ); + }).toList(); + } + + Widget buildDocumentPicker(BuildContext context) { + return Container( + child: Row( + children: [ + DottedBorder( + borderType: BorderType.RRect, + color: context.color.textLightColor, + radius: const Radius.circular(20), + child: Container( + width: 60, + height: 60, + child: Center( + child: IconButton( + onPressed: () async { + FilePickerResult? filePickerResult = + await FilePicker.platform.pickFiles( + allowMultiple: true, + ); + if (filePickerResult != null) { + List list = + List.from(filePickerResult.files.map((e) { + return FileDocument(File(e.path!)); + }).toList()); + documentFiles.addAll(list); + } + + setState(() {}); + }, + icon: const Icon(Icons.upload), + )), + ), + ), + const SizedBox( + width: 15, + ), + Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("UploadDocs".translate(context)), + const SizedBox( + height: 4, + ), + Text(documentFiles.length.toString()) + ], + ), + ], + ), + ); + } +} + +abstract class Document { + abstract final T value; +} + +class FileDocument extends Document { + final File value; + FileDocument(this.value); +} + +class UrlDocument extends Document { + @override + final String value; + final int id; + UrlDocument(this.value, this.id); +} diff --git a/lib/Ui/screens/project/create/add_project_meta_details.dart b/lib/Ui/screens/project/create/add_project_meta_details.dart new file mode 100644 index 0000000..2d14094 --- /dev/null +++ b/lib/Ui/screens/project/create/add_project_meta_details.dart @@ -0,0 +1,177 @@ +import 'package:ebroker/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import 'package:ebroker/data/cubits/project/fetchMyProjectsListCubit.dart'; +import 'package:ebroker/data/cubits/project/manage_project_cubit.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/CloudState/cloud_state.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:flutter/material.dart'; + +import '../../../../data/helper/widgets.dart'; +import '../../../../data/model/category.dart' as c; +import '../../../../data/model/project_model.dart'; +import '../../../../utils/ui_utils.dart'; +import '../../widgets/adaptive_image_picker.dart'; +import '../../widgets/custom_text_form_field.dart'; + +class ProjectMetaDetails extends StatefulWidget { + static route(RouteSettings settings) { + return BlurredRouter( + builder: (context) { + return BlocProvider( + create: (context) => ManageProjectCubit(), + child: const ProjectMetaDetails()); + }, + ); + } + + const ProjectMetaDetails({super.key}); + + @override + CloudState createState() => _ProjectMetaDetailsState(); +} + +class _ProjectMetaDetailsState extends CloudState { + late Map projectDetails = + Map.from(getCloudData("add_project_details")); + late ProjectModel? project = projectDetails['project'] == null + ? null + : ProjectModel.fromMap(projectDetails['project']); + final GlobalKey _formKey = GlobalKey(); + late final TextEditingController _metaTitleController = + TextEditingController(text: project?.metaTitle); + late final TextEditingController _metaDescriptionController = + TextEditingController(text: project?.metaDescription); + late final TextEditingController _metaKeywordsController = + TextEditingController(text: project?.metaKeywords); + ImagePickerValue? metaImage; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: UiUtils.buildAppBar(context, + title: "addProjectMeta".translate(context), showBackButton: true), + bottomNavigationBar: BottomAppBar( + color: context.color.backgroundColor, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 14.0, vertical: 5), + child: MaterialButton( + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + color: context.color.tertiaryColor, + onPressed: () { + if (_formKey.currentState!.validate()) { + Map data = {}; + + Map metaDetails = { + "meta_title": _metaTitleController.text, + "meta_description": _metaDescriptionController.text, + "meta_keywords": _metaKeywordsController.text, + "meta_image": metaImage + }; + data + ..addAll(projectDetails) + ..addAll(metaDetails) + ..addAll( + Map.from( + getCloudData('floor_plans'), + ), + ); + + if (!projectDetails.containsKey('category_id')) { + data.addAll({ + "category_id": + (Constant.addProperty['category'] as c.Category).id! + }); + } + data.remove("project"); + + context + .read() + .manage(type: ManageProjectType.create, data: data); + } + // Navigator.pushNamed(context, Routes.projectMetaDataScreens); + }, + height: 50, + child: Text("continue".translate(context)) + .color(context.color.secondaryColor), + ), + ), + ), + body: BlocListener( + listener: (context, state) { + if (state is ManageProjectInProgress) { + Widgets.showLoader(context); + } + + if (state is ManageProjectInSuccess) { + context.read().update(state.project); + Widgets.hideLoder(context); + Navigator.of(context) + ..pop() + ..pop() + ..pop(); + } + if (state is ManageProjectInFail) { + throw state.error; + } + }, + child: Padding( + padding: const EdgeInsets.all(14.0), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("metaDetails".translate(context)), + height(), + CustomTextFormField( + controller: _metaTitleController, + hintText: "metaTitle".translate(context), + ), + height(10), + CustomTextFormField( + controller: _metaKeywordsController, + hintText: "metaKeywords".translate(context), + ), + height(10), + CustomTextFormField( + controller: _metaDescriptionController, + hintText: "metaDescription".translate(context), + minLine: 5, + maxLine: 100, + ), + height(10), + AdaptiveImagePickerWidget( + isRequired: true, + title: UiUtils.translate(context, "addMetaImage"), + multiImage: false, + value: project != null ? UrlValue(project!.metaImage!) : null, + allowedSizeBytes: 307200, + onSelect: (ImagePickerValue? selected) { + if (selected is FileValue || selected == null) { + metaImage = selected; + setState(() {}); + } + }, + ), + ], + ), + ), + ), + ), + ); + } + + Widget height([double? h]) { + return SizedBox( + height: (h)?.rh(context) ?? 15.rh(context), + ); + } +} diff --git a/lib/Ui/screens/project/create/manage_floor_plans.dart b/lib/Ui/screens/project/create/manage_floor_plans.dart new file mode 100644 index 0000000..5bc5cc3 --- /dev/null +++ b/lib/Ui/screens/project/create/manage_floor_plans.dart @@ -0,0 +1,272 @@ +import 'package:ebroker/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import 'package:ebroker/Ui/screens/widgets/custom_text_form_field.dart'; +import 'package:ebroker/utils/CloudState/cloud_state.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; + +import '../../widgets/adaptive_image_picker.dart'; + +class ManageFloorPlansScreen extends StatefulWidget { + final List? floorPlans; + + static route(RouteSettings settings) { + Map? arguments = settings.arguments as Map?; + return BlurredRouter( + builder: (context) { + return ManageFloorPlansScreen( + floorPlans: arguments?['floorPlan'], + ); + }, + ); + } + + const ManageFloorPlansScreen({super.key, required this.floorPlans}); + + @override + CloudState createState() => + _ManageFloorPlansScreenState(); +} + +class _ManageFloorPlansScreenState extends CloudState { + List floorPlans = []; + List removePlanId = []; + + final GlobalKey _formKey = GlobalKey(); + @override + void initState() { + if (widget.floorPlans != null) { + widget.floorPlans?.forEach((value) { + FloorPlan floorPlan = FloorPlan( + planKey: value['id'] is int ? ValueKey(value['id']) : value['id'], + key: UniqueKey(), + title: value['title'], + imagePickerValue: value['image'] is String + ? UrlValue(value['image']) + : value['image'], + onClose: (e) { + removeFromListWhere( + listKey: 'floorsList', + whereKey: 'id', + equals: e, + ); + if (e is ValueKey) { + removePlanId.add(e.value); + } + floorPlans.removeWhere((element) => element.planKey == e); + setState(() {}); + }, + ); + floorPlans.add(floorPlan); + }); + setState(() {}); + } else { + FloorPlan floorPlan = FloorPlan( + planKey: GlobalKey(), + key: UniqueKey(), + onClose: (key) { + removeFromGroup('floors', key); + if (key is ValueKey) { + removePlanId.add(key.value); + } + floorPlans.removeWhere((element) => element.planKey == key); + setState(() {}); + }, + ); + floorPlans.add(floorPlan); + } + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return PopScope( + onPopInvoked: (didPop) { + clearGroup('floors'); + }, + child: Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: UiUtils.buildAppBar(context, + showBackButton: true, title: "FloorPlans".translate(context)), + bottomNavigationBar: BottomAppBar( + color: context.color.backgroundColor, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 14.0, vertical: 5), + child: MaterialButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10)), + color: context.color.tertiaryColor, + onPressed: () { + List? floors = getCloudData("floorsList") as List?; + + Navigator.pop(context, { + "floorPlans": floors, + "removed": removePlanId, + }); + }, + height: 50, + child: Text("continue".translate(context)) + .color(context.color.secondaryColor), + ), + ), + ), + body: Form( + key: _formKey, + child: SingleChildScrollView( + child: Container( + width: context.screenWidth, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ...floorPlans, + MaterialButton( + color: context.color.tertiaryColor, + onPressed: () { + if (_formKey.currentState!.validate()) { + FloorPlan floorPlan = FloorPlan( + planKey: GlobalKey(), + key: UniqueKey(), + onClose: (e) { + removeFromListWhere( + listKey: 'floorsList', + whereKey: 'id', + equals: e); + if (e is ValueKey) { + removePlanId.add(e.value); + } + floorPlans + .removeWhere((element) => element.planKey == e); + setState(() {}); + }, + ); + floorPlans.add(floorPlan); + setState(() {}); + } + }, + elevation: 0, + minWidth: context.screenWidth * 0.45, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10)), + child: Text("Add".translate(context)) + .color(context.color.buttonColor), + ) + ], + ), + ), + ), + ), + ), + ); + } +} + +class FloorPlan extends StatefulWidget { + final planKey; + final String? title; + final ImagePickerValue? imagePickerValue; + final Function(Key e) onClose; + + const FloorPlan({ + super.key, + this.title, + required this.planKey, + required this.onClose, + this.imagePickerValue, + }); + + @override + CloudState createState() { + return FloorPlanState(); + } +} + +class FloorPlanState extends CloudState { + ImagePickerValue? imagePickerValue; + + late final TextEditingController floorTitle = + TextEditingController(text: widget.title); + + @override + void initState() { + imagePickerValue = widget.imagePickerValue; + super.initState(); + } + + @override + void dispose() { + floorTitle.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text("Floor Title".translate(context)), + const Spacer(), + IconButton( + onPressed: () { + widget.onClose.call(widget.planKey); + }, + icon: const Icon(Icons.close)), + ], + ), + const SizedBox( + height: 10, + ), + CustomTextFormField( + controller: floorTitle, + autovalidate: AutovalidateMode.onUserInteraction, + validator: CustomTextFieldValidator.nullCheck, + onChange: (value) { + appendToListWhere( + listKey: "floorsList", + whereKey: "id", + equals: widget.planKey, + add: { + "title": value, + "id": widget.planKey, + "image": imagePickerValue + }); + }, + hintText: "title".translate(context), + ), + const SizedBox(height: 10), + AdaptiveImagePickerWidget( + multiImage: false, + isRequired: true, + value: imagePickerValue, + title: "pickFloorMap".translate(context), + onSelect: (ImagePickerValue? selected) { + if (selected is FileValue) { + imagePickerValue = selected; + } + + // appendToList("floorsList", { + // "title": floorTitle.text, + // "key": widget.key, + // "image": imagePickerValue + // }); + appendToListWhere( + listKey: "floorsList", + whereKey: "id", + equals: widget.planKey, + add: { + "title": floorTitle.text, + "id": widget.planKey, + "image": imagePickerValue + }); + setState(() {}); + }, + ) + ], + ), + ); + } +} diff --git a/lib/Ui/screens/project/view/project_details_screen.dart b/lib/Ui/screens/project/view/project_details_screen.dart new file mode 100644 index 0000000..0aeab57 --- /dev/null +++ b/lib/Ui/screens/project/view/project_details_screen.dart @@ -0,0 +1,853 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:dio/dio.dart'; +import 'package:ebroker/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import 'package:ebroker/Ui/screens/widgets/blurred_dialoge_box.dart'; +import 'package:ebroker/data/cubits/project/delete_project_cubit.dart'; +import 'package:ebroker/data/cubits/project/fetchMyProjectsListCubit.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/CloudState/cloud_state.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/helper_utils.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:open_filex/open_filex.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../../../../data/helper/widgets.dart'; +import '../../../../data/model/project_model.dart'; +import '../../../../utils/AppIcon.dart'; +import '../../../../utils/VideoPlayer/video_player_widget.dart'; +import '../../../../utils/typedefs.dart'; +import '../../../../utils/ui_utils.dart'; +import '../../proprties/property_details.dart'; +import '../../widgets/gallery_view.dart'; + +class ProjectDetailsScreen extends StatefulWidget { + final ProjectModel project; + static route(RouteSettings settings) { + Map? arguement = settings.arguments as Map?; + return BlurredRouter( + builder: (context) { + return BlocProvider( + create: (context) => DeleteProjectCubit(), + child: ProjectDetailsScreen( + project: arguement?['project'], + ), + ); + }, + ); + } + + const ProjectDetailsScreen({ + super.key, + required this.project, + }); + + @override + CloudState createState() => + _ProjectDetailsScreenState(); +} + +class _ProjectDetailsScreenState extends CloudState { + final Completer _controller = + Completer(); + bool isMyProject = false; + late ProjectModel project; + + late final CameraPosition _kInitialPlace = CameraPosition( + target: LatLng( + double.parse(project.latitude!), + double.parse(project.longitude!), + ), + zoom: 14.4746, + ); + + @override + void initState() { + project = widget.project; + isMyProject = checkIsProjectMine(); + + super.initState(); + } + + bool checkIsProjectMine() { + return project.addedBy.toString() == HiveUtils.getUserId(); + } + + bool hasFloors() { + return project.plans!.isNotEmpty; + } + + bool hasDocuments() { + return project.documents!.isNotEmpty; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.backgroundColor, + bottomNavigationBar: BottomAppBar( + color: context.color.secondaryColor, + child: bottomNavigation(context), + ), + body: Builder(builder: (context) { + return Padding( + padding: EdgeInsets.zero, + child: BlocListener( + listener: (context, state) { + if (state is DeleteProjectInProgress) { + Widgets.showLoader(context); + } + + if (state is DeleteProjectSuccess) { + Widgets.hideLoder(context); + context.read().delete(state.id); + + Navigator.pop( + context, + ); + } + }, + child: CustomScrollView( + physics: const BouncingScrollPhysics(), + slivers: [ + SliverAppBar( + backgroundColor: context.color.secondaryColor, + leading: Material( + clipBehavior: Clip.antiAlias, + color: Colors.transparent, + type: MaterialType.circle, + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Padding( + padding: const EdgeInsets.all(18.0), + child: UiUtils.getSvg(AppIcons.arrowLeft, + fit: BoxFit.none, + color: context.color.tertiaryColor), + ), + ), + ), + systemOverlayStyle: const SystemUiOverlayStyle( + statusBarColor: Colors.transparent), + expandedHeight: context.screenHeight * 0.45, + // toolbarHeight: 0, + primary: true, + automaticallyImplyLeading: true, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + // forceMaterialTransparency: true, + floating: false, + pinned: true, + + // title: Text("I am title"), + centerTitle: true, + flexibleSpace: FlexibleSpaceBar( + collapseMode: CollapseMode.parallax, + titlePadding: EdgeInsets.zero, + background: ProjectImageCareusel( + images: [ + ...{project.image!}, + ...project.gallaryImages!.map((e) => e.name!) + ], + ), + )), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.all(14.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + categoryCard(context, project), + const Spacer(), + Text(project.type!.translate(context)) + .bold() + .size(context.font.small), + ], + ), + const SizedBox( + height: 15, + ), + Text(project.title!) + .size(context.font.larger) + .bold(weight: FontWeight.w400), + const SizedBox( + height: 15, + ), + Text(project.description!.trim()).color( + context.color.textColorDark.withOpacity(0.89)), + const SizedBox( + height: 18, + ), + + ContactDetailsWidget( + url: project.customer?.profile ?? "", + number: project.customer!.mobile!, + name: project.customer!.name!, + email: project.customer!.email!), + const SizedBox( + height: 14, + ), + if (project.videoLink != null && + project.videoLink!.isNotEmpty) + VideoPlayerWideget( + padding: const EdgeInsets.symmetric(vertical: 18), + url: project.videoLink!, + ), + if (hasDocuments()) ...[ + Text("Documents".translate(context)) + .size(context.font.large) + .bold(), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + Document? document = project.documents?[index]; + + return DownloadableDocument( + url: document!.name!, + ); + // return ListTile( + // dense: true, + // title: Text(name).size(context.font.large).color( + // context.color.textColorDark.withOpacity(0.9)), + // trailing: IconButton( + // icon: const Icon(Icons.download), + // onPressed: () {}, + // ), + // ); + }, + itemCount: project.documents?.length ?? 0, + ), + ], + const SizedBox( + height: 15, + ), + if (hasFloors()) ...[ + Text("Floor Plans".translate(context)) + .size(context.font.large) + .bold(), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: project.plans?.length ?? 0, + itemBuilder: (context, index) { + Plan floor = project.plans![index]; + return CustomExpansionTile( + title: floor.title!, + children: [Image.network(floor.document!)], + ); + }, + ), + const SizedBox( + height: 18, + ), + ], + + // Container( + // width: context.screenWidth, + // height: 210, + // child: YoutubeExplode, + // decoration: BoxDecoration( + // color: context.color.tertiaryColor, + // borderRadius: BorderRadius.circular(10)), + // ), + + Text("projectLocation".translate(context)) + .size(context.font.large) + .bold(), + + const SizedBox( + height: 15, + ), + + Padding( + padding: const EdgeInsets.symmetric(horizontal: 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text("locationLbl".translate(context)).bold(), + Text(project.location!) + ], + ), + const SizedBox( + height: 5, + ), + Row( + children: [ + Text("cityProj".translate(context)).bold(), + Text(project.city!) + ], + ), + const SizedBox( + height: 5, + ), + Row( + children: [ + Text("stateProj".translate(context)).bold(), + Text(project.state!) + ], + ), + const SizedBox( + height: 5, + ), + Row( + children: [ + Text("countryProj".translate(context)).bold(), + Text(project.country!) + ], + ), + ], + ), + ), + const SizedBox( + height: 15, + ), + + SizedBox( + height: 175, + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Stack( + fit: StackFit.expand, + children: [ + Image.asset( + "assets/map.png", + fit: BoxFit.cover, + ), + BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 4.0, + sigmaY: 4.0, + ), + child: Center( + child: MaterialButton( + onPressed: () { + Navigator.push(context, BlurredRouter( + builder: (context) { + return Scaffold( + extendBodyBehindAppBar: true, + appBar: AppBar( + elevation: 0, + iconTheme: IconThemeData( + color: context + .color.tertiaryColor), + backgroundColor: + Colors.transparent, + ), + body: GoogleMapScreen( + latitude: double.parse( + project.latitude!), + longitude: double.parse( + project.longitude!), + kInitialPlace: _kInitialPlace, + controller: _controller, + ), + ); + }, + )); + }, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(5)), + color: context.color.tertiaryColor, + elevation: 0, + child: + Text(("viewMap".translate(context))) + .color( + context.color.buttonColor, + ), + ), + ), + ), + ], + ), + ), + ), + const SizedBox( + height: 40, + ) + ], + ), + ), + ) + ], + ), + ), + ); + }), + ); + } + + Widget bottomNavigation(BuildContext context) { + if (isMyProject) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: SizedBox( + height: 65.rh(context), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: UiUtils.buildButton(context, + // padding: const EdgeInsets.symmetric(horizontal: 1), + outerPadding: const EdgeInsets.all(1), onPressed: () { + Navigator.pushNamed(context, Routes.addProjectDetails, + arguments: { + "id": project.id, + "category_id": project.category!.id!, + "project": project.toMap(), + }); + }, + fontSize: context.font.normal, + width: context.screenWidth / 3, + prefixWidget: Padding( + padding: const EdgeInsets.only(right: 6.0), + child: SvgPicture.asset(AppIcons.edit), + ), + buttonTitle: UiUtils.translate(context, "edit")), + ), + const SizedBox( + width: 8, + ), + Expanded( + child: UiUtils.buildButton(context, + padding: const EdgeInsets.symmetric(horizontal: 1), + outerPadding: const EdgeInsets.all(1), + prefixWidget: Padding( + padding: const EdgeInsets.only(right: 6.0), + child: SvgPicture.asset( + AppIcons.delete, + color: context.color.buttonColor, + width: 14, + height: 14, + ), + ), onPressed: () async { + UiUtils.showBlurredDialoge(context, + dialoge: BlurredDialogBox( + title: "areYouSure".translate(context), + onAccept: () async { + context + .read() + .delete(project.id!); + }, + content: Text( + "projectWillNotRecover".translate(context)))); + }, + fontSize: context.font.normal, + width: context.screenWidth / 3.2, + buttonTitle: UiUtils.translate(context, "deleteBtnLbl")), + ), + ], + ), + ), + ), + ); + } + return const SizedBox.shrink(); + } +} + +Widget categoryCard(BuildContext context, ProjectModel project) { + return Container( + decoration: BoxDecoration( + // color: context.color.tertiaryColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(5)), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + decoration: BoxDecoration(borderRadius: BorderRadius.circular(5)), + height: 35, + child: SvgPicture.network(project.category!.image!), + ), + const SizedBox( + width: 3, + ), + Text(project.category!.category!).size(context.font.large) + ], + ), + ); +} + +class ProjectImageCareusel extends StatefulWidget { + final List images; + const ProjectImageCareusel({ + super.key, + required this.images, + }); + + @override + State createState() => _ProjectImageCareuselState(); +} + +class _ProjectImageCareuselState extends State + with AutomaticKeepAliveClientMixin { + final ValueNotifier _sliderIndex = ValueNotifier(0); + final PageController _pageController = PageController( + initialPage: 0, + ); + late Timer _timer; + @override + void initState() { + _timer = Timer.periodic(const Duration(seconds: 5), (timer) { + if (_sliderIndex.value < widget.images.length - 1) { + _sliderIndex.value++; + } else { + _sliderIndex.value = 0; + } + if (_pageController.hasClients) { + _pageController.animateToPage( + _sliderIndex.value, + duration: const Duration(milliseconds: 1000), + curve: Curves.easeIn, + ); + } + }); + super.initState(); + } + + @override + void dispose() { + _sliderIndex.dispose(); + _timer.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + return Stack( + children: [ + PageView.builder( + itemCount: widget.images.length, + controller: _pageController, + clipBehavior: Clip.antiAlias, + physics: const BouncingScrollPhysics( + decelerationRate: ScrollDecelerationRate.fast, + ), + onPageChanged: (index) { + _sliderIndex.value = index; + }, + itemBuilder: (context, index) { + List images = widget.images; + return GestureDetector( + onTap: () { + Navigator.push( + context, + BlurredRouter( + builder: (context) => GalleryViewWidget( + images: images, + initalIndex: index, + ), + )); + }, + child: ProjectCateuseItem( + url: widget.images[index], + ), + ); + }), + Align( + alignment: Alignment.bottomCenter.add(const Alignment(0, -0.05)), + child: ValueListenableBuilder( + valueListenable: _sliderIndex, + builder: (context, val, ch) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ...List.generate( + widget.images.length, + (index) => Container( + width: 7, + margin: const EdgeInsets.symmetric(horizontal: 1), + height: 7, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: index == val + ? context.color.tertiaryColor + : Colors.white, + ), + )), + ], + ); + }), + ), + ], + ); + } + + @override + bool get wantKeepAlive => true; +} + +class ProjectCateuseItem extends StatelessWidget { + final String url; + + const ProjectCateuseItem({ + super.key, + required this.url, + }); + + @override + Widget build(BuildContext context) { + return Stack( + fit: StackFit.expand, + children: [ + Image.network( + url, + fit: BoxFit.cover, + ), + BackdropFilter( + filter: ImageFilter.blur(sigmaY: 8, sigmaX: 8), + child: Container( + color: Colors.black.withOpacity(0.2), + ), + ), + Image.network( + url, + fit: BoxFit.cover, + ), + ], + ); + } +} + +class CustomExpansionTile extends StatefulWidget { + final String title; + final Widgetss children; + const CustomExpansionTile({ + super.key, + required this.title, + required this.children, + }); + + @override + State createState() => _CustomExpansionTileState(); +} + +class _CustomExpansionTileState extends State { + bool isExpanded = false; + @override + Widget build(BuildContext context) { + return ExpansionTile( + title: Text(widget.title) + .size(context.font.large) + .color(context.color.textColorDark.withOpacity(0.9)), + // dense: true, + collapsedTextColor: context.color.textColorDark, + textColor: context.color.textColorDark, + iconColor: context.color.tertiaryColor, + collapsedIconColor: context.color.tertiaryColor, + trailing: AnimatedCrossFade( + firstChild: const Icon(Icons.add), + secondChild: const Icon(Icons.remove), + duration: const Duration(milliseconds: 250), + crossFadeState: + isExpanded ? CrossFadeState.showSecond : CrossFadeState.showFirst, + ), + onExpansionChanged: (value) { + isExpanded = value; + setState(() {}); + }, + controlAffinity: ListTileControlAffinity.trailing, + children: widget.children, + ); + } +} + +class ContactDetailsWidget extends StatelessWidget { + final String url; + final String name; + final String email; + final String number; + + const ContactDetailsWidget( + {super.key, + required this.url, + required this.name, + required this.email, + required this.number}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("contactUS".translate(context)).size(context.font.large).bold(), + SizedBox( + height: 15, + ), + Row( + children: [ + GestureDetector( + onTap: () { + UiUtils.showFullScreenImage(context, + provider: NetworkImage(url)); + }, + child: Container( + width: 70, + height: 70, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Colors.grey.shade200, + borderRadius: BorderRadius.circular(10)), + child: UiUtils.getImage(url, fit: BoxFit.cover) + + // CachedNetworkImage( + // imageUrl: widget.propertyData?.customerProfile ?? "", + // fit: BoxFit.cover, + // ), + + ), + ), + const SizedBox( + width: 10, + ), + Expanded( + flex: 5, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(name) + .size(context.font.large) + .bold() + .setMaxLines(lines: 1), + Text(email).setMaxLines(lines: 1), + ], + ), + ), + Expanded( + flex: 3, + child: Container( + // color: Colors.red, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + IconButton( + onPressed: () async { + await launchUrl(Uri.parse("tel:+$number")); + }, + icon: Icon( + Icons.call, + color: context.color.tertiaryColor, + )), + IconButton( + onPressed: () async { + await launchUrl(Uri.parse("mailto:$email")); + }, + icon: Icon( + Icons.email, + color: context.color.tertiaryColor, + )), + ], + ), + ), + ), + ], + ), + ], + ); + } +} + +class DownloadableDocument extends StatefulWidget { + final String url; + const DownloadableDocument({super.key, required this.url}); + + @override + State createState() => _DownloadableDocumentState(); +} + +class _DownloadableDocumentState extends State { + bool downloaded = false; + Dio dio = Dio(); + ValueNotifier percentage = ValueNotifier(0); + + @override + void initState() { + super.initState(); + } + + Future? path() async { + String? downloadPath = await HelperUtils.getDownloadPath(); + return downloadPath; + } + + @override + Widget build(BuildContext context) { + String name = widget.url.split("/").last; + return ListTile( + dense: true, + title: Text(name) + .size(context.font.large) + .color(context.color.textColorDark.withOpacity(0.9)), + trailing: ValueListenableBuilder( + valueListenable: percentage, + builder: (context, value, child) { + if (value != 0.0 && value != 1.0) { + return SizedBox( + height: 24, + width: 24, + child: CircularProgressIndicator( + value: value, + color: context.color.tertiaryColor, + ), + ); + } + if (downloaded) { + return IconButton( + padding: EdgeInsets.zero, + alignment: Alignment.centerRight, + splashRadius: 1, + icon: const Icon(Icons.file_open), + onPressed: () async { + String? downloadPath = await path(); + + await OpenFilex.open("$downloadPath/$name"); + }); + } + return IconButton( + padding: EdgeInsets.zero, + alignment: Alignment.centerRight, + splashRadius: 1, + icon: const Icon(Icons.download), + onPressed: () async { + String? downloadPath = await path(); + bool storagePermission = + await HelperUtils.hasStoragePermissionGiven(); + if (storagePermission) { + await dio.download( + widget.url, + "$downloadPath/$name", + onReceiveProgress: (count, total) async { + percentage.value = count / total; + if (percentage.value == 1.0) { + downloaded = true; + setState(() {}); + await OpenFilex.open("$downloadPath/$name"); + } + }, + ); + } else { + HelperUtils.showSnackBarMessage( + context, "Storage Permission denied!"); + } + }, + ); + }), + ); + } +} diff --git a/lib/Ui/screens/project/view/project_list_screen.dart b/lib/Ui/screens/project/view/project_list_screen.dart new file mode 100644 index 0000000..b4d12db --- /dev/null +++ b/lib/Ui/screens/project/view/project_list_screen.dart @@ -0,0 +1,221 @@ +import 'dart:developer'; + +import 'package:ebroker/Ui/screens/widgets/Erros/no_data_found.dart'; +import 'package:ebroker/Ui/screens/widgets/Erros/something_went_wrong.dart'; +import 'package:ebroker/data/cubits/project/fetchMyProjectsListCubit.dart'; +import 'package:ebroker/data/model/project_model.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +import '../../../../utils/ui_utils.dart'; +import '../../home/Widgets/project_card_horizontal.dart'; +import '../../widgets/AnimatedRoutes/blur_page_route.dart'; + +class ProjectListScreen extends StatefulWidget { + const ProjectListScreen({super.key}); + + static Route route(RouteSettings settings) { + return BlurredRouter( + builder: (context) { + return const ProjectListScreen(); + }, + ); + } + + @override + State createState() => _ProjectListScreenState(); +} + +class _ProjectListScreenState extends State { + final ScrollController _scrollController = ScrollController(); + + @override + void initState() { + context.read().fetch(); + + _scrollController.addListener(() { + if (_scrollController.isEndReached()) { + if (context.read().hasMore()) { + context.read().fetchMore(); + } + } + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: UiUtils.buildAppBar(context, + showBackButton: true, + title: UiUtils.translate(context, "myProjects")), + body: BlocBuilder( + builder: (context, state) { + if (state is FetchMyProjectsListInProgress) { + return Center(child: UiUtils.progress()); + } + if (state is FetchMyProjectsListFail) { + log(state.error.toString(), name: "project fetch errori s"); + return const SomethingWentWrong(); + } + if (state is FetchMyProjectsListSuccess) { + if (state.projects.isEmpty) { + return const NoDataFound(); + } + return Column( + mainAxisSize: MainAxisSize.max, + children: [ + ListView.builder( + shrinkWrap: true, + controller: _scrollController, + itemCount: state.projects.length, + padding: const EdgeInsets.all(14), + itemBuilder: (context, index) { + ProjectModel project = state.projects[index]; + + return ProjectHorizontalCard( + project: project, + ); + }, + ), + if (state.isLoadingMore) UiUtils.progress(), + ], + ); + // return ProjectCard(title: "Hello",categoryIcon: ,); + } + if (state is FetchMyProjectsListFail) { + return Center( + child: Text(state.error.toString()), + ); + } + + return Container(); + }, + ), + ); + } +} + +class ProjectCard extends StatelessWidget { + final String url; + final String title; + final String description; + final String categoryIcon; + final String categoryName; + + final String status; + const ProjectCard({ + super.key, + required this.categoryName, + required this.url, + required this.title, + required this.description, + required this.categoryIcon, + required this.status, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 0), + child: SizedBox( + width: context.screenWidth * 0.9, + height: 220, + child: LayoutBuilder(builder: (context, c) { + return Stack(children: [ + Positioned( + top: 0, + left: 0, + child: Container( + width: c.maxWidth, + height: c.maxHeight, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(18), + image: DecorationImage( + image: NetworkImage(url), fit: BoxFit.fitWidth), + ))), + Positioned( + top: 0, + left: 0, + child: Container( + width: c.maxWidth, + height: c.maxHeight, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(18), + gradient: LinearGradient( + begin: Alignment.bottomLeft, + end: Alignment.topRight, + colors: [ + Colors.black.withOpacity(0.8), + Colors.black.withOpacity(0.1) + ]), + ))), + PositionedDirectional( + bottom: 10, + start: 14, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + textAlign: TextAlign.left, + ) + .size(context.font.larger + 2) + .color(context.color.buttonColor) + .setMaxLines(lines: 1), + Text( + description, + textAlign: TextAlign.left, + ) + .size(context.font.small + 1) + .color(context.color.buttonColor) + .setMaxLines(lines: 1), + ], + ), + ), + Positioned( + top: 9, + left: 14, + child: Row( + children: [ + SvgPicture.network( + categoryIcon, + color: context.color.tertiaryColor.brighten(20), + width: 18, + height: 18, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Text( + categoryName, + textAlign: TextAlign.left, + ).size(13).color(context.color.buttonColor), + ), + ], + )), + Positioned( + top: 12, + right: 14, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), + color: context.color.buttonColor.withOpacity(0.8), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 4, horizontal: 7), + child: Text( + status, + textAlign: TextAlign.left, + ) + .size(context.font.smaller) + .color(context.color.blackColor), + ), + )), + ]); + })), + ); + } +} diff --git a/lib/Ui/screens/proprties/AddProperyScreens/add_property_details.dart b/lib/Ui/screens/proprties/AddProperyScreens/add_property_details.dart new file mode 100644 index 0000000..5e3e215 --- /dev/null +++ b/lib/Ui/screens/proprties/AddProperyScreens/add_property_details.dart @@ -0,0 +1,1409 @@ +import 'dart:io'; + +import 'package:dotted_border/dotted_border.dart'; +import 'package:ebroker/Ui/screens/widgets/adaptive_image_picker.dart'; +import 'package:ebroker/utils/hive_keys.dart'; +import 'package:ebroker/utils/string_extenstion.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:geocoding/geocoding.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:hive/hive.dart'; + +import '../../../../app/routes.dart'; +import '../../../../data/model/category.dart'; +import '../../../../data/model/property_model.dart'; +import '../../../../utils/AppIcon.dart'; +import '../../../../utils/Extensions/extensions.dart'; +import '../../../../utils/constant.dart'; +import '../../../../utils/helper_utils.dart'; +import '../../../../utils/hive_utils.dart'; +import '../../../../utils/imagePicker.dart'; +import '../../../../utils/responsiveSize.dart'; +import '../../../../utils/ui_utils.dart'; +import '../../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../../widgets/blurred_dialoge_box.dart'; +import '../../widgets/custom_text_form_field.dart'; +import '../../widgets/panaroma_image_view.dart'; + +class AddPropertyDetails extends StatefulWidget { + final Map? propertyDetails; + + const AddPropertyDetails({super.key, this.propertyDetails}); + + static Route route(RouteSettings routeSettings) { + Map? arguments = routeSettings.arguments as Map?; + return BlurredRouter( + builder: (context) { + return AddPropertyDetails( + propertyDetails: arguments?['details'], + ); + }, + ); + } + + @override + State createState() => _AddPropertyDetailsState(); +} + +class _AddPropertyDetailsState extends State { + final GlobalKey _formKey = GlobalKey(); + + late final TextEditingController _propertyNameController = + TextEditingController(text: widget.propertyDetails?['name']); + late final TextEditingController _descriptionController = + TextEditingController(text: widget.propertyDetails?['desc']); + late final TextEditingController _cityNameController = + TextEditingController(text: widget.propertyDetails?['city']); + late final TextEditingController _stateNameController = + TextEditingController(text: widget.propertyDetails?['state']); + late final TextEditingController _countryNameController = + TextEditingController(text: widget.propertyDetails?['country']); + late final TextEditingController _latitudeController = + TextEditingController(text: widget.propertyDetails?['latitude']); + late final TextEditingController _longitudeController = + TextEditingController(text: widget.propertyDetails?['longitude']); + late final TextEditingController _addressController = + TextEditingController(text: widget.propertyDetails?['address']); + late final TextEditingController _priceController = + TextEditingController(text: widget.propertyDetails?['price']); + late final TextEditingController _clientAddressController = + TextEditingController(text: widget.propertyDetails?['client']); + + late final TextEditingController _videoLinkController = + TextEditingController(); + + bool isPrivateProperty = false; + + ///META DETAILS + late final TextEditingController metaTitleController = + TextEditingController(); + late final TextEditingController metaDescriptionController = + TextEditingController(); + late final TextEditingController metaKeywordController = + TextEditingController(); + + /// + Map propertyData = {}; + final PickImage _pickTitleImage = PickImage(); + final PickImage _propertiesImagePicker = PickImage(); + final PickImage _pick360deg = PickImage(); + final PickImage _pickMetaTitle = PickImage(); + List editPropertyImageList = []; + String titleImageURL = ""; + String metaImageUrl = ""; + String selectedRentType = "Monthly"; + List removedImageId = []; + int propertyType = 0; + + List mixedPropertyImageList = []; + + @override + void initState() { + titleImageURL = widget.propertyDetails?['titleImage'] ?? ""; + metaImageUrl = widget.propertyDetails?['allPropData']['meta_image'] ?? ""; + mixedPropertyImageList = + List.from(widget.propertyDetails?['images'] ?? []); + if ((widget.propertyDetails != null)) { + selectedRentType = widget.propertyDetails?['rentduration'] ?? "Monthly"; + isPrivateProperty = + widget.propertyDetails?['allPropData']?['is_premium'] ?? false; + } + + metaTitleController.text = + widget.propertyDetails?['allPropData']['meta_title'] ?? ""; + metaDescriptionController.text = + widget.propertyDetails?['allPropData']['meta_description'] ?? ""; + metaKeywordController.text = + widget.propertyDetails?['allPropData']['meta_keywords'] ?? ""; + _propertiesImagePicker.listener((images) { + try { + mixedPropertyImageList.addAll(List.from(images)); + } catch (e) {} + + setState(() {}); + }); + + _pickMetaTitle.listener((p0) { + metaImageUrl = ""; + if (mounted) setState(() {}); + }); + _pickTitleImage.listener((p0) { + titleImageURL = ""; + if (mounted) setState(() {}); + }); + super.initState(); + } + + void _onTapChooseLocation(FormFieldState state) async { + FocusManager.instance.primaryFocus?.unfocus(); + print("object ${widget.propertyDetails?['latitude']}"); + // return; + Map? placeMark = await Navigator.pushNamed( + context, Routes.chooseLocaitonMap, + arguments: { + "latitude": widget.propertyDetails?['latitude'] != null + ? double.parse(widget.propertyDetails?['latitude']) + : Hive.box(HiveKeys.userDetailsBox) + .get("latitude") + .toString() + .toDouble(), + "longitude": widget.propertyDetails?['longitude'] != null + ? double.parse(widget.propertyDetails?['longitude']) + : Hive.box(HiveKeys.userDetailsBox) + .get("longitude") + .toString() + .toDouble() + }) as Map?; + var latlng = placeMark?['latlng'] as LatLng?; + Placemark? place = placeMark?['place'] as Placemark?; + if (latlng != null && place != null) { + _latitudeController.text = latlng.latitude.toString(); + _longitudeController.text = latlng.longitude.toString(); + _cityNameController.text = place.locality ?? ""; + _countryNameController.text = place.country ?? ""; + _stateNameController.text = place.administrativeArea ?? ""; + _addressController.text = ""; + _addressController.text = getAddress(place); + + state.didChange(true); + } else { + // state.didChange(false); + } + } + + String getAddress(Placemark place) { + try { + String address = ""; + if (place.street == null && place.subLocality != null) { + address = place.subLocality!; + } else if (place.street == null && place.subLocality == null) { + address = ""; + } else { + address = "${place.street ?? ""},${place.subLocality ?? ""}"; + } + + return address; + } catch (e, st) { + throw Exception("$st"); + } + } + + void _onTapContinue() async { + File? titleImage; + File? v360Image; + File? metaTitle; + + if (_pickTitleImage.pickedFile != null) { + // final mimeType = lookupMimeType(_pickTitleImage.pickedFile!.path); + // var extension = mimeType!.split("/"); + + titleImage = _pickTitleImage.pickedFile; + } + + if (_pick360deg.pickedFile != null) { + // final mimeType = lookupMimeType(_pick360deg.pickedFile!.path); + // var extension = mimeType!.split("/"); + + v360Image = _pick360deg.pickedFile; + } + + if (_pickMetaTitle.pickedFile != null) { + metaTitle = _pickMetaTitle.pickedFile; + } + + var kb = formatFileSize(metaTitle?.lengthSync() ?? 0).kb; + + if (_formKey.currentState!.validate()) { + _formKey.currentState?.save(); + bool check = _checkIfLocationIsChosen(); + if (check == false) { + Future.delayed(Duration.zero, () { + UiUtils.showBlurredDialoge( + context, + sigmaX: 5, + sigmaY: 5, + dialoge: BlurredDialogBox( + svgImagePath: AppIcons.warning, + title: UiUtils.translate(context, "incomplete"), + showCancleButton: false, + onAccept: () async {}, + acceptTextColor: context.color.buttonColor, + content: Text( + UiUtils.translate(context, "addressError"), + ), + ), + ); + }); + + return; + } else if (titleImage == null && titleImageURL == "") { + Future.delayed(Duration.zero, () { + UiUtils.showBlurredDialoge(context, + sigmaX: 5, + sigmaY: 5, + dialoge: BlurredDialogBox( + svgImagePath: AppIcons.warning, + title: UiUtils.translate(context, "incomplete"), + showCancleButton: false, + acceptTextColor: context.color.buttonColor, + onAccept: () async { + // Navigator.pop(context); + }, + content: Text( + UiUtils.translate(context, "uploadImgMsgLbl"), + ), + )); + }); + return; + } else if (metaTitle == null && metaImageUrl == "") { + Future.delayed(Duration.zero, () { + UiUtils.showBlurredDialoge(context, + sigmaX: 5, + sigmaY: 5, + dialoge: BlurredDialogBox( + svgImagePath: AppIcons.warning, + title: UiUtils.translate(context, "incomplete"), + showCancleButton: false, + acceptTextColor: context.color.buttonColor, + onAccept: () async { + // Navigator.pop(context); + }, + content: Text( + "uploadMetaTitleImage".translate(context), + ), + )); + }); + return; + } + if (metaImageUrl == "" && kb > 300) { + HelperUtils.showSnackBarMessage( + context, "Meta image must be lower than 300KB in size"); + + return; + } + + var list = mixedPropertyImageList.map((e) { + if (e is File) { + return e; + } + }).toList() + ..removeWhere((element) => element == null); + + // return; + + propertyData.addAll({ + "title": _propertyNameController.text, + "description": _descriptionController.text, + "city": _cityNameController.text, + "state": _stateNameController.text, + "country": _countryNameController.text, + "latitude": _latitudeController.text, + "longitude": _longitudeController.text, + "address": _addressController.text, + "client_address": _clientAddressController.text, + "price": _priceController.text, + "title_image": titleImage, + "gallery_images": list, + "remove_gallery_images": removedImageId, + // "category_id": 1, + "category_id": widget.propertyDetails == null + ? (Constant.addProperty['category'] as Category).id + : widget.propertyDetails?['catId'], + // "property_type": 1, + "property_type": widget.propertyDetails == null + ? propertyType + : widget.propertyDetails?['propType'], + "threeD_image": v360Image, + "video_link": _videoLinkController.text, + "meta_image": metaTitle, + if ((widget.propertyDetails == null + ? propertyType + : widget.propertyDetails?['propType']) == + 1) + "rentduration": selectedRentType, + "meta_title": metaTitleController.text, + "meta_description": metaDescriptionController.text, + "meta_keywords": metaKeywordController.text, + "is_premium": isPrivateProperty + }); + + if (widget.propertyDetails?.containsKey("assign_facilities") ?? false) { + propertyData["assign_facilities"] = + widget.propertyDetails!['assign_facilities']; + } + if (widget.propertyDetails != null) { + propertyData['id'] = widget.propertyDetails?['id']; + propertyData['action_type'] = "0"; + } + + Future.delayed( + Duration.zero, + () { + _pickTitleImage.pauseSubscription(); + _pickMetaTitle.pauseSubscription(); + Navigator.pushNamed( + context, + Routes.setPropertyParametersScreen, + arguments: { + "details": propertyData, + "isUpdate": (widget.propertyDetails != null) + }, + ).then((value) { + _pickMetaTitle.resumeSubscription(); + _pickTitleImage.resumeSubscription(); + }); + }, + ); + } + } + + bool _checkIfLocationIsChosen() { + if (_cityNameController.text == "" || + _stateNameController.text == "" || + _countryNameController.text == "" || + _latitudeController.text == "" || + _longitudeController.text == "") { + return false; + } + return true; + } + + @override + void dispose() { + _pickMetaTitle.dispose(); + _propertyNameController.dispose(); + _descriptionController.dispose(); + _cityNameController.dispose(); + _stateNameController.dispose(); + _countryNameController.dispose(); + _latitudeController.dispose(); + _longitudeController.dispose(); + _addressController.dispose(); + _priceController.dispose(); + _clientAddressController.dispose(); + _videoLinkController.dispose(); + _pick360deg.dispose(); + _pickTitleImage.dispose(); + _propertiesImagePicker.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.primaryColor, + bottomNavigationBar: Container( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 20), + child: UiUtils.buildButton(context, + onPressed: _onTapContinue, + height: 48.rh(context), + fontSize: context.font.large, + buttonTitle: UiUtils.translate(context, "next")), + ), + ), + appBar: UiUtils.buildAppBar( + context, + title: widget.propertyDetails == null + ? UiUtils.translate(context, "ddPropertyLbl") + : UiUtils.translate(context, "updateProperty"), + actions: const [ + Text("2/4"), + SizedBox( + width: 14, + ), + ], + showBackButton: true, + ), + body: Form( + key: _formKey, + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag, + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("propertyType".translate(context)), + SizedBox( + height: 15.rh(context), + ), + buildPropertyTypeSelector(context), + SizedBox( + height: 15.rh(context), + ), + Text(UiUtils.translate(context, "propertyNameLbl")), + SizedBox( + height: 15.rh(context), + ), + CustomTextFormField( + controller: _propertyNameController, + validator: CustomTextFieldValidator.nullCheck, + action: TextInputAction.next, + hintText: UiUtils.translate(context, "propertyNameLbl"), + ), + SizedBox( + height: 15.rh(context), + ), + Text(UiUtils.translate(context, "descriptionLbl")), + SizedBox( + height: 15.rh(context), + ), + CustomTextFormField( + action: TextInputAction.next, + controller: _descriptionController, + validator: CustomTextFieldValidator.nullCheck, + hintText: UiUtils.translate(context, "writeSomething"), + maxLine: 100, + minLine: 6, + ), + SizedBox( + height: 15.rh(context), + ), + Row( + children: [ + const Text("Is Private Property?"), + const Spacer(), + CupertinoSwitch( + value: isPrivateProperty, + activeColor: context.color.tertiaryColor, + onChanged: (bool value) { + isPrivateProperty = value; + setState(() {}); + }, + ), + ], + ), + SizedBox( + height: 15.rh(context), + ), + SizedBox( + height: 35.rh(context), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + flex: 3, + child: + Text(UiUtils.translate(context, "addressLbl"))), + // const Spacer(), + Expanded( + flex: 3, + child: ChooseLocationFormField( + initialValue: false, + validator: (bool? value) { + //Check if it has already data so we will not validate it. + if ((widget.propertyDetails != null)) { + return null; + } + + if (value == true) { + return null; + } else { + return "Select location"; + } + }, + build: (state) { + return Container( + decoration: BoxDecoration( + // color: context.color.teritoryColor, + border: Border.all( + width: 1.5, + color: state.hasError + ? Colors.red + : Colors.transparent), + borderRadius: BorderRadius.circular(9)), + child: MaterialButton( + height: 30, + onPressed: () { + _onTapChooseLocation.call(state); + }, + child: FittedBox( + fit: BoxFit.fitWidth, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + UiUtils.getSvg(AppIcons.location, + color: + context.color.textLightColor), + const SizedBox( + width: 3, + ), + Text( + UiUtils.translate( + context, "chooseLocation"), + ) + .size(context.font.normal) + .color(context.color.tertiaryColor) + .underline(), + ], + ), + )), + ); + }, + ), + ) + ], + ), + ), + SizedBox( + height: 15.rh(context), + ), + CustomTextFormField( + action: TextInputAction.next, + controller: _cityNameController, + isReadOnly: false, + validator: CustomTextFieldValidator.nullCheck, + hintText: UiUtils.translate(context, "city"), + ), + SizedBox( + height: 10.rh(context), + ), + CustomTextFormField( + action: TextInputAction.next, + controller: _stateNameController, + isReadOnly: false, + validator: CustomTextFieldValidator.nullCheck, + hintText: UiUtils.translate(context, "state"), + ), + SizedBox( + height: 10.rh(context), + ), + CustomTextFormField( + action: TextInputAction.next, + controller: _countryNameController, + isReadOnly: false, + validator: CustomTextFieldValidator.nullCheck, + hintText: UiUtils.translate(context, "country"), + ), + SizedBox( + height: 10.rh(context), + ), + CustomTextFormField( + action: TextInputAction.next, + controller: _addressController, + hintText: UiUtils.translate(context, "addressLbl"), + maxLine: 100, + validator: CustomTextFieldValidator.nullCheck, + minLine: 4, + ), + SizedBox( + height: 10.rh(context), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + TextButton( + onPressed: () { + _clientAddressController.clear(); + _clientAddressController.text = + HiveUtils.getUserDetails().address ?? ""; + }, + style: ButtonStyle( + overlayColor: MaterialStatePropertyAll( + context.color.tertiaryColor.withOpacity(0.3))), + child: Text("useYourLocation".translate(context)) + .underline() + .color(context.color.tertiaryColor)), + CustomTextFormField( + action: TextInputAction.next, + controller: _clientAddressController, + validator: CustomTextFieldValidator.nullCheck, + hintText: UiUtils.translate(context, "clientaddressLbl"), + maxLine: 100, + minLine: 4, + ), + ], + ), + SizedBox( + height: 10.rh(context), + ), + if ((widget.propertyDetails == null + ? propertyType + : widget.propertyDetails?['propType']) == + 1) ...[ + Text(UiUtils.translate(context, "rentPrice")), + ] else ...[ + Text(UiUtils.translate(context, "price")), + ], + SizedBox( + height: 10.rh(context), + ), + Row( + children: [ + Expanded( + child: CustomTextFormField( + action: TextInputAction.next, + prefix: Text("${Constant.currencySymbol} "), + controller: _priceController, + formaters: [ + FilteringTextInputFormatter.allow( + RegExp(r'^\d+\.?\d*')), + ], + isReadOnly: false, + keyboard: TextInputType.number, + validator: CustomTextFieldValidator.nullCheck, + hintText: "00", + ), + ), + if ((widget.propertyDetails == null + ? propertyType + : widget.propertyDetails?['propType']) == + 1) ...[ + const SizedBox( + width: 5, + ), + Container( + decoration: BoxDecoration( + color: context.color.secondaryColor, + border: Border.all( + color: context.color.borderColor, width: 1.5), + borderRadius: BorderRadius.circular(10)), + child: Padding( + padding: const EdgeInsets.all(7.0), + child: DropdownButton( + value: selectedRentType, + dropdownColor: context.color.primaryColor, + underline: const SizedBox.shrink(), + items: [ + DropdownMenuItem( + value: "Daily", + child: Text( + "Daily".translate(context), + ), + ), + DropdownMenuItem( + value: "Monthly", + child: Text("Monthly".translate(context)), + ), + DropdownMenuItem( + value: "Quarterly", + child: Text("Quarterly".translate(context)), + ), + DropdownMenuItem( + value: "Yearly", + child: Text("Yearly".translate(context)), + ), + ], + onChanged: (value) { + selectedRentType = value ?? ""; + setState(() {}); + }, + ), + ), + ), + ] + ], + ), + SizedBox( + height: 10.rh(context), + ), + Row( + children: [ + Text(UiUtils.translate(context, "uploadPictures")), + const SizedBox( + width: 3, + ), + Text("maxSize".translate(context)) + .italic() + .size(context.font.small), + ], + ), + SizedBox( + height: 10.rh(context), + ), + Wrap( + children: [ + if (_pickTitleImage.pickedFile != null) ...[] else ...[], + titleImageListener(), + ], + ), + SizedBox( + height: 10.rh(context), + ), + Text(UiUtils.translate(context, "otherPictures")), + SizedBox( + height: 10.rh(context), + ), + SizedBox( + height: 10.rh(context), + ), + propertyImagesListener(), + SizedBox( + height: 10.rh(context), + ), + Text(UiUtils.translate(context, "additionals")), + SizedBox( + height: 10.rh(context), + ), + CustomTextFormField( + // prefix: Text("${Constant.currencySymbol} "), + controller: _videoLinkController, + // isReadOnly: widget.properyDetails != null, + hintText: "http://example.com/video.mp4", + ), + SizedBox( + height: 10.rh(context), + ), + DottedBorder( + color: context.color.textLightColor, + borderType: BorderType.RRect, + radius: const Radius.circular(12), + child: GestureDetector( + onTap: () { + _pick360deg.pick(pickMultiple: false); + }, + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10)), + alignment: Alignment.center, + height: 48.rh(context), + child: + Text(UiUtils.translate(context, "add360degPicture")), + ), + ), + ), + _pick360deg.listenChangesInUI((context, image) { + if (image != null) { + return Stack( + children: [ + Container( + width: 100, + height: 100, + margin: const EdgeInsets.all(5), + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10)), + child: Image.file( + image, + fit: BoxFit.cover, + )), + Positioned.fill( + child: GestureDetector( + onTap: () { + Navigator.push(context, BlurredRouter( + builder: (context) { + return PanaromaImageScreen( + imageUrl: image.path, + isFileImage: true, + ); + }, + )); + }, + child: Container( + width: 100, + margin: const EdgeInsets.all(5), + height: 100, + decoration: BoxDecoration( + color: + context.color.tertiaryColor.withOpacity( + 0.68, + ), + borderRadius: BorderRadius.circular(10)), + child: FittedBox( + fit: BoxFit.none, + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: context.color.secondaryColor, + ), + width: 60.rw(context), + height: 60.rh(context), + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 30.rh(context), + width: 40.rw(context), + child: UiUtils.getSvg( + AppIcons.v360Degree, + color: context + .color.textColorDark)), + Text(UiUtils.translate(context, "view")) + .color(context.color.textColorDark) + .size(context.font.small) + .bold() + ], + ), + ), + ), + ), + ), + ), + ), + ], + ); + } + + return Container(); + }), + SizedBox( + height: 15.rh(context), + ), + Text("Meta Details".translate(context)), + SizedBox( + height: 15.rh(context), + ), + CustomTextFormField( + controller: metaTitleController, + validator: CustomTextFieldValidator.nullCheck, + hintText: "Title".translate(context), + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: Text("metaTitleLength".translate(context)) + .size(context.font.small - 1.5) + .color(context.color.textLightColor), + ), + SizedBox( + height: 10.rh(context), + ), + CustomTextFormField( + controller: metaDescriptionController, + validator: CustomTextFieldValidator.nullCheck, + hintText: "Description".translate(context), + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: Text("metaDescriptionLength".translate(context)) + .size(context.font.small - 1.5) + .color(context.color.textLightColor), + ), + SizedBox( + height: 10.rh(context), + ), + CustomTextFormField( + controller: metaKeywordController, + hintText: "Keywords".translate(context), + validator: CustomTextFieldValidator.nullCheck, + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: Text("metaKeywordsLength".translate(context)) + .size(context.font.small - 1.5) + .color(context.color.textLightColor), + ), + SizedBox( + height: 10.rh(context), + ), + DottedBorder( + color: context.color.textLightColor, + borderType: BorderType.RRect, + radius: const Radius.circular(12), + child: GestureDetector( + onTap: () { + _pickMetaTitle.pick(pickMultiple: false); + }, + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10)), + alignment: Alignment.center, + height: 48.rh(context), + child: Text("Meta Image".translate(context)), + ), + ), + ), + Builder(builder: (context) { + Widget currentWidget = Container(); + if (metaImageUrl != "") { + currentWidget = GestureDetector( + onTap: () { + UiUtils.showFullScreenImage(context, + provider: NetworkImage(metaImageUrl)); + }, + child: Container( + width: 100, + height: 100, + margin: const EdgeInsets.all(5), + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10)), + child: Image.network( + metaImageUrl, + fit: BoxFit.cover, + ), + ), + ); + } + if (_pickMetaTitle.pickedFile != null) { + currentWidget = Container( + width: 100, + height: 100, + margin: const EdgeInsets.all(5), + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10)), + child: Image.file( + _pickMetaTitle.pickedFile!, + fit: BoxFit.cover, + )); + } + + return currentWidget; + }), + const SizedBox( + height: 30, + ), + ], + ), + ), + ), + ), + ); + } + + InputDecorator buildPropertyTypeSelector(BuildContext context) { + return InputDecorator( + decoration: InputDecoration( + hintStyle: TextStyle( + color: context.color.textColorDark.withOpacity(0.7), + fontSize: context.font.large), + filled: true, + fillColor: context.color.secondaryColor, + focusedBorder: OutlineInputBorder( + borderSide: + BorderSide(width: 1.5, color: context.color.tertiaryColor), + borderRadius: BorderRadius.circular(10)), + enabledBorder: OutlineInputBorder( + borderSide: + BorderSide(width: 1.5, color: context.color.borderColor), + borderRadius: BorderRadius.circular(10)), + border: OutlineInputBorder( + borderSide: + BorderSide(width: 1.5, color: context.color.borderColor), + borderRadius: BorderRadius.circular(10))), + child: DropdownButton( + value: propertyType, + isExpanded: true, + isDense: true, + borderRadius: BorderRadius.zero, + padding: EdgeInsets.zero, + underline: const SizedBox.shrink(), + items: const [ + DropdownMenuItem( + value: 0, + child: Text("Sell"), + ), + DropdownMenuItem( + value: 1, + child: Text("Rent"), + ) + ], + onTap: () {}, + onChanged: (int? value) { + propertyType = value!; + setState(() {}); + }, + ), + ); + } + + Widget propertyImagesListener() { + return _propertiesImagePicker.listenChangesInUI((context, file) { + Widget current = Container(); + + current = Wrap( + children: mixedPropertyImageList + .map((image) { + return Stack( + children: [ + GestureDetector( + onTap: () { + HelperUtils.unfocus(); + if (image is String) { + UiUtils.showFullScreenImage(context, + provider: NetworkImage(image)); + } else { + UiUtils.showFullScreenImage(context, + provider: FileImage(image)); + } + }, + child: Container( + width: 100, + height: 100, + margin: const EdgeInsets.all(5), + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + ), + child: ImageAdapter( + image: image, + )), + ), + + // Positioned( + // right: 5, + // top: 5, + // child: Container( + // width: 100, + // height: 100, + // margin: const EdgeInsets.all(5), + // clipBehavior: Clip.antiAlias, + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(10)), + // child: Icon(Icons.close)), + // ), + closeButton(context, () { + mixedPropertyImageList.remove(image); + + if (image is String) { + List properyDetail = + widget.propertyDetails?['gallary_with_id'] + as List; + var id = properyDetail + .where((element) => element.imageUrl == image) + .first + .id; + + removedImageId.add(id); + } + setState(() {}); + }), + + // child: GestureDetector( + // onTap: () { + // mixedPropertyImageList.remove(image); + // // removedImageId.add(); + // + // setState(() {}); + // }, + // child: Icon( + // Icons.close, + // color: context.color.secondaryColor, + // ), + // ), + // ) + ], + ); + }) + .toList() + .cast()); + + // if (propertyImageList.isEmpty && editPropertyImageList.isNotEmpty) { + // current = Wrap( + // children: editPropertyImageList + // .map((image) { + // log(image.runtimeType.toString()); + // return Stack( + // children: [ + // GestureDetector( + // onTap: () { + // HelperUtils.unfocus(); + // UiUtils.showFullScreenImage(context, + // provider: FileImage(image)); + // }, + // child: Container( + // width: 100, + // height: 100, + // margin: const EHEHEHEHEdgeInsets.all(5), + // clipBehavior: Clip.antiAlias, + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(10)), + // child: Image.network( + // image, + // fit: BoxFit.cover, + // )), + // ), + // Positioned( + // right: 5, + // top: 5, + // child: GestureDetector( + // onTap: () { + // editPropertyImageList.remove(image); + // // removedImageId.add(); + // + // setState(() {}); + // }, + // child: Icon( + // Icons.close, + // color: context.color.secondaryColor, + // ), + // ), + // ) + // ], + // ); + // }) + // .toList() + // .cast()); + // } + // + // if (file is List) { + // current = Wrap( + // children: propertyImageList + // .map((image) { + // return Stack( + // children: [ + // GestureDetector( + // onTap: () { + // HelperUtils.unfocus(); + // UiUtils.showFullScreenImage(context, + // provider: FileImage(image)); + // }, + // child: Container( + // width: 100, + // height: 100, + // margin: const EdgeInsets.all(5), + // clipBehavior: Clip.antiAlias, + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(10)), + // child: Image.file( + // image, + // fit: BoxFit.cover, + // )), + // ), + // closeButton(context, () { + // propertyImageList.remove(image); + // setState(() {}); + // }) + // ], + // ); + // }) + // .toList() + // .cast()); + // } + + return Wrap( + runAlignment: WrapAlignment.start, + children: [ + if (file == null && mixedPropertyImageList.isEmpty) + DottedBorder( + color: context.color.textLightColor, + borderType: BorderType.RRect, + radius: const Radius.circular(12), + child: GestureDetector( + onTap: () { + _propertiesImagePicker.pick(pickMultiple: true); + }, + child: Container( + clipBehavior: Clip.antiAlias, + decoration: + BoxDecoration(borderRadius: BorderRadius.circular(10)), + alignment: Alignment.center, + height: 48.rh(context), + child: Text(UiUtils.translate(context, "addOtherPicture")), + ), + ), + ), + current, + if (file != null || titleImageURL != "") + uploadPhotoCard(context, onTap: () { + _propertiesImagePicker.pick(pickMultiple: true); + }) + ], + ); + }); + } + + Widget titleImageListener() { + return Builder(builder: (context) { + Widget currentWidget = Container(); + if (titleImageURL != "") { + currentWidget = GestureDetector( + onTap: () { + UiUtils.showFullScreenImage(context, + provider: NetworkImage(titleImageURL)); + }, + child: Container( + width: 100, + height: 100, + margin: const EdgeInsets.all(5), + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)), + child: Image.network( + titleImageURL, + fit: BoxFit.cover, + ), + ), + ); + } + if (_pickTitleImage.pickedFile is File) { + currentWidget = GestureDetector( + onTap: () { + UiUtils.showFullScreenImage(context, + provider: FileImage(_pickTitleImage.pickedFile!)); + }, + child: Column( + children: [ + Container( + width: 100, + height: 100, + margin: const EdgeInsets.all(5), + clipBehavior: Clip.antiAlias, + decoration: + BoxDecoration(borderRadius: BorderRadius.circular(10)), + child: Image.file( + _pickTitleImage.pickedFile!, + fit: BoxFit.cover, + )), + ], + ), + ); + } + + return Wrap( + children: [ + if (_pickTitleImage.pickedFile == null && titleImageURL == "") + DottedBorder( + color: context.color.textLightColor, + borderType: BorderType.RRect, + radius: const Radius.circular(12), + child: GestureDetector( + onTap: () { + _pickTitleImage.pick(pickMultiple: false); + titleImageURL = ""; + setState(() {}); + }, + child: Container( + clipBehavior: Clip.antiAlias, + decoration: + BoxDecoration(borderRadius: BorderRadius.circular(10)), + alignment: Alignment.center, + height: 48.rh(context), + child: Text(UiUtils.translate(context, "addMainPicture")), + ), + ), + ), + Stack( + children: [ + currentWidget, + closeButton(context, () { + _pickTitleImage.clearImage(); + titleImageURL = ""; + setState(() {}); + }) + ], + ), + if (_pickTitleImage.pickedFile != null || titleImageURL != "") + uploadPhotoCard(context, onTap: () { + _pickTitleImage.resumeSubscription(); + _pickTitleImage.pick(pickMultiple: false); + _pickTitleImage.pauseSubscription(); + titleImageURL = ""; + setState(() {}); + }) + // GestureDetector( + // onTap: () { + // _pickTitleImage.resumeSubscription(); + // _pickTitleImage.pick(pickMultiple: false); + // _pickTitleImage.pauseSubscription(); + // titleImageURL = ""; + // setState(() {}); + // }, + // child: Container( + // width: 100, + // height: 100, + // margin: const EdgeInsets.all(5), + // clipBehavior: Clip.antiAlias, + // decoration: + // BoxDecoration(borderRadius: BorderRadius.circular(10)), + // child: DottedBorder( + // borderType: BorderType.RRect, + // radius: Radius.circular(10), + // child: Container( + // alignment: Alignment.center, + // child: Text("Upload \n Photo"), + // )), + // ), + // ), + ], + ); + }); + } +} + +Widget uploadPhotoCard(BuildContext context, {required Function onTap}) { + return GestureDetector( + onTap: () { + onTap.call(); + }, + child: Container( + width: 100, + height: 100, + margin: const EdgeInsets.all(5), + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)), + child: DottedBorder( + color: context.color.textColorDark.withOpacity(0.5), + borderType: BorderType.RRect, + radius: const Radius.circular(10), + child: Container( + alignment: Alignment.center, + child: Text("uploadPhoto".translate(context)), + )), + ), + ); +} + +PositionedDirectional closeButton(BuildContext context, Function onTap) { + return PositionedDirectional( + top: 6, + end: 6, + child: GestureDetector( + onTap: () { + onTap.call(); + }, + child: Container( + decoration: BoxDecoration( + color: context.color.primaryColor.withOpacity(0.7), + borderRadius: BorderRadius.circular(10)), + child: const Padding( + padding: EdgeInsets.all(4.0), + child: Icon( + Icons.close, + size: 24, + color: Colors.black, + ), + ), + ), + ), + ); +} + +class ChooseLocationFormField extends FormField { + ChooseLocationFormField( + {super.key, + FormFieldSetter? onSaved, + FormFieldValidator? validator, + bool? initialValue, + required Widget Function(FormFieldState state) build, + bool autovalidateMode = false}) + : super( + onSaved: onSaved, + validator: validator, + initialValue: initialValue, + builder: (FormFieldState state) { + return build(state); + }); +} + +class ImageAdapter extends StatelessWidget { + final dynamic image; + ImageAdapter({super.key, this.image}); + + @override + Widget build(BuildContext context) { + if (image is String) { + return Image.network( + image, + fit: BoxFit.cover, + ); + } else if (image is File) { + return Image.file( + image, + fit: BoxFit.cover, + ); + } + return Container(); + } +} diff --git a/lib/Ui/screens/proprties/AddProperyScreens/property_success.dart b/lib/Ui/screens/proprties/AddProperyScreens/property_success.dart new file mode 100644 index 0000000..0b7abea --- /dev/null +++ b/lib/Ui/screens/proprties/AddProperyScreens/property_success.dart @@ -0,0 +1,84 @@ +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +import '../../../../app/routes.dart'; +import '../../../../data/model/property_model.dart'; +import '../../../../utils/AppIcon.dart'; +import '../../../../utils/helper_utils.dart'; + +class PropertyAddSuccess extends StatelessWidget { + final PropertyModel model; + const PropertyAddSuccess({super.key, required this.model}); + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + Navigator.popUntil(context, (Route route) => route.isFirst); + return false; + }, + child: Scaffold( + backgroundColor: context.color.backgroundColor, + body: SizedBox( + width: context.screenWidth, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset(AppIcons.propertySubmittedc), + const SizedBox( + height: 32, + ), + Text("congratulations".translate(context)) + .size(context.font.extraLarge) + .bold() + .color(context.color.tertiaryColor), + const SizedBox( + height: 18, + ), + Text("submittedSuccess".translate(context)) + .centerAlign() + .size(context.font.larger), + const SizedBox( + height: 68, + ), + MaterialButton( + onPressed: () { + HelperUtils.goToNextPage( + Routes.propertyDetails, + context, + false, + args: { + 'propertyData': model, + 'propertiesList': [], + 'fromMyProperty': false, + "fromSuccess": true + }, + ); + }, + height: 48, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + side: BorderSide(color: context.color.tertiaryColor)), + color: context.color.backgroundColor, + child: Text( + "previewProperty".translate(context), + ) + .size(context.font.larger) + .color(context.color.tertiaryColor), + ), + const SizedBox( + height: 15, + ), + GestureDetector( + onTap: () { + Navigator.popUntil(context, (route) => route.isFirst); + }, + child: Text("backToHome".translate(context)).underline()) + ], + ), + ), + )); + } +} diff --git a/lib/Ui/screens/proprties/AddProperyScreens/select_outdoor_facility.dart b/lib/Ui/screens/proprties/AddProperyScreens/select_outdoor_facility.dart new file mode 100644 index 0000000..adfbb98 --- /dev/null +++ b/lib/Ui/screens/proprties/AddProperyScreens/select_outdoor_facility.dart @@ -0,0 +1,597 @@ +// ignore_for_file: invalid_use_of_visible_for_testing_member + +// ignore_for_file: invalid_use_of_visible_for_testing_member + +import 'package:ebroker/Ui/screens/widgets/custom_text_form_field.dart'; +import 'package:ebroker/data/cubits/outdoorfacility/fetch_outdoor_facility_list.dart'; +import 'package:ebroker/data/model/outdoor_facility.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../../data/cubits/property/create_property_cubit.dart'; +import '../../../../utils/constant.dart'; +import '../../../../utils/sliver_grid_delegate_with_fixed_cross_axis_count_and_fixed_height.dart'; +import '../../widgets/AnimatedRoutes/blur_page_route.dart'; + +class SelectOutdoorFacility extends StatefulWidget { + final Map? apiParameters; + + const SelectOutdoorFacility({super.key, required this.apiParameters}); + + static Route route(RouteSettings settings) { + Map _apiParameters = + settings.arguments as Map; + return BlurredRouter( + builder: (context) { + return SelectOutdoorFacility( + apiParameters: _apiParameters, + ); + }, + ); + } + + @override + State createState() => _SelectOutdoorFacilityState(); +} + +class _SelectOutdoorFacilityState extends State { + final ValueNotifier> _selectedIdsList = ValueNotifier([]); + List facilityList = []; + Map distanceFieldList = {}; + final GlobalKey _formKey = GlobalKey(); + var _oldSize; + @override + void initState() { + List facilities = []; + facilities = widget.apiParameters?['assign_facilities'] ?? []; + + // context.read().fetchIfFailed(); + facilityList = context.read().getList(); + + setState(() {}); + _selectedIdsList.addListener(() { + _selectedIdsList.value.forEach((element) { + if (!distanceFieldList.keys.contains(element)) { + if (widget.apiParameters?['isUpdate'] ?? false) { + List match = + facilities.where((x) => x.facilityId == element).toList(); + + if (match.isNotEmpty) { + distanceFieldList[element] = + TextEditingController(text: match.first.distance.toString()); + } else { + distanceFieldList[element] = TextEditingController(); + } + } else { + distanceFieldList[element] = TextEditingController(); + } + } + }); + setState(() {}); + }); + + if (widget.apiParameters?['isUpdate'] ?? false) { + facilities.forEach((element) { + if (!_selectedIdsList.value.contains(element)) { + _selectedIdsList.value.add(element.facilityId!); + _selectedIdsList.notifyListeners(); + } + }); + } + super.initState(); + } + + Map assembleOutdoorFacility() { + Map facilitymap = {}; + for (var i = 0; i < distanceFieldList.entries.length; i++) { + MapEntry element = distanceFieldList.entries.elementAt(i); + + facilitymap.addAll({ + "facilities[$i][facility_id]": element.key, + "facilities[$i][distance]": element.value.text + }); + } + + return facilitymap; + } + + OutdoorFacility getSelectedFacility(int id) { + try { + return facilityList + .where((OutdoorFacility element) => element.id == id) + .first; + } catch (e) { + throw "$e"; + } + } + + @override + Widget build(BuildContext context) { + // log(facilityList.toString()); + bool fetchInProgress = (context.watch().state + is FetchOutdoorFacilityListInProgress); + bool fetchFails = (context.watch().state + is FetchOutdoorFacilityListInProgress); + + return BlocListener( + listener: (context, state) { + if (state is FetchOutdoorFacilityListSucess) { + facilityList = + context.read().getList(); + setState(() {}); + } + }, + child: Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: UiUtils.buildAppBar(context, + showBackButton: true, + actions: const [ + Text("4/4"), + SizedBox( + width: 14, + ), + ], + title: "selectNearestPlaces".translate(context)), + bottomNavigationBar: BottomAppBar( + child: GestureDetector( + onTap: () { + distanceFieldList.forEach((element, v) {}); + }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: UiUtils.buildButton( + context, + onPressed: () { + Map? parameters = widget.apiParameters; + + ///adding facility data to api payload + parameters!.addAll(assembleOutdoorFacility()); + parameters + ..remove("assign_facilities") + ..remove("isUpdate"); + if (_formKey.currentState!.validate()) { + context + .read() + .create(parameters: parameters); + } + }, + buttonTitle: widget.apiParameters?['action_type'] == "0" + ? UiUtils.translate(context, "update") + : UiUtils.translate(context, "submitProperty"), + ), + ), + ), + ), + body: Builder(builder: (context) { + if (fetchInProgress) { + return Center( + child: UiUtils.progress(), + ); + } + + if (fetchFails) { + return Center( + child: Text("Something Went wrong"), + ); + } + return SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: + const EdgeInsetsDirectional.fromSTEB(15.0, 10, 15, 0), + child: Text("selectPlaces".translate(context)), + ), + SizedBox( + height: 12, + ), + BlocBuilder( + builder: (context, state) { + if (state is FetchOutdoorFacilityListFailure) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 15.0), + child: Text(state.error.toString()), + ); + } + if (state is FetchOutdoorFacilityListSucess) { + return ValueListenableBuilder>( + valueListenable: _selectedIdsList, + builder: (context, List value, child) { + return OutdoorFacilityTable( + length: state.outdoorFacilityList.length, + child: (index) { + OutdoorFacility outdoorFacilityList = + state.outdoorFacilityList[index]; + + return buildTypeCard( + index, context, outdoorFacilityList, + onSelect: (id) { + if (_selectedIdsList.value.contains(id)) { + _selectedIdsList.value.remove(id); + + ///Dispose and remove from object + distanceFieldList[id]?.dispose(); + distanceFieldList.remove(id); + _selectedIdsList.notifyListeners(); + } else { + _selectedIdsList.value.add(id); + _selectedIdsList.notifyListeners(); + } + }, + isSelected: + value.contains(outdoorFacilityList.id)); + }, + ); + + return GridView.builder( + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.all(15), + shrinkWrap: true, + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + mainAxisSpacing: 5, + crossAxisSpacing: 5, + crossAxisCount: 4), + itemCount: state.outdoorFacilityList.length, + itemBuilder: (context, index) { + OutdoorFacility outdoorFacilityList = + state.outdoorFacilityList[index]; + + return buildTypeCard( + index, context, outdoorFacilityList, + onSelect: (id) { + if (_selectedIdsList.value.contains(id)) { + _selectedIdsList.value.remove(id); + + ///Dispose and remove from object + distanceFieldList[id]?.dispose(); + distanceFieldList.remove(id); + _selectedIdsList.notifyListeners(); + } else { + _selectedIdsList.value.add(id); + _selectedIdsList.notifyListeners(); + } + }, + isSelected: + value.contains(outdoorFacilityList.id)); + }, + ); + }); + } + + return Container(); + }, + ), + ValueListenableBuilder( + valueListenable: _selectedIdsList, + builder: (context, value, child) { + return Padding( + padding: const EdgeInsets.all(15.0), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _selectedIdsList.value.isEmpty + ? const SizedBox.shrink() + : Text("selectedItems".translate(context)), + const SizedBox( + height: 10, + ), + ...List.generate(_selectedIdsList.value.length, + (index) { + if (fetchInProgress) { + return const SizedBox.shrink(); + } + + OutdoorFacility facility = getSelectedFacility( + _selectedIdsList.value[index]); + return Padding( + padding: + const EdgeInsets.symmetric(vertical: 3.0), + child: OutdoorFacilityDistanceField( + facility: facility, + controller: distanceFieldList[facility.id]!, + ), + ); + }) + ], + ), + ), + ); + }, + ), + ], + ), + ); + }), + ), + ); + } + + Widget buildTypeCard( + int index, BuildContext context, OutdoorFacility facility, + {required bool isSelected, required Function(int id) onSelect}) { + return GestureDetector( + onTap: () { + onSelect.call(facility.id!); + }, + child: Container( + decoration: BoxDecoration( + color: isSelected + ? context.color.tertiaryColor + : context.color.secondaryColor, + borderRadius: BorderRadius.circular(10), + boxShadow: isSelected + ? [ + BoxShadow( + offset: const Offset(1, 3), + blurRadius: 6, + color: context.color.tertiaryColor.withOpacity(0.2), + ) + ] + : null, + border: isSelected + ? null + : Border.all(color: context.color.borderColor, width: 1.5)), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: 20.rh(context), + width: 20.rw(context), + child: UiUtils.imageType(facility.image!, + color: isSelected + ? context.color.secondaryColor + : (Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null)), + ), + Padding( + padding: const EdgeInsets.all(2.0), + child: Text( + facility.name!, + textAlign: TextAlign.center, + ).color(isSelected + ? context.color.secondaryColor + : context.color.tertiaryColor), + ) + ], + ), + ), + ); + } +} + +class OutdoorFacilityDistanceField extends StatelessWidget { + final TextEditingController controller; + + const OutdoorFacilityDistanceField({ + super.key, + required this.facility, + required this.controller, + }); + + final OutdoorFacility facility; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Container( + width: 48.rw(context), + height: 48.rh(context), + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: FittedBox( + fit: BoxFit.none, + child: SizedBox( + height: 24, + width: 24, + child: UiUtils.imageType(facility.image ?? "", + color: context.color.tertiaryColor, fit: BoxFit.cover))), + ), + const SizedBox( + width: 10, + ), + Expanded(child: Text(facility.name ?? "")), + Expanded( + child: CustomTextFormField( + keyboard: TextInputType.number, + validator: CustomTextFieldValidator.nullCheck, + hintText: "00", + formaters: [ + FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d*')), + ], + controller: controller, + dense: true, + suffix: SizedBox( + width: 5, + child: Center( + child: const Text("KM").color(context.color.textLightColor)), + ), + ), + ), + const SizedBox( + width: 10, + ), + ], + ); + } +} + +class OutdoorFacilityWithController { + final OutdoorFacility facility; + final TextEditingController controller; + + const OutdoorFacilityWithController({ + required this.facility, + required this.controller, + }); + + @override + String toString() { + return 'OutdoorFacilityWithController{' + + ' facility: $facility,' + + ' controller: $controller,' + + '}'; + } + + OutdoorFacilityWithController copyWith({ + OutdoorFacility? facility, + TextEditingController? controller, + }) { + return OutdoorFacilityWithController( + facility: facility ?? this.facility, + controller: controller ?? this.controller, + ); + } + + Map toMap() { + return { + 'facility': this.facility, + 'controller': this.controller, + }; + } + + factory OutdoorFacilityWithController.fromMap(Map map) { + return OutdoorFacilityWithController( + facility: map['facility'] as OutdoorFacility, + controller: map['controller'] as TextEditingController, + ); + } +} + +class OutdoorFacilityTable extends StatefulWidget { + final int length; + const OutdoorFacilityTable( + {super.key, required this.child, required this.length}); + final Widget Function(int index) child; + @override + State createState() => _OutdoorFacilityTableState(); +} + +class _OutdoorFacilityTableState extends State { + Size? _oldSize; + PageController _pageController = PageController(); + int rowCount = 3; + Map? sizeMap = {}; + int colCount = 3; + late int totalData = widget.length; + int itemsPerPage = 9; + int selectedPage = 0; + @override + Widget build(BuildContext context) { + return Column( + children: [ + Container( + height: (sizeMap?.isEmpty ?? true) + ? 290 + : (sizeMap?[Key(selectedPage.toString())]?.height ?? 290), + child: PageView.builder( + controller: _pageController, + pageSnapping: true, + onPageChanged: (value) { + selectedPage = value; + setState(() {}); + }, + physics: const BouncingScrollPhysics(), + itemCount: (totalData / itemsPerPage).ceil(), + itemBuilder: (context, pageIndex) { + final startIndex = pageIndex * itemsPerPage; + final endIndex = (startIndex + itemsPerPage) > totalData + ? totalData + : (startIndex + itemsPerPage); + + final gridData = List.generate( + endIndex - startIndex, + (index) { + return 'Data ${(startIndex + index + 1)}'; + }, + ); + + Key pageKey = Key(pageIndex.toString()); + return GridView.builder( + shrinkWrap: true, + key: pageKey, + physics: BouncingScrollPhysics(), + padding: EdgeInsets.symmetric(horizontal: 14), + gridDelegate: + SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight( + crossAxisCount: colCount, + crossAxisSpacing: 13, + mainAxisSpacing: 13, + height: 83, + ), + itemCount: gridData.length, + itemBuilder: (BuildContext c, int index) { + getGridSize(c, pageIndex, pageKey); + final dataIndex = startIndex + index; + return widget.child.call(dataIndex); + }, + ); + }, + ), + ), + SizedBox( + height: 10, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ...List.generate((totalData / itemsPerPage).ceil(), (index) { + bool isSelected = selectedPage == index; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 3), + child: Container( + width: isSelected ? 24 : 8, + height: 8, + decoration: BoxDecoration( + border: isSelected + ? Border() + : Border.all(color: context.color.textColorDark), + color: isSelected + ? context.color.tertiaryColor + : Colors.transparent, + borderRadius: BorderRadius.circular(10), + ), + ), + ); + }) + ], + ) + ], + ); + } + + getGridSize(BuildContext context, int pageIndex, Key pageKey) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + if (!context.mounted) { + return; + } + Size size = ((context as SliverMultiBoxAdaptorElement).renderObject + as RenderSliverGrid) + .getAbsoluteSize(); + + if (_oldSize != size) { + if (Key(pageIndex.toString()) == pageKey) { + sizeMap?[pageKey] = size; + _oldSize = size; + setState(() {}); + } + } + }); + } +} diff --git a/lib/Ui/screens/proprties/AddProperyScreens/select_type_of_property.dart b/lib/Ui/screens/proprties/AddProperyScreens/select_type_of_property.dart new file mode 100644 index 0000000..04a8836 --- /dev/null +++ b/lib/Ui/screens/proprties/AddProperyScreens/select_type_of_property.dart @@ -0,0 +1,308 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../../app/routes.dart'; +import '../../../../data/Repositories/subscription_repository.dart'; +import '../../../../data/cubits/category/fetch_category_cubit.dart'; +import '../../../../data/cubits/outdoorfacility/fetch_outdoor_facility_list.dart'; +import '../../../../data/cubits/subscription/get_subsctiption_package_limits_cubit.dart'; +import '../../../../data/helper/widgets.dart'; +import '../../../../data/model/category.dart'; +import '../../../../utils/Extensions/extensions.dart'; +import '../../../../utils/constant.dart'; +import '../../../../utils/helper_utils.dart'; +import '../../../../utils/responsiveSize.dart'; +import '../../../../utils/ui_utils.dart'; +import '../../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../../widgets/blurred_dialoge_box.dart'; + +enum PropertyAddType { project, property } + +class SelectPropertyType extends StatefulWidget { + final PropertyAddType type; + const SelectPropertyType({super.key, required this.type}); + + static Route route(RouteSettings settings) { + Map? arguments = settings.arguments as Map?; + return BlurredRouter( + builder: (context) { + return SelectPropertyType( + type: arguments?['type'], + ); + }, + ); + } + + @override + State createState() => _SelectPropertyTypeState(); +} + +class _SelectPropertyTypeState extends State { + int? selectedIndex; + Category? selectedCategory; + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + Widgets.showLoader(context); + }); + // penyebab error tidak bisa add property + + Future.delayed( + Duration(seconds: 2), + () { + context + .read() + .getLimits(SubscriptionLimitType.property); + }, + ); + context.read().fetch(); + } + + void _openSubscriptionScreen() { + Navigator.pop(context); + Navigator.pushNamed( + context, + Routes.subscriptionPackageListRoute, + ).then((value) { + // Navigator.pop(context); + context + .read() + .getLimits(SubscriptionLimitType.property); + + // Navigator.pop(context); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.primaryColor, + appBar: UiUtils.buildAppBar( + context, + title: widget.type == PropertyAddType.property + ? "ddPropertyLbl".translate(context) + : "projectType".translate(context), + actions: const [ + Text("1/4"), + SizedBox( + width: 14, + ), + ], + showBackButton: true, + ), + bottomNavigationBar: Container( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 20), + child: UiUtils.buildButton(context, + disabledColor: Colors.grey, + onTapDisabledButton: () { + HelperUtils.showSnackBarMessage( + context, "pleaseSelectCategory".translate(context), + isFloating: true); + }, + disabled: selectedCategory == null, + onPressed: () { + var state = + context.read().state; + if (state is! GetSubsctiptionPackageLimitsInProgress) { + Constant.addProperty.addAll({"category": selectedCategory}); + + if (selectedCategory != null) { + // Navigator.push(context, MaterialPageRoute(builder: (context) { + // return SelectOutdoorFacility(); + // },)); + //TODO: + + if (widget.type == PropertyAddType.property) { + Navigator.pushNamed( + context, Routes.addPropertyDetailsScreen); + } else { + Navigator.pushNamed(context, Routes.addProjectDetails); + } + } + } + }, + height: 48.rh(context), + fontSize: context.font.large, + buttonTitle: UiUtils.translate(context, "continue")), + ), + ), + body: BlocListener( + bloc: context.read(), + listener: (context, state) { + if (state is GetSubsctiptionPackageLimitsInProgress) { + Widgets.showLoader(context); + } + if (state is GetSubsctiptionPackageLimitsFailure) { + Widgets.hideLoder(context); + HelperUtils.showSnackBarMessage( + context, + state.errorMessage.firstUpperCase(), + onClose: () { + Navigator.pop(context); + }, + ); + } + if (state is GetSubsctiptionPackageLimitsSuccess) { + if (state.packageLimit.hasPackage == false) { + Widgets.hideLoder(context); + + UiUtils.showBlurredDialoge(context, + sigmaX: 3, + sigmaY: 3, + dialoge: BlurredDialogBox( + isAcceptContainesPush: true, + acceptButtonName: UiUtils.translate(context, "subscribe"), + backAllowedButton: false, + title: UiUtils.translate(context, "packageNotValid"), + content: Text( + UiUtils.translate(context, "packageNotForProperty"), + ), + onCancel: () { + Navigator.pop(context); + }, + onAccept: () async { + _openSubscriptionScreen(); + }, + )); + } else { + Widgets.hideLoder(context); + } + //we are making sure that total number is not in string, if it will be Unlimited so we dont have to show nay dialoge + + // if (state.packageLimit.totalLimitOfProperty.runtimeType == int) { + // if (state.packageLimit.usedLimitOfProperty >= + // state.packageLimit.totalLimitOfProperty) { + // UiUtils.showBlurredDialoge(context, + // sigmaX: 3, + // sigmaY: 3, + // dialoge: BlurredDialogBox( + // isAcceptContainesPush: true, + // acceptButtonName: + // UiUtils.getTranslatedLabel(context, "subscribe"), + // backAllowedButton: false, + // title: UiUtils.getTranslatedLabel( + // context, "yourPackageReachedLimit"), + // content: Text( + // UiUtils.getTranslatedLabel( + // context, "yourPackageReachedLimitDescription"), + // ), + // onCancel: () { + // Navigator.pop(context); + // }, + // onAccept: () async { + // _openSubscriptionScreen(); + // }, + // )); + // } + // } + } + }, + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsetsDirectional.only( + start: 20.0, end: 20, top: 20), + child: Text(UiUtils.translate(context, "typeOfProperty")) + .color(context.color.textColorDark), + ), + BlocBuilder( + builder: (context, state) { + if (state is FetchCategoryInProgress) {} + if (state is FetchCategoryFailure) { + return Center( + child: Text(state.errorMessage), + ); + } + if (state is FetchCategorySuccess) { + return GridView.builder( + itemCount: state.categories.length, + shrinkWrap: true, + padding: const EdgeInsets.all(20.0), + physics: const NeverScrollableScrollPhysics(), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + mainAxisSpacing: 10, + crossAxisSpacing: 10, + crossAxisCount: 3), + itemBuilder: (context, index) { + return buildTypeCard( + index, context, state.categories[index]); + }, + ); + } + return Container(); + }, + ) + ], + ), + ), + ), + ); + } + + Widget buildTypeCard(int index, BuildContext context, Category category) { + return GestureDetector( + onTap: () { + selectedCategory = category; + selectedIndex = index; + setState(() {}); + }, + child: Container( + decoration: BoxDecoration( + color: (selectedIndex == index) + ? context.color.tertiaryColor + : context.color.secondaryColor, + borderRadius: BorderRadius.circular(10), + boxShadow: (selectedIndex == index) + ? [ + BoxShadow( + offset: const Offset(1, 2), + blurRadius: 5, + color: context.color.tertiaryColor) + ] + : null, + border: (selectedIndex == index) + ? null + : Border.all(color: context.color.borderColor, width: 1.5)), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Icon( + // Icons.house, + // color: selectedIndex == index + // ? context.color.secondaryColor + // : context.color.teritoryColor, + // ), + SizedBox( + height: 25.rh(context), + width: 25.rw(context), + child: UiUtils.imageType(category.image!, + color: selectedIndex == index + ? context.color.secondaryColor + : (Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null)), + ), + + Padding( + padding: const EdgeInsets.all(2.0), + child: Text( + category.category!, + textAlign: TextAlign.center, + ).color(selectedIndex == index + ? context.color.secondaryColor + : context.color.tertiaryColor), + ) + ], + ), + ), + ); + } +} diff --git a/lib/Ui/screens/proprties/AddProperyScreens/set_property_parameters.dart b/lib/Ui/screens/proprties/AddProperyScreens/set_property_parameters.dart new file mode 100644 index 0000000..1c1692e --- /dev/null +++ b/lib/Ui/screens/proprties/AddProperyScreens/set_property_parameters.dart @@ -0,0 +1,363 @@ +// ignore_for_file: depend_on_referenced_packages + +import 'dart:collection'; +import 'dart:io'; + +import 'package:collection/collection.dart'; +import 'package:dio/dio.dart'; +import 'package:ebroker/Ui/screens/proprties/AddProperyScreens/property_success.dart'; +import 'package:ebroker/app/routes.dart'; +import 'package:ebroker/data/cubits/Utility/proeprty_edit_global.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:http_parser/http_parser.dart' as h; +import 'package:mime/mime.dart'; + +import '../../../../data/cubits/property/create_property_cubit.dart'; +import '../../../../data/cubits/property/fetch_my_properties_cubit.dart'; +import '../../../../data/helper/widgets.dart'; +import '../../../../data/model/property_model.dart'; +import '../../../../utils/Extensions/extensions.dart'; +import '../../../../utils/constant.dart'; +import '../../../../utils/helper_utils.dart'; +import '../../../../utils/responsiveSize.dart'; +import '../../../../utils/ui_utils.dart'; +import '../../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../../widgets/AnimatedRoutes/scale_up_route.dart'; +import '../../widgets/DynamicField/dynamic_field.dart'; +import '../Property tab/sell_rent_screen.dart'; + +class SetProeprtyParametersScreen extends StatefulWidget { + final Map propertyDetails; + final bool isUpdate; + const SetProeprtyParametersScreen( + {super.key, required this.propertyDetails, required this.isUpdate}); + static Route route(RouteSettings settings) { + Map? argument = settings.arguments as Map?; + + return BlurredRouter( + builder: (context) { + return SetProeprtyParametersScreen( + propertyDetails: argument?['details'], + isUpdate: argument?['isUpdate'], + ); + }, + ); + } + + @override + State createState() => + _SetProeprtyParametersScreenState(); +} + +class _SetProeprtyParametersScreenState + extends State + with AutomaticKeepAliveClientMixin { + List disposableFields = []; + final GlobalKey _formKey = GlobalKey(); + List galleryImage = []; + File? titleImage; + File? t360degImage; + File? meta_image; + Map? apiParameters; + var paramaeterUI = []; + @override + void initState() { + apiParameters = Map.from(widget.propertyDetails); + galleryImage = apiParameters!['gallery_images']; + titleImage = apiParameters!['title_image']; + t360degImage = apiParameters!['threeD_image']; + meta_image = apiParameters!['meta_image']; + Future.delayed( + Duration.zero, + () { + paramaeterUI = (Constant + .addProperty['category']?.parameterTypes!['parameters'] as List) + .mapIndexed((index, parameter) { + var parm = parameter; + if (parameter is! Map) { + parm = (parameter as Parameter).toMap(); + } + return Padding( + padding: EdgeInsets.only(top: index == 0 ? 0 : 10, bottom: 10), + child: buildDynamicField(parm, index), + ); + }) + .toList() + .cast(); + setState(() {}); + }, + ); + super.initState(); + } + + Widget buildDynamicField(Map parameter, int index) { + ///Initital Container to assign + Widget dynamicField = Container(); + + ///This is factory class it will check type and it will return field class accordingly + AbstractField field = + FieldFactory.getField(context, parameter['type_of_parameter']); + // if (widget.isUpdate) { + // AbstractField.fieldsData.addAll( + // { + // "parameters[$index][parameter_id]": parameter['id'], + // "parameters[$index][value]": parameter['value'] + // }, + // ); + // } + + ///Same like Bloc State management we check if field is AbstractDropdown, So we can apply additional configuration or add data to it + if (field is AbstractDropdown) { + var selected = parameter['value']; + if (selected == "") { + selected = (parameter['type_values'] as List).first; + } + dynamicField = field + .setItems(parameter['type_values']) + .setSelectedItem(selected) + .createField( + parameter, + ); + } else if (field is AbstractTextField) { + dynamicField = field.createField(parameter); + } else if (field is AbstractNumberField) { + dynamicField = field.createField(parameter); + } else if (field is AbstractRadioButton) { + dynamicField = + field.setValues(parameter['type_values']).createField(parameter); + } else if (field is AbstractTextAreaField) { + dynamicField = field.createField(parameter); + } else if (field is AbstractCheckBoxButton) { + dynamicField = field + .setCheckBoxValues(parameter['type_values']) + .createField(parameter); + // disposableFields.add(field.checked); + } else if (field is AbstractPickFileButton) { + dynamicField = field.createField(parameter); + // field.filePicked.value + } + + ///Returning field + return dynamicField; + } + + ///This will convert {0:Demo} to it's required format here we have assigned Parameter id : value, before. + Map assembleDynamicFieldsParameters() { + Map parameters = {}; + + Map fieldsData = AbstractField.fieldsData; + for (var i = 0; i < fieldsData.entries.length; i++) { + MapEntry element = fieldsData.entries.elementAt(i); + var value = element.value; + if (value is LinkedHashMap) { + value = (value).toString(); + } + if (value == null) { + continue; + } + parameters.addAll({ + "parameters[$i][parameter_id]": element.key, + "parameters[$i][value]": value + }); + } + return parameters; + } + + List buildFields() { + if (Constant.addProperty['category'] == null) { + return [Container()]; + } + + ///Loop parameters + return paramaeterUI.cast(); + } + + void disposeDynamicFieldsValueControllers() { + for (var element in disposableFields) { + element.dispose(); + } + } + + @override + void dispose() { + disposeDynamicFieldsValueControllers(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + return Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: UiUtils.buildAppBar( + context, + showBackButton: true, + actions: const [ + Text("3/4"), + SizedBox( + width: 14, + ), + ], + title: widget.isUpdate + ? UiUtils.translate(context, "updateProperty") + : UiUtils.translate(context, "ddPropertyLbl"), + ), + bottomNavigationBar: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 7), + child: UiUtils.buildButton( + context, + height: 48.rh(context), + onPressed: () async { + // if (_formKey.currentState!.validate() == false) return; + + //TODO: TODO + apiParameters!.addAll(assembleDynamicFieldsParameters()); + + /// Multipartimage of gallery images + List gallery = []; + await Future.forEach( + galleryImage, + (dynamic item) async { + var multipartFile = await MultipartFile.fromFile(item.path); + if (!multipartFile.isFinalized) { + gallery.add(multipartFile); + } + }, + ); + apiParameters!['gallery_images'] = gallery; + + if (titleImage != null) { + ///Multipart image of title image + final mimeType = lookupMimeType((titleImage as File).path); + var extension = mimeType!.split("/"); + apiParameters!['title_image'] = await MultipartFile.fromFile( + (titleImage as File).path, + contentType: h.MediaType('image', extension[1]), + filename: (titleImage as File).path.split("/").last); + } + + //set 360 deg image + + if (t360degImage != null) { + final mimeType = lookupMimeType(t360degImage!.path); + var extension = mimeType!.split("/"); + + apiParameters!['threeD_image'] = await MultipartFile.fromFile( + t360degImage?.path ?? "", + contentType: h.MediaType('image', extension[1]), + filename: t360degImage?.path.split("/").last); + } + + if (meta_image != null) { + final mimeType = lookupMimeType(meta_image!.path); + List extension = mimeType!.split("/"); + apiParameters!['meta_image'] = await MultipartFile.fromFile( + meta_image?.path ?? "", + contentType: h.MediaType('image', extension[1]), + filename: meta_image?.path.split("/").last); + } + + Future.delayed( + Duration.zero, + () { + // / if (Constant.isDemoModeOn) { + // / HelperUtils.showSnackBarMessage( + // / context, + // UiUtils.getTranslatedLabel( + // context, "thisActionNotValidDemo")); + // return; + // } + + // Navigator.push(context, MaterialPageRoute(builder: (context) { + // return SelectOutdoorFacility(); + // },)); + apiParameters?['isUpdate'] = widget.isUpdate; + Navigator.pushNamed(context, Routes.selectOutdoorFacility, + arguments: apiParameters); + + // context + // .read() + // .create(parameters: apiParameters!); + }, + ); + }, + buttonTitle: UiUtils.translate(context, "next"), + ), + ), + body: Form( + key: _formKey, + child: BlocListener( + listener: (context, state) { + if (state is CreatePropertyInProgress) { + Widgets.showLoader(context); + } + + if (state is CreatePropertyFailure) { + Widgets.hideLoder(context); + HelperUtils.showSnackBarMessage(context, state.errorMessage); + } + if (state is CreatePropertySuccess) { + Widgets.hideLoder(context); + if (widget.isUpdate == false) { + ref[propertyType ?? "sell"] + ?.fetchMyProperties(type: propertyType ?? "sell"); + Future.delayed( + const Duration(milliseconds: 260), + () { + Navigator.pushReplacement( + context, + ScaleUpRouter( + builder: (context) { + return PropertyAddSuccess( + model: state.propertyModel!, + ); + }, + current: widget, + )); + }, + ); + } else { + context.read().add(state.propertyModel!); + context + .read() + .update(state.propertyModel!); + cubitReference?.update(state.propertyModel!); + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "propertyUpdated"), + type: MessageType.success, onClose: () { + Navigator.of(context) + ..pop() + ..pop() + ..pop() + ..pop(); + }); + } + } + }, + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag, + child: Padding( + padding: const EdgeInsets.all(18.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(UiUtils.translate(context, "addvalues")), + SizedBox( + height: 18, + ), + ...buildFields(), + ], + ), + ), + ), + ), + ), + ); + } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; +} diff --git a/lib/Ui/screens/proprties/Property tab/favorites_property_tab.dart b/lib/Ui/screens/proprties/Property tab/favorites_property_tab.dart new file mode 100644 index 0000000..8971887 --- /dev/null +++ b/lib/Ui/screens/proprties/Property tab/favorites_property_tab.dart @@ -0,0 +1,220 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../../app/routes.dart'; +import '../../../../data/cubits/Utility/like_properties.dart'; +import '../../../../data/cubits/favorite/add_to_favorite_cubit.dart'; +import '../../../../data/cubits/favorite/fetch_favorites_cubit.dart'; +import '../../../../data/helper/designs.dart'; +import '../../../../data/model/property_model.dart'; +import '../../../../utils/Extensions/extensions.dart'; +import '../../../../utils/ui_utils.dart'; +import '../../home/Widgets/property_horizontal_card.dart'; +import '../../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../../widgets/Erros/no_data_found.dart'; +import '../../widgets/shimmerLoadingContainer.dart'; + +class FavoritesScreen extends StatefulWidget { + const FavoritesScreen({super.key}); + + static Route route(RouteSettings settings) { + return BlurredRouter( + builder: (context) => BlocProvider( + create: (context) => FetchFavoritesCubit(), + child: const FavoritesScreen(), + ), + ); + } + + @override + State createState() => _FavoritesScreenState(); +} + +class _FavoritesScreenState extends State + with AutomaticKeepAliveClientMixin { + final ScrollController _pageScrollController = ScrollController(); + @override + void initState() { + _pageScrollController.addListener(pageScrollListen); + context.read().fetchFavorites(); + super.initState(); + } + + void pageScrollListen() { + if (_pageScrollController.isEndReached()) { + if (context.read().hasMoreData()) { + context.read().fetchFavoritesMore(); + } + } + } + + @override + void dispose() { + _pageScrollController.dispose(); + super.dispose(); + } + + Widget buildFavoritePropertyShimmer() { + return ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.symmetric( + vertical: 10 + defaultPadding, horizontal: defaultPadding), + itemCount: 5, + separatorBuilder: (context, index) { + return const SizedBox( + height: 12, + ); + }, + itemBuilder: (context, index) { + return Container( + width: double.maxFinite, + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ClipRRect( + clipBehavior: Clip.antiAliasWithSaveLayer, + borderRadius: BorderRadius.all(Radius.circular(15)), + child: CustomShimmer(height: 90, width: 90), + ), + const SizedBox( + width: 10, + ), + Expanded( + child: LayoutBuilder(builder: (context, c) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 10, + ), + CustomShimmer( + height: 10, + width: c.maxWidth - 50, + ), + const SizedBox( + height: 10, + ), + const CustomShimmer( + height: 10, + ), + const SizedBox( + height: 10, + ), + CustomShimmer( + height: 10, + width: c.maxWidth / 1.2, + ), + const SizedBox( + height: 10, + ), + CustomShimmer( + height: 10, + width: c.maxWidth / 4, + ), + ], + ); + }), + ) + ]), + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + super.build(context); + return RefreshIndicator( + color: context.color.tertiaryColor, + onRefresh: () async { + context.read().fetchFavorites(); + }, + child: Scaffold( + backgroundColor: Theme.of(context).colorScheme.backgroundColor, + body: BlocBuilder( + builder: (context, state) { + if (state is FetchFavoritesInProgress) { + return buildFavoritePropertyShimmer(); + } + if (state is FetchFavoritesFailure) { + return Center( + child: Text(state.errorMessage.toString()), + ); + } + if (state is FetchFavoritesSuccess) { + if (state.propertymodel.isEmpty) { + return NoDataFound( + onTap: () { + context.read().fetchFavorites(); + }, + ); + } + + return RefreshIndicator( + color: context.color.tertiaryColor, + onRefresh: () async { + context.read().fetchFavorites(); + }, + child: Column( + children: [ + Expanded( + child: ListView.builder( + controller: _pageScrollController, + padding: const EdgeInsets.symmetric(horizontal: 8.0), + itemCount: state.propertymodel.length, + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + itemBuilder: (context, index) { + PropertyModel property = state.propertymodel[index]; + context.read().add(property.id); + + return Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8), + child: GestureDetector( + onTap: () { + // return; + Navigator.pushNamed( + context, Routes.propertyDetails, + arguments: { + 'propertyData': property, + 'fromMyProperty': true + }).then((value) {}); + }, + child: BlocProvider( + create: (context) => + AddToFavoriteCubitCubit(), + child: PropertyHorizontalCard( + property: property, + onLikeChange: (type) { + context + .read() + .remove(property.id); + }, + ), + ))); + }, + ), + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ), + ); + } + + return Container(); + }, + ), + ), + ); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/Ui/screens/proprties/Property tab/sell_rent_screen.dart b/lib/Ui/screens/proprties/Property tab/sell_rent_screen.dart new file mode 100644 index 0000000..680be26 --- /dev/null +++ b/lib/Ui/screens/proprties/Property tab/sell_rent_screen.dart @@ -0,0 +1,267 @@ +import 'package:ebroker/utils/api.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../../app/routes.dart'; +import '../../../../data/cubits/favorite/add_to_favorite_cubit.dart'; +import '../../../../data/cubits/property/fetch_my_properties_cubit.dart'; +import '../../../../data/helper/designs.dart'; +import '../../../../data/model/property_model.dart'; +import '../../../../utils/Extensions/extensions.dart'; +import '../../../../utils/responsiveSize.dart'; +import '../../../../utils/ui_utils.dart'; +import '../../home/Widgets/property_horizontal_card.dart'; +import '../../widgets/Erros/no_data_found.dart'; +import '../../widgets/Erros/no_internet.dart'; +import '../../widgets/Erros/something_went_wrong.dart'; +import '../../widgets/shimmerLoadingContainer.dart'; + +FetchMyPropertiesCubit? cubitReference; +dynamic propertyType; +Map ref = {}; + +class SellRentScreen extends StatefulWidget { + final String type; + final ScrollController controller; + const SellRentScreen( + {super.key, required this.type, required this.controller}); + + @override + State createState() => _SellRentScreenState(); +} + +class _SellRentScreenState extends State + with AutomaticKeepAliveClientMixin { + late ScrollController controller; + + bool isNetworkAvailable = true; + @override + void initState() { + super.initState(); + controller = widget.controller..addListener(pageScrollListener); + context.read().fetchMyProperties(type: widget.type); + } + + void pageScrollListener() { + if (controller.isEndReached()) { + if (context.read().hasMoreData()) { + context + .read() + .fetchMoreProperties(type: widget.type); + } + } + } + + String statusText(String text) { + if (text == "1") { + return UiUtils.translate(context, "active"); + } else if (text == "0") { + return UiUtils.translate(context, "deactive"); + } + return ""; + } + + Color statusColor(String text) { + if (text == "1") { + return const Color.fromRGBO(64, 171, 60, 1); + } else { + return const Color.fromRGBO(238, 150, 43, 1); + } + } + + @override + Widget build(BuildContext context) { + super.build(context); + + if (widget.type == "sell") { + ref['sell'] = context.read(); + } else { + ref['rent'] = context.read(); + } + + return RefreshIndicator( + color: context.color.tertiaryColor, + onRefresh: () async { + context + .read() + .fetchMyProperties(type: widget.type); + }, + child: Scaffold( + backgroundColor: context.color.backgroundColor, + body: BlocBuilder( + builder: (context, state) { + if (state is FetchMyPropertiesInProgress) { + return buildMyPropertyShimmer(); + } + if (state is FetchMyPropertiesFailure) { + if (state.errorMessage is ApiException) { + if (state.errorMessage.errorMessage == "no-internet") { + return NoInternet( + onRetry: () { + context + .read() + .fetchMyProperties(type: widget.type); + }, + ); + } + } + + return const SomethingWentWrong(); + } + + if (state is FetchMyPropertiesSuccess) { + if (state.myProperty.isEmpty) { + return SizedBox( + height: context.screenHeight - 150.rh(context), + child: NoDataFound( + height: 200, + title: "noPropertyAdded".translate(context), + description: "noPropertyDescription".translate(context), + onTap: () { + context + .read() + .fetchMyProperties(type: widget.type); + }, + ), + ); + } + + return ListView.separated( + physics: const AlwaysScrollableScrollPhysics(), + controller: controller, + padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 16), + itemCount: + state.myProperty.length + (state.isLoadingMore ? 1 : 0), + separatorBuilder: (context, index) { + return const SizedBox( + height: 2, + ); + }, + itemBuilder: ((context, index) { + if (state.myProperty.length == index) { + return const Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [CircularProgressIndicator()], + ); + } + + PropertyModel property = state.myProperty[index]; + + return GestureDetector( + onTap: () { + cubitReference = context.read(); + Navigator.pushNamed( + context, + Routes.propertyDetails, + arguments: { + 'propertyData': property, + 'fromMyProperty': true + }, + ).then((value) { + if (value == true) { + context + .read() + .fetchMyProperties(type: widget.type); + } + }); + }, + child: BlocProvider( + create: (context) => AddToFavoriteCubitCubit(), + child: PropertyHorizontalCard( + property: property, + + statusButton: StatusButton( + lable: statusText(property.status.toString()), + color: statusColor(property.status.toString()), + textColor: context.color.buttonColor, + ), + // useRow: true, + ), + ), + ); + }), + ); + } + + return Container(); + }), + ), + ); + } + + Widget buildMyPropertyShimmer() { + return ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.symmetric( + vertical: 10 + defaultPadding, horizontal: defaultPadding), + itemCount: 5, + separatorBuilder: (context, index) { + return const SizedBox( + height: 12, + ); + }, + itemBuilder: (context, index) { + return Container( + width: double.maxFinite, + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ClipRRect( + clipBehavior: Clip.antiAliasWithSaveLayer, + borderRadius: BorderRadius.all(Radius.circular(15)), + child: CustomShimmer(height: 90, width: 90), + ), + const SizedBox( + width: 10, + ), + Expanded( + child: LayoutBuilder(builder: (context, c) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 10, + ), + CustomShimmer( + height: 10, + width: c.maxWidth - 50, + ), + const SizedBox( + height: 10, + ), + const CustomShimmer( + height: 10, + ), + const SizedBox( + height: 10, + ), + CustomShimmer( + height: 10, + width: c.maxWidth / 1.2, + ), + const SizedBox( + height: 10, + ), + CustomShimmer( + height: 10, + width: c.maxWidth / 4, + ), + ], + ); + }), + ) + ]), + ); + }, + ); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/Ui/screens/proprties/my_properties_screen.dart b/lib/Ui/screens/proprties/my_properties_screen.dart new file mode 100644 index 0000000..f71884e --- /dev/null +++ b/lib/Ui/screens/proprties/my_properties_screen.dart @@ -0,0 +1,245 @@ +import 'package:ebroker/Ui/screens/main_activity.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../app/app_theme.dart'; +import '../../../data/cubits/property/fetch_my_properties_cubit.dart'; +import '../../../data/cubits/system/app_theme_cubit.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/ui_utils.dart'; +import 'Property%20tab/sell_rent_screen.dart'; + +int propertyScreenCurrentPage = 0; +ValueNotifier emptyCheckNotifier = + ValueNotifier({"isSellEmpty": false, "isRentEmpty": false}); + +class PropertiesScreen extends StatefulWidget { + const PropertiesScreen({Key? key}) : super(key: key); + + State createState() => MyPropertyState(); +} + +class MyPropertyState extends State + with TickerProviderStateMixin { + int offset = 0, total = 0; + int selectTab = 0; + final PageController _pageController = PageController(); + bool isSellEmpty = false; + bool isRentEmpty = false; + + @override + void initState() { + // if (ref.containsKey('sell')) { + // (ref['sell'] as FetchMyPropertiesCubit).stream.listen((event) { + // if (event is FetchMyPropertiesSuccess) { + // isSellEmpty = event.myProperty.isEmpty; + // setState(() {}); + // } + // }); + // } + // if (ref.containsKey("rent")) { + // (ref['rent'] as FetchMyPropertiesCubit).stream.listen((event) { + // if (event is FetchMyPropertiesSuccess) { + // isRentEmpty = event.myProperty.isEmpty; + // setState(() {}); + // } + // }); + // } + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AnnotatedRegion( + value: SystemUiOverlayStyle( + systemNavigationBarDividerColor: Colors.transparent, + // systemNavigationBarColor: Theme.of(context).colorScheme.secondaryColor, + systemNavigationBarIconBrightness: + context.watch().state.appTheme == AppTheme.dark + ? Brightness.light + : Brightness.dark, + // + statusBarColor: Theme.of(context).colorScheme.secondaryColor, + statusBarBrightness: + context.watch().state.appTheme == AppTheme.dark + ? Brightness.dark + : Brightness.light, + statusBarIconBrightness: + context.watch().state.appTheme == AppTheme.dark + ? Brightness.light + : Brightness.dark), + child: Scaffold( + backgroundColor: context.color.primaryColor, + appBar: UiUtils.buildAppBar( + context, + title: "myProperty".translate(context), + // bottomHeight: 49, + bottomHeight: 49, + + bottom: [ + Padding( + padding: const EdgeInsetsDirectional.fromSTEB(15, 8, 15, 0), + child: Row( + children: [ + customTab( + context, + isSelected: (selectTab == 0), + onTap: () { + selectTab = 0; + propertyScreenCurrentPage = 0; + setState(() {}); + _pageController.jumpToPage(0); + cubitReference = context.read(); + propertyType = "sell"; + }, + name: UiUtils.translate(context, "sell"), + onDoubleTap: () {}, + ), + const SizedBox( + width: 5, + ), + customTab( + context, + isSelected: selectTab == 1, + onTap: () { + _pageController.jumpToPage(1); + selectTab = 1; + propertyScreenCurrentPage = 1; + + cubitReference = context.read(); + propertyType = "rent"; + + setState(() {}); + }, + onDoubleTap: () {}, + name: UiUtils.translate(context, "rent"), + ), + ], + ), + ) + ], + ) + + // appBar: AppBar( + // elevation: 0, + // centerTitle: false, + // backgroundColor: context.color.primaryColor, + // title: Text(UiUtils.getTranslatedLabel(context, "myProperty")) + // .color(context.color.textColorDark), + // bottom: PreferredSize( + // preferredSize: const Size.fromHeight(40), + // child: Padding( + // padding: + // const EdgeInsets.symmetric(horizontal: 16.0, vertical: 5), + // child: Row( + // children: [ + // customTab( + // context, + // isSelected: (selectTab == 0), + // onTap: () { + // selectTab = 0; + // propertyScreenCurrentPage = 0; + // setState(() {}); + // _pageController.jumpToPage(0); + // cubitReference = context.read(); + // propertyType = "sell"; + // }, + // name: UiUtils.getTranslatedLabel(context, "sell"), + // onDoubleTap: () {}, + // ), + // const SizedBox( + // width: 5, + // ), + // customTab( + // context, + // isSelected: selectTab == 1, + // onTap: () { + // _pageController.jumpToPage(1); + // selectTab = 1; + // propertyScreenCurrentPage = 1; + + // cubitReference = context.read(); + // propertyType = "rent"; + + // setState(() {}); + // }, + // onDoubleTap: () {}, + // name: UiUtils.getTranslatedLabel(context, "rent"), + // ), + // ], + // ), + // )), + // ), + + , + body: ScrollConfiguration( + behavior: RemoveGlow(), + child: PageView( + onPageChanged: (value) { + propertyScreenCurrentPage = value; + selectTab = value; + setState(() {}); + }, + controller: _pageController, + children: [ + BlocProvider( + create: (context) => FetchMyPropertiesCubit(), + child: SellRentScreen( + type: "sell", + key: const Key("0"), + controller: sellScreenController, + ), + ), + BlocProvider( + create: (context) => FetchMyPropertiesCubit(), + child: SellRentScreen( + type: "rent", + key: const Key("1"), + controller: rentScreenController, + ), + ), + ], + ), + ), + ), + ); + } + + Widget customTab( + BuildContext context, { + required bool isSelected, + required String name, + required Function() onTap, + required Function() onDoubleTap, + }) { + return GestureDetector( + onTap: onTap, + onDoubleTap: onDoubleTap, + child: Container( + constraints: const BoxConstraints( + minWidth: 110, + ), + height: 40, + decoration: BoxDecoration( + color: (isSelected + ? (context.color.tertiaryColor) + : context.color.textColorDark) + .withOpacity(0.04), + border: Border.all( + color: isSelected + ? context.color.tertiaryColor + : context.color.textLightColor, + ), + borderRadius: BorderRadius.circular(11)), + child: Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(name).size(context.font.large), + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/proprties/properties_list.dart b/lib/Ui/screens/proprties/properties_list.dart new file mode 100644 index 0000000..a617aac --- /dev/null +++ b/lib/Ui/screens/proprties/properties_list.dart @@ -0,0 +1,320 @@ +import 'dart:developer'; + +import 'package:ebroker/Ui/screens/widgets/Erros/no_internet.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; + +import '../../../app/routes.dart'; +import '../../../data/cubits/property/fetch_property_from_category_cubit.dart'; +import '../../../data/model/property_model.dart'; +import '../../../utils/AdMob/bannerAdLoadWidget.dart'; +import '../../../utils/AdMob/interstitialAdManager.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/api.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../home/Widgets/property_horizontal_card.dart'; +import '../main_activity.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/Erros/no_data_found.dart'; +import '../widgets/shimmerLoadingContainer.dart'; + +class PropertiesList extends StatefulWidget { + final String? categoryId, categoryName; + + const PropertiesList({Key? key, this.categoryId, this.categoryName}) + : super(key: key); + + @override + PropertiesListState createState() => PropertiesListState(); + static Route route(RouteSettings routeSettings) { + Map? arguments = routeSettings.arguments as Map?; + return BlurredRouter( + builder: (_) => PropertiesList( + categoryId: arguments?['catID'] as String, + categoryName: arguments?['catName'] ?? "", + ), + ); + } +} + +class PropertiesListState extends State { + int offset = 0, total = 0; + + late ScrollController controller; + List propertylist = []; + int adPosition = 9; + InterstitialAdManager interstitialAdManager = InterstitialAdManager(); + @override + void initState() { + super.initState(); + searchbody = {}; + loadAd(); + interstitialAdManager.load(); + Constant.propertyFilter = null; + controller = ScrollController()..addListener(_loadMore); + context.read().fetchPropertyFromCategory( + int.parse( + widget.categoryId!, + ), + showPropertyType: false); + + Future.delayed(Duration.zero, () { + selectedcategoryId = widget.categoryId!; + selectedcategoryName = widget.categoryName!; + searchbody[Api.categoryId] = widget.categoryId; + setState(() {}); + }); + } + + BannerAd? _bannerAd; + bool _isLoaded = false; + void loadAd() { + _bannerAd = BannerAd( + adUnitId: Constant.admobBannerAndroid, + request: const AdRequest(), + size: AdSize.largeBanner, + listener: BannerAdListener( + // Called when an ad is successfully received. + onAdLoaded: (ad) { + debugPrint('$ad loaded.'); + setState(() { + _isLoaded = true; + }); + }, + // Called when an ad request failed. + onAdFailedToLoad: (ad, err) { + // Dispose the ad here to free resources. + ad.dispose(); + }, + ), + )..load(); + } + + @override + void dispose() { + controller.removeListener(_loadMore); + controller.dispose(); + super.dispose(); + } + + void _loadMore() async { + if (controller.isEndReached()) { + if (context.read().hasMoreData()) { + context + .read() + .fetchPropertyFromCategoryMore(); + } + } + } + + Widget? noInternetCheck(error) { + if (error is ApiException) { + if ((error).errorMessage == 'no-internet') { + return NoInternet( + onRetry: () { + context + .read() + .fetchPropertyFromCategory( + int.parse( + widget.categoryId!, + ), + showPropertyType: false); + }, + ); + } + } + return null; + } + + int itemIndex = 0; + @override + Widget build(BuildContext context) { + return bodyWidget(); + } + + Widget bodyWidget() { + return WillPopScope( + onWillPop: () async { + await interstitialAdManager.show(); + Constant.propertyFilter = null; + return true; + }, + child: Scaffold( + backgroundColor: Theme.of(context).colorScheme.primaryColor, + appBar: UiUtils.buildAppBar(context, + showBackButton: true, + title: selectedcategoryName == "" + ? widget.categoryName + : selectedcategoryName, + actions: [ + filterOptionsBtn(), + ]), + bottomNavigationBar: const BottomAppBar( + child: BannerAdWidget(bannerSize: AdSize.banner), + ), + body: BlocBuilder(builder: (context, state) { + if (state is FetchPropertyFromCategoryInProgress) { + return ListView.builder( + padding: + const EdgeInsets.symmetric(horizontal: 15, vertical: 15), + itemCount: 10, + itemBuilder: (context, index) { + return buildPropertiesShimmer(context); + }, + ); + } + + if (state is FetchPropertyFromCategoryFailure) { + var error = noInternetCheck(state.errorMessage); + if (error != null) { + return error; + } + return Center( + child: Text(state.errorMessage), + ); + } + if (state is FetchPropertyFromCategorySuccess) { + if (state.propertymodel.isEmpty) { + return Center( + child: NoDataFound( + onTap: () { + context + .read() + .fetchPropertyFromCategory( + int.parse( + widget.categoryId!, + ), + showPropertyType: false); + }, + ), + ); + } + + return Column( + children: [ + Expanded( + child: ListView.separated( + shrinkWrap: true, + controller: controller, + padding: const EdgeInsets.symmetric( + horizontal: 15, vertical: 3), + itemCount: state.propertymodel.length, + physics: const BouncingScrollPhysics(), + separatorBuilder: (context, index) { + if ((index + 1) % adPosition == 0) { + return (_bannerAd == null) + ? Container() + : Builder(builder: (context) { + return BannerAdWidget(); + }); + } + + return const SizedBox.shrink(); + }, + itemBuilder: (context, index) { + PropertyModel property = state.propertymodel[index]; + return GestureDetector( + onTap: () { + Navigator.pushNamed( + context, + Routes.propertyDetails, + arguments: { + 'propertyData': property, + 'propertiesList': state.propertymodel, + 'fromMyProperty': false, + }, + ); + }, + child: PropertyHorizontalCard( + property: property, + ), + ); + }, + ), + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ); + } + return Container(); + })), + ); + } + + Widget buildPropertiesShimmer(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + height: 120.rh(context), + decoration: BoxDecoration( + border: Border.all(width: 1.5, color: context.color.borderColor), + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(18)), + child: Row( + children: [ + CustomShimmer( + height: 120.rh(context), + width: 100.rw(context), + ), + SizedBox( + width: 10.rw(context), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + CustomShimmer( + width: 100.rw(context), + height: 10, + borderRadius: 7, + ), + CustomShimmer( + width: 150.rw(context), + height: 10, + borderRadius: 7, + ), + CustomShimmer( + width: 120.rw(context), + height: 10, + borderRadius: 7, + ), + CustomShimmer( + width: 80.rw(context), + height: 10, + borderRadius: 7, + ) + ], + ) + ], + ), + ), + ); + } + + Widget filterOptionsBtn() { + return IconButton( + onPressed: () { + // show filter screen + + // Constant.propertyFilter = null; + Navigator.pushNamed(context, Routes.filterScreen, + arguments: {"showPropertyType": false}).then((value) { + if (value == true) { + context + .read() + .fetchPropertyFromCategory(int.parse(widget.categoryId!), + showPropertyType: false); + } + setState(() {}); + }); + }, + icon: Icon( + Icons.filter_list_rounded, + color: context.color.textColorDark, + )); + } +} diff --git a/lib/Ui/screens/proprties/property_details.dart b/lib/Ui/screens/proprties/property_details.dart new file mode 100644 index 0000000..23a3797 --- /dev/null +++ b/lib/Ui/screens/proprties/property_details.dart @@ -0,0 +1,2585 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first, use_build_context_synchronously +import 'dart:async'; +import 'dart:convert'; +import 'dart:ui'; + +import 'package:ebroker/Ui/screens/chat/chat_screen.dart'; +import 'package:ebroker/Ui/screens/proprties/Property%20tab/sell_rent_screen.dart'; +import 'package:ebroker/Ui/screens/proprties/widgets/report_property_widget.dart'; +import 'package:ebroker/Ui/screens/widgets/blurred_dialoge_box.dart'; +import 'package:ebroker/Ui/screens/widgets/like_button_widget.dart'; +import 'package:ebroker/Ui/screens/widgets/panaroma_image_view.dart'; +import 'package:ebroker/Ui/screens/widgets/read_more_text.dart'; +import 'package:ebroker/app/routes.dart'; +import 'package:ebroker/data/Repositories/interest_repository.dart'; +import 'package:ebroker/data/Repositories/subscription_repository.dart'; +import 'package:ebroker/data/cubits/Report/property_report_cubit.dart'; +import 'package:ebroker/data/cubits/category/fetch_category_cubit.dart'; +import 'package:ebroker/data/cubits/chatCubits/delete_message_cubit.dart'; +import 'package:ebroker/data/cubits/chatCubits/load_chat_messages.dart'; +import 'package:ebroker/data/cubits/enquiry/store_enqury_id.dart'; +import 'package:ebroker/data/cubits/favorite/add_to_favorite_cubit.dart'; +import 'package:ebroker/data/cubits/property/Interest/change_interest_in_property_cubit.dart'; +import 'package:ebroker/data/cubits/property/delete_property_cubit.dart'; +import 'package:ebroker/data/cubits/property/fetch_my_properties_cubit.dart'; +import 'package:ebroker/data/cubits/property/set_property_view_cubit.dart'; +import 'package:ebroker/data/cubits/property/update_property_status.dart'; +import 'package:ebroker/data/cubits/subscription/get_subsctiption_package_limits_cubit.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/interested_user_model.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:ebroker/utils/AppIcon.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/api.dart'; +import 'package:ebroker/utils/constant.dart'; +import 'package:ebroker/utils/hive_utils.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:ebroker/utils/string_extenstion.dart'; +import 'package:flick_video_player/flick_video_player.dart'; +import 'package:flutter/foundation.dart' as f; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; +import 'package:url_launcher/url_launcher.dart' as urllauncher; +import 'package:url_launcher/url_launcher.dart'; +import 'package:video_player/video_player.dart'; +import 'package:youtube_player_flutter/youtube_player_flutter.dart'; + +import '../../../data/cubits/Interested/get_interested_user_cubit.dart'; +import '../../../data/cubits/chatCubits/send_message.dart'; +import '../../../data/cubits/outdoorfacility/fetch_outdoor_facility_list.dart'; +import '../../../data/cubits/system/fetch_system_settings_cubit.dart'; +import '../../../data/helper/widgets.dart'; +import '../../../data/model/category.dart'; +import '../../../settings.dart'; +import '../../../utils/AdMob/interstitialAdManager.dart'; +import '../../../utils/guestChecker.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/ui_utils.dart'; +import '../analytics/analytics_screen.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/all_gallary_image.dart'; +import '../widgets/video_view_screen.dart'; +import '../widgets/my_maps.dart'; + +Map rentDurationMap = { + "Quarterly": "Quarter", + "Monthly": "Month", + "Yearly": "Year", + "Daily": "Daily", + "Day": "Day" +}; + +class PropertyDetails extends StatefulWidget { + final PropertyModel? property; + final bool? fromMyProperty; + final bool? fromCompleteEnquiry; + final bool fromSlider; + final bool? fromPropertyAddSuccess; + const PropertyDetails( + {Key? key, + this.fromPropertyAddSuccess, + required this.property, + this.fromSlider = false, + this.fromMyProperty, + this.fromCompleteEnquiry}) + : super(key: key); + + @override + PropertyDetailsState createState() => PropertyDetailsState(); + + static Route route(RouteSettings routeSettings) { + try { + Map? arguments = routeSettings.arguments as Map?; + return BlurredRouter( + builder: (_) => MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => ChangeInterestInPropertyCubit(), + ), + BlocProvider( + create: (context) => UpdatePropertyStatusCubit(), + ), + BlocProvider( + create: (context) => DeletePropertyCubit(), + ), + BlocProvider( + create: (context) => PropertyReportCubit(), + ), + BlocProvider( + create: (context) => GetInterestedUserCubit(), + ), + ], + child: PropertyDetails( + property: arguments?['propertyData'], + fromMyProperty: arguments?['fromMyProperty'] ?? false, + fromSlider: arguments?['fromSlider'] ?? false, + fromCompleteEnquiry: arguments?['fromCompleteEnquiry'] ?? false, + fromPropertyAddSuccess: arguments?['fromSuccess'] ?? false, + ), + ), + ); + } catch (e) { + rethrow; + } + } +} + +class PropertyDetailsState extends State + with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { + FlickManager? flickManager; + ValueNotifier shouldShowSubscriptionOverlay = ValueNotifier(false); + // late Property propertyData; + bool favoriteInProgress = false; + bool isPlayingYoutubeVideo = false; + bool fromMyProperty = false; //get its value from Widget + bool fromCompleteEnquiry = false; //get its value from Widget + List promotedProeprtiesIds = []; + bool toggleEnqButton = false; + PropertyModel? property; + bool isPromoted = false; + bool showGoogleMap = false; + bool isEnquiryFromChat = false; + BannerAd? _bannerAd; + @override + bool get wantKeepAlive => true; + GlobalKey appBarKey = GlobalKey(); + + final Completer _controller = + Completer(); + List? gallary; + String youtubeVideoThumbnail = ""; + bool? _isLoaded; + InterstitialAdManager interstitialAdManager = InterstitialAdManager(); + bool isPremiumProperty = true; + bool isPremiumUser = false; + bool isReported = false; + + bool shouldRestrictPropertyAccess() { + if (isPremiumProperty && !isPremiumUser) { + return true; + } + return false; + } + + @override + void initState() { + super.initState(); + context + .read() + .getLimits(SubscriptionLimitType.property); + isPremiumProperty = widget.property?.allPropData['is_premium'] ?? false; + + isPremiumUser = context + .read() + .getRawSettings()['is_premium'] ?? + false; + isReported = widget.property?.allPropData?['is_reported'] ?? false; + + loadAd(); + // Future.delayed( + // Duration(milliseconds: 50), + // () { + // Map widgetInfo = + // UiUtils.getWidgetInfo(context, appBarKey); + // bottomAppBarHeight = widgetInfo['height'] ?? 0; + // setState(() {}); + // }, + // ); + interstitialAdManager.load(); + // customListenerForConstant(); + //add title image along with gallery images + context.read().fetch(); + context.read().fetch("${widget.property?.id}"); + Future.delayed( + const Duration(seconds: 3), + () { + showGoogleMap = true; + if (mounted) setState(() {}); + }, + ); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + gallary = List.from(widget.property!.gallery!); + if (widget.property?.video != "") { + injectVideoInGallery(); + setState(() {}); + } + }); + + if (widget.fromSlider) { + getProperty(); + } else { + property = widget.property; + setData(); + } + + setViewdProperty(); + if (widget.property?.video != "" && + widget.property?.video != null && + !HelperUtils.isYoutubeVideo(widget.property?.video ?? "")) { + flickManager = FlickManager( + videoPlayerController: VideoPlayerController.network( + property!.video!, + ), + ); + flickManager?.onVideoEnd = () {}; + } + + if (widget.property?.video != "" && + widget.property?.video != null && + HelperUtils.isYoutubeVideo(widget.property?.video ?? "")) { + String? videoId = YoutubePlayer.convertUrlToId(property!.video!); + String thumbnail = YoutubePlayer.getThumbnail(videoId: videoId!); + youtubeVideoThumbnail = thumbnail; + setState(() {}); + } + } + + void loadAd() { + _bannerAd = BannerAd( + adUnitId: Constant.admobBannerAndroid, + request: const AdRequest(), + size: AdSize.largeBanner, + listener: BannerAdListener( + // Called when an ad is successfully received. + onAdLoaded: (ad) { + debugPrint('$ad loaded.'); + setState(() { + _isLoaded = true; + }); + }, + // Called when an ad request failed. + onAdFailedToLoad: (ad, err) { + // Dispose the ad here to free resources. + ad.dispose(); + }, + ), + )..load(); + } + + Future getProperty() async { + var response = await HelperUtils.sendApiRequest( + Api.apiGetProprty, + { + Api.id: widget.property!.id, + }, + true, + context, + passUserid: false); + if (response != null) { + var getdata = json.decode(response); + if (!getdata[Api.error]) { + getdata['data']; + setData(); + setState(() {}); + } + } + } + + void setData() { + fromMyProperty = widget.fromMyProperty!; + fromCompleteEnquiry = widget.fromCompleteEnquiry!; + } + + void setViewdProperty() { + if (property!.addedBy.toString() != HiveUtils.getUserId()) { + context.read().set(property!.id!.toString()); + } + } + + late final CameraPosition _kInitialPlace = CameraPosition( + target: LatLng( + double.parse( + (property?.latitude ?? "0"), + ), + double.parse( + (property?.longitude ?? "0"), + ), + ), + zoom: 14.4746, + ); + + @override + void dispose() { + flickManager?.dispose(); + + super.dispose(); + } + + void injectVideoInGallery() { + ///This will inject video in image list just like another platforms + if ((gallary?.length ?? 0) < 2) { + if (widget.property?.video != null) { + gallary?.add(Gallery( + id: 99999999999, + image: property!.video ?? "", + imageUrl: "", + isVideo: true)); + } + } else { + gallary?.insert( + 0, + Gallery( + id: 99999999999, + image: property!.video!, + imageUrl: "", + isVideo: true)); + } + + setState(() {}); + } + + String? _statusFilter(String value) { + if (value == "Sell" || value == "sell") { + return "sold".translate(context); + } + if (value == "Rent" || value == "rent") { + return "Rented".translate(context); + } + + return null; + } + + int? _getStatus(type) { + int? value; + if (type == "Sell" || type == "sell") { + value = 2; + } else if (type == "Rent" || type == "rent") { + value = 3; + } else if (type == "Rented" || type == "rented") { + value = 1; + } + return value; + } + +//main build + @override + Widget build(BuildContext context) { + super.build(context); + + print("ISRENTED ${isPremiumProperty} nd ${isPremiumUser}"); + + String rentPrice = (property!.price! + .priceFormate( + disabled: false, + ) + .toString() + .formatAmount(prefix: true)); + + if (property?.rentduration != "" && property?.rentduration != null) { + rentPrice = + ("$rentPrice / ") + (rentDurationMap[property!.rentduration] ?? ""); + } + + String Lat = property!.latitude.toString(); + String Long = property!.longitude.toString(); + + return SafeArea( + child: WillPopScope( + onWillPop: () async { + await interstitialAdManager.show(); + if (widget.fromPropertyAddSuccess ?? false) { + Navigator.popUntil(context, (route) => route.isFirst); + return false; + } + + showGoogleMap = false; + setState(() {}); + + return true; + }, + child: AnnotatedRegion( + value: UiUtils.getSystemUiOverlayStyle( + context: context, + ), + child: SafeArea( + child: BlocListener( + listener: (context, state) { + if (state is GetSubsctiptionPackageLimitsSuccess) { + isPremiumUser = state.packageLimit.isPremium; + setState(() {}); + } + }, + child: Stack( + children: [ + Scaffold( + appBar: UiUtils.buildAppBar(context, + hideTopBorder: true, + showBackButton: true, + actions: [ + if (!HiveUtils.isGuest()) ...[ + if (int.parse(HiveUtils.getUserId() ?? "0") == + property?.addedBy) + IconButton( + onPressed: () async { + DataOutput interestUser = + await InterestRepository() + .getInterestUser( + property!.id!.toString(), + offset: 0); + + GetInterestedUserCubit + interestedUserCubitReference = + context.read(); + + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(20)), + backgroundColor: context + .color.secondaryColor + .withOpacity(0.86), + constraints: BoxConstraints( + minWidth: double.infinity, + maxHeight: context.screenHeight * 0.7, + minHeight: context.screenHeight * 0.3), + builder: (context) { + return InterestedUserListWidget( + totalCount: + "${widget.property?.totalInterestedUsers}", + interestedUserCubitReference: + interestedUserCubitReference); + }, + ); + + return; + Navigator.push(context, BlurredRouter( + builder: (context) { + return AnalyticsScreen( + interestUserCount: widget + .property!.totalInterestedUsers + .toString(), + ); + }, + )); + }, + icon: Icon( + Icons.analytics, + color: context.color.tertiaryColor, + )), + ], + IconButton( + onPressed: () { + HelperUtils.share( + context, property!.id!, property?.slugId ?? ""); + }, + icon: Icon( + Icons.share, + color: context.color.tertiaryColor, + ), + ), + if (property?.addedBy.toString() == + HiveUtils.getUserId() && + property!.properyType != "Sold" && + property?.status == 1) + PopupMenuButton( + onSelected: (value) async { + var action = await UiUtils.showBlurredDialoge( + context, + dialoge: BlurredDialogBuilderBox( + title: "changePropertyStatus" + .translate(context), + acceptButtonName: + "change".translate(context), + contentBuilder: (context, s) { + return FittedBox( + fit: BoxFit.none, + child: Row( + mainAxisAlignment: + MainAxisAlignment.start, + children: [ + Container( + decoration: BoxDecoration( + color: context + .color.tertiaryColor, + borderRadius: + BorderRadius.circular( + 10)), + width: s.maxWidth / 4, + height: 50, + child: Center( + child: Text(property! + .properyType! + .translate(context)) + .color(context + .color.buttonColor)), + ), + Text( + "toArrow".translate(context), + ), + Container( + width: s.maxWidth / 4, + decoration: BoxDecoration( + color: context + .color.tertiaryColor + .withOpacity(0.4), + borderRadius: + BorderRadius.circular( + 10)), + height: 50, + child: Center( + child: Text(_statusFilter( + property! + .properyType!) ?? + "") + .color(context + .color.buttonColor)), + ), + ], + ), + ); + }), + ); + if (action == true) { + Future.delayed(Duration.zero, () { + context + .read() + .update( + propertyId: property!.id, + status: + _getStatus(property!.properyType), + ); + }); + } + }, + color: context.color.secondaryColor, + itemBuilder: (BuildContext context) { + return { + 'changeStatus'.translate(context), + }.map((String choice) { + return PopupMenuItem( + value: choice, + textStyle: TextStyle( + color: context.color.textColorDark), + child: Text(choice), + ); + }).toList(); + }, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 4.0), + child: Icon( + Icons.more_vert_rounded, + color: context.color.tertiaryColor, + ), + ), + ), + const SizedBox( + width: 10, + ) + ]), + backgroundColor: context.color.backgroundColor, + floatingActionButton: (property == null || + property!.addedBy.toString() == HiveUtils.getUserId()) + ? const SizedBox.shrink() + : Container(), + bottomNavigationBar: isPlayingYoutubeVideo == false + ? BottomAppBar( + key: appBarKey, + padding: const EdgeInsets.symmetric(horizontal: 0), + color: context.color.secondaryColor, + child: bottomNavBar()) + : null, + floatingActionButtonLocation: + FloatingActionButtonLocation.centerFloat, + body: BlocListener( + listener: (context, state) { + if (state is DeletePropertyInProgress) { + Widgets.showLoader(context); + } + + if (state is DeletePropertySuccess) { + Widgets.hideLoder(context); + Future.delayed( + const Duration(milliseconds: 1000), + () { + Navigator.pop(context, true); + }, + ); + } + if (state is DeletePropertyFailure) { + Widgets.showLoader(context); + } + }, + child: SafeArea( + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: BlocListener( + listener: (context, state) { + if (state is UpdatePropertyStatusInProgress) { + Widgets.showLoader(context); + } + + if (state is UpdatePropertyStatusSuccess) { + Widgets.hideLoder(context); + Fluttertoast.showToast( + msg: "statusUpdated".translate(context), + backgroundColor: successMessageColor, + gravity: ToastGravity.TOP, + toastLength: Toast.LENGTH_LONG); + + (cubitReference as FetchMyPropertiesCubit) + .updateStatus( + property!.id!, property!.properyType!); + setState(() {}); + } + if (state is UpdatePropertyStatusFail) { + Widgets.hideLoder(context); + } + }, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: + isPlayingYoutubeVideo == false ? 20.0 : 0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 10, + ), + + if (!isPlayingYoutubeVideo) + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(20), + child: SizedBox( + height: 227.rh(context), + child: Stack( + children: [ + GestureDetector( + onTap: () { + // google map doesn't allow blur so we hide it:) + showGoogleMap = false; + setState(() {}); + UiUtils.showFullScreenImage( + context, + provider: NetworkImage( + property!.titleImage!, + ), + then: () { + showGoogleMap = true; + setState(() {}); + }, + ); + }, + child: UiUtils.getImage( + property!.titleImage!, + fit: BoxFit.cover, + width: double.infinity, + height: 227.rh(context), + showFullScreenImage: true, + ), + ), + PositionedDirectional( + top: 20, + end: 20, + child: LikeButtonWidget( + onStateChange: + (AddToFavoriteCubitState + state) { + if (state + is AddToFavoriteCubitInProgress) { + favoriteInProgress = true; + setState( + () {}, + ); + } else { + favoriteInProgress = + false; + setState( + () {}, + ); + } + }, + property: property!, + ), + ), + PositionedDirectional( + bottom: 5, + end: 18, + child: Visibility( + visible: + property?.threeDImage != + "", + child: GestureDetector( + onTap: () { + Navigator.push( + context, + BlurredRouter( + builder: (context) => + PanaromaImageScreen( + imageUrl: property! + .threeDImage!, + ), + ), + ); + }, + child: Container( + decoration: BoxDecoration( + color: context.color + .secondaryColor, + shape: BoxShape.circle, + ), + height: 40.rh(context), + width: 40.rw(context), + child: Padding( + padding: + const EdgeInsets + .all(5.0), + child: UiUtils.getSvg( + AppIcons.v360Degree, + color: context.color + .tertiaryColor), + ), + ), + ), + ), + ), + advertismentLable() + ], + ), + ), + ), + const SizedBox( + height: 15, + ), + Row(children: [ + UiUtils.imageType( + property?.category!.image ?? "", + width: 18, + height: 18, + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null), + const SizedBox( + width: 10, + ), + SizedBox( + width: 158.rw(context), + child: Text( + property!.category!.category!) + .setMaxLines(lines: 1) + .size( + context.font.normal, + ) + .bold( + weight: FontWeight.w400, + ) + .color(UiUtils.makeColorLight( + context.color.textColorDark)), + ), + const Spacer(), + Container( + width: 50, + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(3.5), + color: + context.color.tertiaryColor), + child: Padding( + padding: const EdgeInsets.all(3.0), + child: Center( + child: Text( + property!.properyType + .toString() + .toLowerCase() + .translate(context), + ).size(context.font.small).color( + context.color.buttonColor)), + ), + ) + ]), + const SizedBox( + height: 15, + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text(property!.title! + .firstUpperCase()) + .color( + context.color.textColorDark) + .size(18) + .bold(weight: FontWeight.w600), + ), + Text(property?.postCreated ?? "") + .color(context.color.textColorDark + .withOpacity(0.6)), + ], + ), + const SizedBox(height: 13), + Row( + children: [ + if (property!.properyType + .toString() + .toLowerCase() == + "rent") ...[ + Text(rentPrice) + .color( + context.color.tertiaryColor) + .size(18) + .bold(weight: FontWeight.w700), + ] else ...[ + Text(property!.price! + .priceFormate( + disabled: false) + .formatAmount(prefix: true)) + .color( + context.color.tertiaryColor) + .size(18) + .bold(weight: FontWeight.w700), + ], + if (Constant.isNumberWithSuffix) ...[ + if (property!.properyType + .toString() + .toLowerCase() != + "rent") ...[ + const SizedBox( + width: 5, + ), + Text("(${property!.price!})") + .color(context + .color.tertiaryColor) + .size(18) + .bold( + weight: FontWeight.w500), + ] + ] + ], + ), + const SizedBox( + height: 20, + ), + Wrap( + direction: Axis.horizontal, + crossAxisAlignment: + WrapCrossAlignment.start, + runAlignment: WrapAlignment.start, + alignment: WrapAlignment.start, + children: List.generate( + property?.parameters?.length ?? 0, + (index) { + Parameter? parameter = + property?.parameters![index]; + bool isParameterValueEmpty = + (parameter?.value == "" || + parameter?.value == "0" || + parameter?.value == null || + parameter?.value == "null"); + + ///If it has no value + if (isParameterValueEmpty) { + return const SizedBox.shrink(); + } + + return ConstrainedBox( + constraints: BoxConstraints( + minWidth: + (context.screenWidth / 2) - + 40), + child: Padding( + padding: + const EdgeInsets.fromLTRB( + 0, 8, 8, 8), + child: SizedBox( + // height: 37, + child: Row( + crossAxisAlignment: + CrossAxisAlignment + .start, + mainAxisSize: + MainAxisSize.min, + children: [ + Container( + width: 36.rw(context), + height: 36.rh(context), + alignment: + Alignment.center, + decoration: BoxDecoration( + color: context.color + .tertiaryColor + .withOpacity( + 0.2), + borderRadius: + BorderRadius + .circular( + 10)), + child: SizedBox( + height: + 20.rh(context), + width: 20.rw(context), + child: FittedBox( + child: UiUtils + .imageType( + parameter + ?.image ?? + "", + fit: BoxFit.cover, + color: Constant + .adaptThemeColorSvg + ? context + .color + .tertiaryColor + : null, + ), + ), + ), + ), + SizedBox( + width: 10.rw(context), + ), + Column( + crossAxisAlignment: + CrossAxisAlignment + .start, + mainAxisSize: + MainAxisSize.min, + children: [ + Text(parameter + ?.name ?? + "") + .size(12) + .color(context + .color + .textColorDark + .withOpacity( + 0.8)), + if (parameter + ?.typeOfParameter == + "file") ...{ + InkWell( + onTap: () async { + await urllauncher.launchUrl( + Uri.parse( + parameter! + .value), + mode: LaunchMode + .externalApplication); + }, + child: Text( + UiUtils.translate( + context, + "viewFile"), + ) + .underline() + .color(context + .color + .tertiaryColor), + ), + } else if (parameter + ?.value + is List) ...{ + Text((parameter + ?.value + as List) + .join(",")) + } else ...[ + if (parameter + ?.typeOfParameter == + "textarea") ...[ + SizedBox( + width: MediaQuery.of( + context) + .size + .width * + 0.7, + child: Text( + "${parameter?.value}") + .size(14) + .bold( + weight: FontWeight + .w600, + ), + ) + ] else ...[ + Text("${parameter?.value}") + .size(14) + .bold( + weight: + FontWeight + .w600, + ) + ] + ] + ], + ) + ]), + ), + ), + ); + }), + ), + const SizedBox( + height: 14, + ), + UiUtils.getDivider(), + const SizedBox( + height: 14, + ), + Text(UiUtils.translate( + context, "aboutThisPropLbl")) + .color(context.color.textColorDark) + .size(16) + .bold(weight: FontWeight.w600), + const SizedBox( + height: 15, + ), + ReadMoreText( + text: property?.description ?? "", + style: TextStyle( + color: context.color.textColorDark + .withOpacity(0.7)), + readMoreButtonStyle: TextStyle( + color: + context.color.tertiaryColor)), + const SizedBox( + height: 20, + ), + + //TODO: + if (_bannerAd != null && + Constant.isAdmobAdsEnabled) + SizedBox( + width: _bannerAd?.size.width + .toDouble(), + height: _bannerAd?.size.height + .toDouble(), + child: AdWidget(ad: _bannerAd!)), + + const SizedBox( + height: 20, + ), + if (widget + .property + ?.assignedOutdoorFacility + ?.isNotEmpty ?? + false) ...[ + Text(UiUtils.translate( + context, "outdoorFacilities")) + .color(context.color.textColorDark) + .size(16) + .bold(weight: FontWeight.w600), + const SizedBox(height: 10), + ], + OutdoorFacilityListWidget( + outdoorFacilityList: widget.property + ?.assignedOutdoorFacility ?? + []), + + Text(UiUtils.translate( + context, "listedBy")) + .color(context.color.textColorDark) + .size(16) + .bold(weight: FontWeight.w600), + const SizedBox( + height: 14, + ), + GestureDetector( + onTap: () {}, + child: AgentProfileWidget( + hideDetails: + shouldRestrictPropertyAccess(), + widget: widget, + ), + ), + const SizedBox( + height: 10, + ), + if (gallary?.isNotEmpty ?? false) ...[ + Text(UiUtils.translate( + context, "gallery")) + .color(context.color.textColorDark) + .size(16) + .bold(weight: FontWeight.w600), + SizedBox( + height: 10.rh(context), + ), + ], + if (gallary?.isNotEmpty ?? false) ...[ + Row( + children: List.generate( + (gallary?.length.clamp(0, 4)) ?? 0, + (index) { + return Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 3), + child: ClipRRect( + borderRadius: + BorderRadius.circular(18), + child: Stack( + children: [ + GestureDetector( + onTap: () { + if (gallary?[index] + .isVideo == + true) return; + + //google map doesn't allow blur so we hide it:) + showGoogleMap = false; + setState(() {}); + + var images = gallary + ?.map((e) => + e.imageUrl) + .toList(); + + UiUtils + .imageGallaryView( + context, + images: images!, + initalIndex: index, + then: () { + showGoogleMap = + true; + setState(() {}); + }, + ); + }, + child: SizedBox( + width: 76.rw(context), + height: 76.rh(context), + child: gallary?[index] + .isVideo == + true + ? Container( + child: UiUtils.getImage( + youtubeVideoThumbnail, + fit: BoxFit + .cover), + ) + : UiUtils.getImage( + gallary?[index] + .imageUrl ?? + "", + fit: BoxFit + .cover), + ), + ), + if (gallary?[index] + .isVideo == + true) + Positioned.fill( + child: + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return VideoViewScreen( + videoUrl: gallary?[ + index] + .image ?? + "", + flickManager: + flickManager, + ); + }, + )); + }, + child: Container( + color: Colors.black + .withOpacity(0.3), + child: FittedBox( + fit: BoxFit.none, + child: Container( + decoration: BoxDecoration( + shape: BoxShape + .circle, + color: context + .color + .tertiaryColor + .withOpacity( + 0.8)), + width: 30, + height: 30, + child: const Icon( + Icons + .play_arrow, + color: Colors + .white, + ), + ), + ), + ), + )), + if (index == 3) + Positioned.fill( + child: + GestureDetector( + onTap: () { + Navigator.push( + context, + BlurredRouter( + builder: (context) { + return AllGallaryImages( + youtubeThumbnail: + youtubeVideoThumbnail, + images: property + ?.gallery ?? + []); + }, + )); + }, + child: Container( + alignment: + Alignment.center, + color: Colors.black + .withOpacity(0.3), + child: Text( + "+${(property?.gallery?.length ?? 0) - 3}") + .color( + Colors.white, + ) + .size(context + .font.large) + .bold(), + ), + )) + ], + ), + ), + ); + }, + )) + ], + const SizedBox( + height: 15, + ), + Text(UiUtils.translate( + context, "locationLbl")) + .color(context.color.textColorDark) + .size(context.font.large) + .bold(weight: FontWeight.w600), + SizedBox( + height: 10.rh(context), + ), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text("${UiUtils.translate(context, "addressLbl")} :") + .size(context.font.normal) + .color( + context.color.textColorDark), + // .bold(weight: FontWeight.w600), + SizedBox( + height: 5.rh(context), + ), + Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + UiUtils.getSvg(AppIcons.location, + color: context + .color.tertiaryColor), + SizedBox( + width: 5.rw(context), + ), + Expanded( + child: HideDetailsBlur( + hide: + shouldRestrictPropertyAccess(), + child: Text( + "${property?.address!}"), + ), + ) + ], + ), + ], + ), + SizedBox( + height: 10.rh(context), + ), + SizedBox( + height: 175, + child: ClipRRect( + borderRadius: + BorderRadius.circular(10), + child: Stack( + fit: StackFit.expand, + children: [ + Image.asset( + "assets/map.png", + fit: BoxFit.cover, + ), + BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 4.0, + sigmaY: 4.0, + ), + child: Center( + child: MaterialButton( + onPressed: () { + if (shouldRestrictPropertyAccess()) { + GuestChecker.check( + onNotGuest: () { + shouldShowSubscriptionOverlay + .value = true; + return; + }, + ); + return; + } + + Navigator.push(context, + BlurredRouter( + builder: (context) { + return Scaffold( + extendBodyBehindAppBar: + true, + appBar: AppBar( + elevation: 0, + iconTheme: IconThemeData( + color: context + .color + .tertiaryColor), + backgroundColor: + Colors + .transparent, + ), + body: MyMaps( + latitudeString: Lat, + longitudeString: Long, + ), + ); + }, + )); + }, + shape: + RoundedRectangleBorder( + borderRadius: + BorderRadius + .circular( + 5)), + color: context + .color.tertiaryColor, + elevation: 0, + child: + shouldRestrictPropertyAccess() + ? Icon( + Icons + .lock_open_outlined, + color: context + .color + .secondaryColor + .withOpacity( + 0.8), + ) + : Text(("viewMap" + .translate( + context))) + .color( + context.color + .buttonColor, + ), + ), + ), + ), + ], + ), + ), + ), + const SizedBox( + height: 18, + ), + if (!HiveUtils.isGuest()) ...[ + if (int.parse( + HiveUtils.getUserId() ?? "0") != + property?.addedBy) + Row( + children: [ + // sendEnquiryButtonWithState(), + setInterest(), + ], + ), + ], + const SizedBox( + height: 18, + ), + if (!reportedProperties + .contains(widget.property!.id) && + widget.property!.addedBy.toString() != + HiveUtils.getUserId() && + !isReported) + ReportPropertyButton( + propertyId: property!.id!, + onSuccess: () { + setState( + () {}, + ); + }, + ) + ], + ), + + //here + SizedBox( + height: 20.rh(context), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ValueListenableBuilder( + valueListenable: shouldShowSubscriptionOverlay, + builder: (context, value, child) { + return SizedBox( + height: context.screenHeight, + child: AnimatedOpacity( + duration: const Duration(milliseconds: 300), + opacity: value == true ? 1 : 0, + child: IgnorePointer( + ignoring: !value, + child: GestureDetector( + onTap: () { + shouldShowSubscriptionOverlay.value = false; + }, + child: Padding( + padding: EdgeInsets.only( + bottom: + getBottomNavigationBarHeight(context)), + child: Container( + // height: context.screenHeight - .rh(context), + + child: ClipRRect( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaY: 5, sigmaX: 4), + child: Container( + color: Colors.black.withOpacity(0.5), + width: double.infinity, + child: Stack( + fit: StackFit.expand, + children: [ + Align( + alignment: + Alignment.bottomCenter, + child: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + Material( + color: Colors.transparent, + child: Text( + "toAccessSubscribe" + .translate(context), + style: const TextStyle( + shadows: [ + Shadow( + offset: Offset( + 1.0, 1.0), + blurRadius: 3.0, + color: Color + .fromARGB( + 255, + 0, + 0, + 0), + ), + ]), + ) + .size(context + .font.larger) + .bold() + .centerAlign() + .color(context.color + .secondaryColor), + ), + const SizedBox( + height: 15, + ), + MaterialButton( + height: 48, + minWidth: + context.screenWidth * + 0.5, + onPressed: () { + Navigator.pushNamed( + context, + Routes + .subscriptionPackageListRoute, + arguments: { + "from": Routes + .propertyDetails + }).then((value) { + context + .read< + GetSubsctiptionPackageLimitsCubit>() + .getLimits( + SubscriptionLimitType + .property); + shouldShowSubscriptionOverlay + .value = false; + }); + }, + shape: + RoundedRectangleBorder( + borderRadius: + BorderRadius + .circular( + 15)), + color: context + .color.secondaryColor, + elevation: 0, + child: Text("Subscribe") + .color(context.color + .tertiaryColor), + ), + SizedBox( + height: 15, + ), + // bottomNavBar() + ], + ), + ), + ], + )), + ), + ), + ), + ), + ), + ), + ), + ); + // return SizedBox.shrink(); + }) + ], + ), + )), + ), + ), + ); + } + + Widget advertismentLable() { + // if (property?.promoted == false || property?.promoted == null) { + // return const SizedBox.shrink(); + // } + + return PositionedDirectional( + start: 20, + top: 20, + child: SizedBox( + height: 32, + child: Row( + children: [ + if ((property?.promoted == true && + property?.promoted != null)) ...[ + Container( + width: 83, + height: 32, + alignment: Alignment.center, + decoration: BoxDecoration( + color: context.color.tertiaryColor, + borderRadius: BorderRadius.circular(4)), + child: Text(UiUtils.translate(context, 'featured')) + .color(context.color.buttonColor) + .size(context.font.small), + ), + const SizedBox( + width: 4, + ) + ], + if (property != null && + property?.allPropData['is_premium'] == true) ...[ + Container( + height: 32, + width: 32, + // margin: EdgeInsets.symmetric(horizontal: 0), + decoration: BoxDecoration( + color: context.color.tertiaryColor, + borderRadius: BorderRadius.circular(6)), + child: FittedBox( + fit: BoxFit.none, + child: SvgPicture.asset(AppIcons.promoted)), + ), + ] + ], + ), + )); + } + + double getBottomNavigationBarHeight(BuildContext context) { + try { + final RenderBox? renderBox = + appBarKey.currentContext?.findRenderObject() as RenderBox?; + return renderBox?.size.height ?? 0.0; + } catch (e) { + return 0; + } + } + + Widget bottomNavBar() { + /// IF property is added by current user then it will show promote button + if (!HiveUtils.isGuest()) { + if (int.parse(HiveUtils.getUserId() ?? "0") == property?.addedBy) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: SizedBox( + height: 65.rh(context), + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 10.0, horizontal: 0), + child: + BlocBuilder( + builder: (context, state) { + PropertyModel? model; + + if (state is FetchMyPropertiesSuccess) { + model = state.myProperty + .where((element) => element.id == property?.id) + .first; + } + + model ??= widget.property; + + var isPromoted = (model?.promoted); + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (!HiveUtils.isGuest()) ...[ + if (isPromoted == false && + (property?.status.toString() != "0")) ...[ + Expanded( + child: UiUtils.buildButton( + context, + disabled: (property?.status.toString() == "0"), + // padding: const EdgeInsets.symmetric(horizontal: 1), + outerPadding: const EdgeInsets.all( + 1, + ), + onPressed: () { + Navigator.pushNamed( + context, + Routes.createAdvertismentScreenRoute, + arguments: { + "model": property, + }, + ).then( + (value) { + setState(() {}); + }, + ); + }, + prefixWidget: Padding( + padding: const EdgeInsets.only(right: 6), + child: SvgPicture.asset( + AppIcons.promoted, + width: 14, + height: 14, + ), + ), + + fontSize: context.font.normal, + width: context.screenWidth / 3, + buttonTitle: UiUtils.translate(context, "feature"), + )), + const SizedBox( + width: 8, + ), + ], + ], + Expanded( + child: UiUtils.buildButton(context, + // padding: const EdgeInsets.symmetric(horizontal: 1), + outerPadding: const EdgeInsets.all(1), + onPressed: () async { + Category category = await context + .read() + .get(property!.category!.id!); + +// Extract parameter IDs from category + List? parameterIds = + category.parameterTypes?['parameters']; + +// Map parameters + List? mappedParameters = + parameterIds?.map((dynamic id) { + // Find index of parameter in property parameters list + int index = property?.parameters?.indexWhere( + (element) => element.id == id['id']) ?? + -1; + + // If parameter found, return it; otherwise, return the original value + return index != -1 + ? property!.parameters![index] + : id; + }).toList(); + Constant.addProperty.addAll({ + "category": Category( + category: property?.category!.category, + id: property?.category?.id!.toString(), + image: property?.category?.image, + parameterTypes: {"parameters": mappedParameters}, + ) + }); + + // log(" oiasdjalwd j$map"); + Navigator.pushNamed( + context, Routes.addPropertyDetailsScreen, + arguments: { + "details": { + "id": property?.id, + "catId": property?.category?.id, + "propType": property?.properyType, + "name": property?.title, + "desc": property?.description, + "city": property?.city, + "state": property?.state, + "country": property?.country, + "latitude": property?.latitude, + "longitude": property?.longitude, + "address": property?.address, + "client": property?.clientAddress, + "price": property?.price, + 'parms': property?.parameters, + "allPropData": property?.allPropData, + "images": property?.gallery + ?.map((e) => e.imageUrl) + .toList(), + "gallary_with_id": property?.gallery, + "rentduration": property?.rentduration, + "assign_facilities": + property?.assignedOutdoorFacility, + "titleImage": property?.titleImage + } + }); + }, + fontSize: context.font.normal, + width: context.screenWidth / 3, + prefixWidget: Padding( + padding: const EdgeInsets.only(right: 6.0), + child: SvgPicture.asset(AppIcons.edit), + ), + buttonTitle: UiUtils.translate(context, "edit")), + ), + const SizedBox( + width: 8, + ), + Expanded( + child: UiUtils.buildButton(context, + padding: const EdgeInsets.symmetric(horizontal: 1), + outerPadding: const EdgeInsets.all(1), + prefixWidget: Padding( + padding: const EdgeInsets.only(right: 6.0), + child: SvgPicture.asset( + AppIcons.delete, + color: context.color.buttonColor, + width: 14, + height: 14, + ), + ), onPressed: () async { + // //THIS IS FOR DEMO MODE + bool isPropertyActive = + property?.status.toString() == "1"; + + bool isDemoNumber = HiveUtils.getUserDetails() + .mobile == + "${Constant.demoCountryCode}${Constant.demoMobileNumber}"; + + if (Constant.isDemoModeOn && + isPropertyActive && + isDemoNumber) { + HelperUtils.showSnackBarMessage(context, + "Active property cannot be deleted in demo app."); + + return; + } + + var delete = await UiUtils.showBlurredDialoge( + context, + dialoge: BlurredDialogBox( + title: UiUtils.translate( + context, + "deleteBtnLbl", + ), + content: Text( + UiUtils.translate( + context, "deletepropertywarning"), + ), + ), + ); + if (delete == true) { + Future.delayed( + Duration.zero, + () { + // if (Constant.isDemoModeOn) { + // HelperUtils.showSnackBarMessage( + // context, + // UiUtils.getTranslatedLabel( + // context, "thisActionNotValidDemo")); + // } else { + context + .read() + .delete(property!.id!); + // } + }, + ); + } + }, + fontSize: context.font.normal, + width: context.screenWidth / 3.2, + buttonTitle: + UiUtils.translate(context, "deleteBtnLbl")), + ), + ], + ); + }, + ), + ), + ), + ); + } + } + + return SizedBox( + height: 65.rh(context), + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 20), + child: Row( + children: [ + Expanded(child: callButton()), + const SizedBox( + width: 8, + ), + Expanded(child: messageButton()), + const SizedBox( + width: 8, + ), + Expanded(child: chatButton()), + ], + ), + ), + + // ClipRRect( + // child: BackdropFilter( + // filter: ImageFilter.blur(sigmaY: 3, sigmaX: 3), + // child: Container( + // color: Colors.black.withOpacity(0.2), + // child: MaterialButton( + // minWidth: double.infinity, + // height: double.infinity, + // onPressed: () {}, + // child: Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Icon( + // Icons.lock, + // color: context.color.secondaryColor, + // ), + // Text("Subscribe") + // .bold(weight: FontWeight.w700) + // .size(context.font.larger) + // .color(Colors.white), + // ], + // ), + // ), + // ), + // ), + // ) + ], + ), + ); + } + + String statusText(String text) { + if (text == "1") { + return UiUtils.translate(context, "active"); + } else if (text == "0") { + return UiUtils.translate(context, "deactive"); + } + return ""; + } + + Widget setInterest() { + // check if list has this id or not + bool interestedProperty = + Constant.interestedPropertyIds.contains(widget.property?.id); + + /// default icon + dynamic icon = AppIcons.interested; + + /// first priority is Constant list . + if (interestedProperty == true || widget.property?.isInterested == 1) { + /// If list has id or our property is interested so we are gonna show icon of No Interest + icon = Icons.not_interested_outlined; + } + + return BlocConsumer( + listener: (context, state) {}, + builder: (context, state) { + if (state is ChangeInterestInPropertySuccess) { + if (state.interest == PropertyInterest.interested) { + //If interested show no interested icon + icon = Icons.not_interested_outlined; + } else { + icon = AppIcons.interested; + } + } + + return Expanded( + flex: 1, + child: UiUtils.buildButton( + context, + height: 48, + outerPadding: const EdgeInsets.all(1), + isInProgress: state is ChangeInterestInPropertyInProgress, + onPressed: () { + PropertyInterest interest; + + bool contains = + Constant.interestedPropertyIds.contains(widget.property!.id!); + + if (contains == true || widget.property!.isInterested == 1) { + //change to not interested + interest = PropertyInterest.notInterested; + } else { + //change to not unterested + interest = PropertyInterest.interested; + } + context.read().changeInterest( + propertyId: widget.property!.id!.toString(), + interest: interest); + }, + buttonTitle: (icon == Icons.not_interested_outlined + ? UiUtils.translate(context, "interested") + : UiUtils.translate(context, "interest")), + fontSize: context.font.large, + prefixWidget: Padding( + padding: const EdgeInsetsDirectional.only(end: 14), + child: (icon is String) + ? SvgPicture.asset( + icon, + width: 22, + height: 22, + ) + : Icon( + icon, + color: Theme.of(context).colorScheme.buttonColor, + size: 22, + ), + ), + ), + ); + }, + ); + } + + bool isDisabledEnquireButton(state, id) { + if (state is EnquiryIdsLocalState) { + if (state.ids?.contains(id.toString()) ?? false) { + return true; + } else { + return false; + } + } + return false; + } + + bool showIcon(state, id) { + if (state is EnquiryIdsLocalState) { + if (state.ids?.contains(id.toString()) ?? false) { + return false; + } else { + return true; + } + } + return true; + } + + String setLable(state, id) { + if (state is EnquiryIdsLocalState) { + if (state.ids?.contains(id.toString()) ?? false) { + return UiUtils.translate( + context, + "sent", + ); + } else { + return UiUtils.translate( + context, + "sendEnqBtnLbl", + ); + } + } + return ""; + } + + Widget callButton() { + return UiUtils.buildButton(context, + fontSize: context.font.large, + outerPadding: const EdgeInsets.all(1), + buttonTitle: UiUtils.translate(context, "call"), + width: 35, + onPressed: _onTapCall, + prefixWidget: Padding( + padding: const EdgeInsets.only(right: 3.0), + child: SizedBox( + width: 16, + height: 16, + child: UiUtils.getSvg(AppIcons.call, color: Colors.white)), + )); + } + + Widget messageButton() { + return UiUtils.buildButton(context, + fontSize: context.font.large, + outerPadding: const EdgeInsets.all(1), + buttonTitle: UiUtils.translate(context, "sms"), + width: 35, + onPressed: _onTapMessage, + prefixWidget: SizedBox( + width: 16, + height: 16, + child: Padding( + padding: const EdgeInsets.only(right: 3.0), + child: UiUtils.getSvg(AppIcons.whatsapp, + color: context.color.buttonColor), + ), + )); + } + + Widget chatButton() { + return UiUtils.buildButton(context, + fontSize: context.font.large, + outerPadding: const EdgeInsets.all(1), + buttonTitle: UiUtils.translate(context, "chat"), + width: 35, + onPressed: _onTapChat, + prefixWidget: SizedBox( + width: 16, + height: 16, + child: Padding( + padding: const EdgeInsets.only(right: 3.0), + child: + UiUtils.getSvg(AppIcons.chat, color: context.color.buttonColor), + ), + )); + } + + _onTapCall() async { + print("AAA $isPremiumUser"); + if (isPremiumProperty && !isPremiumUser) { + GuestChecker.check(onNotGuest: () { + shouldShowSubscriptionOverlay.value = true; + + return; + }); + return; + } + + var contactNumber = widget.property?.customerNumber; + + var url = Uri.parse("tel: $contactNumber"); //{contactNumber.data} + if (await canLaunchUrl(url)) { + await launchUrl(url); + } else { + throw 'Could not launch $url'; + } + } + + _onTapMessage() async { + if (isPremiumProperty && !isPremiumUser) { + GuestChecker.check( + onNotGuest: () { + shouldShowSubscriptionOverlay.value = true; + + return; + }, + ); + return; + } + + var contactNumber = widget.property?.customerNumber; + + var url = Uri.parse("sms:$contactNumber"); //{contactNumber.data} + if (await canLaunchUrl(url)) { + await launchUrl(url); + } else { + throw 'Could not launch $url'; + } + } + + _onTapChat() { + GuestChecker.check(onNotGuest: () { + if (isPremiumProperty && !isPremiumUser) { + shouldShowSubscriptionOverlay.value = true; + return; + } + Navigator.push(context, BlurredRouter( + builder: (context) { + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => SendMessageCubit(), + ), + BlocProvider( + create: (context) => LoadChatMessagesCubit(), + ), + BlocProvider( + create: (context) => DeleteMessageCubit(), + ), + ], + child: ChatScreen( + profilePicture: property?.customerProfile ?? "", + userName: property?.customerName ?? "", + propertyImage: property?.titleImage ?? "", + proeprtyTitle: property?.title ?? "", + userId: (property?.addedBy).toString(), + from: "property", + propertyId: (property?.id).toString(), + ), + ); + }, + )); + }); + } +} + +class InterestedUserListWidget extends StatefulWidget { + const InterestedUserListWidget({ + super.key, + required this.totalCount, + required this.interestedUserCubitReference, + }); + final String totalCount; + final GetInterestedUserCubit interestedUserCubitReference; + + @override + State createState() => + _InterestedUserListWidgetState(); +} + +class _InterestedUserListWidgetState extends State { + final ScrollController _bottomSheetScrollController = ScrollController(); + @override + void initState() { + _bottomSheetScrollController.addListener(() { + if (_bottomSheetScrollController.isEndReached()) { + if (widget.interestedUserCubitReference.hasMoreData()) { + widget.interestedUserCubitReference.fetchMore(); + } + } + }); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return ClipRRect( + clipBehavior: Clip.antiAlias, + borderRadius: BorderRadius.circular(10), + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 3, + sigmaY: 10, + ), + child: ScrollConfiguration( + behavior: RemoveGlow(), + child: SingleChildScrollView( + controller: _bottomSheetScrollController, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(14.0), + child: const Text("Interested Users") + .size(context.font.larger) + .bold(), + ), + // SizedBox( + // height: 10, + // ), + // SizedBox( + // child: Container( + // width: context.screenWidth, + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // Text("Interested User Count") + // .size(context.font.large) + // .bold(), + // Text("${widget.totalCount}") + // ]), + // // color: Colors.red, + // ), + // ), + // const SizedBox( + // height: 10, + // ), + BlocBuilder( + bloc: widget.interestedUserCubitReference, + builder: (context, state) { + if (state is GetInterestedUserInProgress) { + return Center(child: UiUtils.progress()); + } + + if (state is GetInterestedUserSuccess) { + if (state.list.isEmpty) { + return const Center( + child: Text("No data found"), + ); + } + + return ListView.builder( + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + InterestedUserModel interestedUser = + state.list[index]; + + return InterestedUserCard( + interestedUser: interestedUser); + }, + itemCount: state.list.length, + shrinkWrap: true, + ); + } + return Container(); + }, + ), + ], + ), + ), + ), + ), + ); + } +} + +class InterestedUserCard extends StatelessWidget { + const InterestedUserCard({ + super.key, + required this.interestedUser, + }); + + final InterestedUserModel interestedUser; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(14.0), + child: Container( + child: Row( + children: [ + // CircleAvatar(radius: 25, backgroundImage: SvgPro), + Container( + width: 50, + height: 50, + clipBehavior: Clip.antiAlias, + decoration: const BoxDecoration( + shape: BoxShape.circle, + // color: Colors.red, + ), + child: UiUtils.getImage(interestedUser.image ?? ""), + ), + const SizedBox( + width: 10, + ), + Expanded( + flex: 3, + child: Text(interestedUser.name ?? "").setMaxLines(lines: 1), + ), + const SizedBox( + width: 10, + ), + const Spacer(), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + onPressed: () async { + await launchUrl( + Uri.parse("mailto:${interestedUser.email}"), + mode: LaunchMode.externalApplication); + }, + icon: Icon( + Icons.email, + color: context.color.tertiaryColor, + )), + IconButton( + onPressed: () async { + await launchUrl(Uri.parse("tel:${interestedUser.mobile}"), + mode: LaunchMode.externalApplication); + }, + color: context.color.tertiaryColor, + icon: const Icon(Icons.call)), + ], + ) + ], + ), + ), + ); + } +} + +class GoogleMapScreen extends StatefulWidget { + final double latitude; + final double longitude; + const GoogleMapScreen({ + super.key, + required this.latitude, + required this.longitude, + required CameraPosition kInitialPlace, + required Completer controller, + }) : _kInitialPlace = kInitialPlace, + _controller = controller; + + final CameraPosition _kInitialPlace; + final Completer _controller; + + @override + State createState() => _GoogleMapScreenState(); +} + +class _GoogleMapScreenState extends State { + bool isGoogleMapVisible = false; + + @override + void initState() { + Future.delayed(const Duration(milliseconds: 500), () { + isGoogleMapVisible = true; + setState(() {}); + }); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + isGoogleMapVisible = false; + setState(() {}); + await Future.delayed(const Duration(milliseconds: 500)); + Future.delayed( + Duration.zero, + () { + Navigator.pop(context); + }, + ); + return false; + }, + child: Builder(builder: (context) { + if (!isGoogleMapVisible) { + return Center(child: UiUtils.progress()); + } + return GoogleMap( + myLocationButtonEnabled: false, + gestureRecognizers: >{ + f.Factory( + () => EagerGestureRecognizer(), + ), + }, + markers: { + Marker( + markerId: const MarkerId("1"), + position: LatLng(widget.latitude, widget.longitude)) + }, + mapType: AppSettings.googleMapType, + initialCameraPosition: widget._kInitialPlace, + onMapCreated: (GoogleMapController controller) { + if (!widget._controller.isCompleted) { + widget._controller.complete(controller); + } + }, + ); + }), + ); + } +} + +class AgentProfileWidget extends StatelessWidget { + final bool hideDetails; + const AgentProfileWidget({ + super.key, + required this.hideDetails, + required this.widget, + }); + + final PropertyDetails widget; + + @override + Widget build(BuildContext context) { + return HideDetailsBlur( + hide: hideDetails, + sigmaX: 4, + sigmaY: 4, + child: Row( + children: [ + GestureDetector( + onTap: () { + if (hideDetails) return; + + UiUtils.showFullScreenImage(context, + provider: + NetworkImage(widget.property?.customerProfile ?? "")); + }, + child: Container( + width: 70, + height: 70, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Colors.grey.shade200, + borderRadius: BorderRadius.circular(10)), + child: UiUtils.getImage(widget.property?.customerProfile ?? "", + fit: BoxFit.cover) + + // CachedNetworkImage( + // imageUrl: widget.propertyData?.customerProfile ?? "", + // fit: BoxFit.cover, + // ), + + ), + ), + const SizedBox( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(widget.property?.customerName ?? "") + .size(context.font.large) + .bold(), + Text(widget.property?.customerEmail ?? ""), + ], + ), + ) + ], + ), + ); + } +} + +class OutdoorFacilityListWidget extends StatelessWidget { + final List outdoorFacilityList; + const OutdoorFacilityListWidget({Key? key, required this.outdoorFacilityList}) + : super(key: key); + + @override + Widget build(BuildContext context) { + CrossAxisAlignment getCrossAxisAlignment(int columnIndex) { + if (columnIndex == 1) { + return CrossAxisAlignment.center; + } else if (columnIndex == 2) { + return CrossAxisAlignment.end; + } else { + return CrossAxisAlignment.start; + } + } + + return GridView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3), + itemCount: outdoorFacilityList.length, + itemBuilder: (context, index) { + AssignedOutdoorFacility facility = outdoorFacilityList[index]; + + return Column( + //crossAxisAlignment: getCrossAxisAlignment(columnIndex), + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + // width: 60, + // height: 60, + decoration: BoxDecoration( + // shape: BoxShape.circle, + borderRadius: BorderRadius.circular(15), + color: context.color.tertiaryColor.withOpacity(0.2)), + child: Padding( + padding: const EdgeInsets.all(10.0), + child: SizedBox( + width: 36, + height: 36, + child: UiUtils.imageType( + facility.image ?? "", + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null, + // fit: BoxFit.cover, + width: 20, + height: 20, + ), + ), + ), + ), + const SizedBox(height: 8), + Text(facility.name ?? "") + .centerAlign() + .size(context.font.normal) + .color(context.color.textColorDark) + .setMaxLines(lines: 2), + const SizedBox(height: 2), + Text("${facility.distance} KM") + .centerAlign() + .size(context.font.small) + .color(context.color.textLightColor) + .setMaxLines(lines: 1) + ], + ), + ], + ); + }, + ); + } +} + +class HideDetailsBlur extends StatelessWidget { + final Widget child; + final bool hide; + final double? sigmaX; + final double? sigmaY; + const HideDetailsBlur( + {super.key, + required this.child, + required this.hide, + this.sigmaX, + this.sigmaY}); + + @override + Widget build(BuildContext context) { + return ClipRRect( + child: Stack( + // fit: StackFit.expand, + children: [ + child, + if (hide) + BackdropFilter( + filter: ImageFilter.blur(sigmaY: sigmaY ?? 3, sigmaX: sigmaX ?? 4), + child: Container(), + ) + ], + )); + } +} diff --git a/lib/Ui/screens/proprties/viewAll.dart b/lib/Ui/screens/proprties/viewAll.dart new file mode 100644 index 0000000..6b71ff4 --- /dev/null +++ b/lib/Ui/screens/proprties/viewAll.dart @@ -0,0 +1,164 @@ +import 'package:ebroker/Ui/screens/home/Widgets/property_horizontal_card.dart'; +import 'package:ebroker/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import 'package:ebroker/Ui/screens/widgets/Erros/something_went_wrong.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/helper_utils.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../app/routes.dart'; +import '../../../data/model/property_model.dart'; + +///In this file https://dart.dev/language/generics generic types are used For more info you can see this + +///This [PropertySuccessStateWireframe] this will force class to have properties list + +abstract class PropertySuccessStateWireframe { + abstract List properties; + abstract bool isLoadingMore; +} + +///this will force class to have error field +abstract class PropertyErrorStateWireframe { + dynamic error; +} + +///This implementation is for cubit this will force property cubit to implement this methods. +abstract class PropertyCubitWireframe { + void fetch(); + bool hasMoreData(); + void fetchMore(); +} + +class ViewAllScreen, C> extends StatefulWidget { + final String title; + final StateMap map; + ViewAllScreen({ + Key? key, + required this.title, + required this.map, + }) : super(key: key) { + assert(T is! PropertyErrorStateWireframe, + "Please Extend PropertyErrorStateWireframe in cubit"); + } + + void open(BuildContext context) { + Navigator.push(context, BlurredRouter( + builder: (context) { + return ViewAllScreen(title: title, map: map); + }, + )); + } + + @override + _ViewAllScreenState createState() => _ViewAllScreenState(); +} + +class _ViewAllScreenState, C> + extends State { + final ScrollController _pageScrollListener = ScrollController(); + + @override + void initState() { + _pageScrollListener.addListener(onPageEnd); + + super.initState(); + } + + @override + void dispose() { + _pageScrollListener.dispose(); + super.dispose(); + } + + bool isSubtype() => [] is List; + void onPageEnd() { + ///This is extension which will check if we reached end or not + if (_pageScrollListener.isEndReached()) { + if (isSubtype()) { + if (read().hasMoreData()) { + read().fetchMore(); + } + } + } + } + + dynamic read() { + return context.read(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.primaryColor, + // appBar: AppBar( + // backgroundColor: context.color.secondaryColor, + // elevation: 0, + // iconTheme: IconThemeData(color: context.color.teritoryColor), + // title: Text( + // widget.title, + // ).color(context.color.teritoryColor).size(context.font.large), + // ), + appBar: UiUtils.buildAppBar(context, + title: widget.title, showBackButton: true), +// body: Container(), + body: BlocBuilder(builder: (context, state) { + return widget.map._buildState(state, _pageScrollListener); + }), + ); + } +} + +///From generic type we are getting state so we can return ui according to that state +class StateMap { + Widget _buildState(dynamic state, ScrollController controller) { + if (state is INITIAL) { + return Container(); + } + if (state is PROGRESS) { + return Center(child: UiUtils.progress()); + } + if (state is FAIL) { + return const SomethingWentWrong(); + } + + if (state is SUCCESS) { + return Column( + children: [ + Expanded( + child: ScrollConfiguration( + behavior: RemoveGlow(), + child: ListView.builder( + controller: controller, + padding: const EdgeInsets.all(20), + itemBuilder: (context, index) { + PropertyModel model = state.properties[index]; + return GestureDetector( + onTap: () { + HelperUtils.goToNextPage( + Routes.propertyDetails, + context, + false, + args: { + 'propertyData': model, + 'propertiesList': state.properties, + 'fromMyProperty': false, + }, + ); + }, + child: PropertyHorizontalCard(property: model)); + }, + itemCount: state.properties.length, + ), + ), + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ); + } + + return Container(); + } +} diff --git a/lib/Ui/screens/proprties/widgets/report_property_widget.dart b/lib/Ui/screens/proprties/widgets/report_property_widget.dart new file mode 100644 index 0000000..03f060c --- /dev/null +++ b/lib/Ui/screens/proprties/widgets/report_property_widget.dart @@ -0,0 +1,125 @@ +import 'package:ebroker/Ui/screens/widgets/blurred_dialoge_box.dart'; +import 'package:ebroker/data/cubits/Report/property_report_cubit.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; + +import '../../../../utils/AppIcon.dart'; +import '../../../../utils/guestChecker.dart'; +import '../../Report/report_property_screen.dart'; + +class ReportPropertyButton extends StatefulWidget { + final int propertyId; + final Function() onSuccess; + const ReportPropertyButton( + {Key? key, required this.propertyId, required this.onSuccess}) + : super(key: key); + + @override + State createState() => _ReportPropertyButtonState(); +} + +class _ReportPropertyButtonState extends State { + bool shouldReport = true; + void _onTapYes(int propertyId) { + _bottomSheet(propertyId); + } + + _onTapNo() { + shouldReport = false; + setState(() {}); + } + + void _bottomSheet(int propertyId) { + PropertyReportCubit cubit = BlocProvider.of(context); + UiUtils.showBlurredDialoge(context, + dialoge: EmptyDialogBox( + child: AlertDialog( + backgroundColor: context.color.secondaryColor, + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), + content: BlocProvider.value( + value: cubit, + child: ReportPropertyScreen(propertyId: propertyId), + ), + ))).then((value) { + widget.onSuccess.call(); + }); + } + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + if (shouldReport == false) { + return SizedBox.shrink(); + } + return Container( + height: 135, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: isDark + ? widgetsBorderColorLight.withOpacity(0.1) + : widgetsBorderColorLight, + width: 1.5)), + child: Padding( + padding: const EdgeInsets.all(15.0), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("didYoufindProblem".translate(context)) + .setMaxLines(lines: 2) + .bold(weight: FontWeight.w100) + .size(context.font.larger), + const Spacer(), + Row( + children: [ + MaterialButton( + onPressed: () { + GuestChecker.check(onNotGuest: () { + _onTapYes.call(widget.propertyId); + }); + }, + textColor: context.color.tertiaryColor, + shape: RoundedRectangleBorder( + side: BorderSide( + color: isDark + ? widgetsBorderColorLight.withOpacity(0.1) + : widgetsBorderColorLight), + borderRadius: BorderRadius.circular(20)), + child: Text("yes".translate(context))), + const SizedBox( + width: 10, + ), + MaterialButton( + onPressed: _onTapNo, + textColor: context.color.tertiaryColor, + shape: RoundedRectangleBorder( + side: BorderSide( + color: isDark + ? widgetsBorderColorLight.withOpacity(0.1) + : widgetsBorderColorLight), + borderRadius: BorderRadius.circular(20)), + child: Text("notReally".translate(context))) + ], + ), + ], + ), + ), + SvgPicture.asset( + Theme.of(context).brightness == Brightness.dark + ? AppIcons.reportDark + : AppIcons.report, + ) + ], + ), + ), + ); + } +} diff --git a/lib/Ui/screens/settings/contact_us.dart b/lib/Ui/screens/settings/contact_us.dart new file mode 100644 index 0000000..a1ae22a --- /dev/null +++ b/lib/Ui/screens/settings/contact_us.dart @@ -0,0 +1,337 @@ +import 'dart:ui' as ui; + +import 'package:ebroker/Ui/screens/widgets/blurred_dialoge_box.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../../../data/cubits/company_cubit.dart'; +import '../../../utils/AppIcon.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/custom_text_form_field.dart'; + +class ContactUs extends StatefulWidget { + const ContactUs({Key? key}) : super(key: key); + + @override + ContactUsState createState() => ContactUsState(); + + static Route route(RouteSettings routeSettings) { + return BlurredRouter(builder: (_) => const ContactUs()); + } +} + +class ContactUsState extends State { + @override + void initState() { + super.initState(); + Future.delayed(Duration.zero, (() { + if (context.read().state is CompanyInitial || + context.read().state is CompanyFetchFailure) { + context.read().fetchCompany(context); + } else { + // print("companyData Fetched already !! "); + } + })); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: UiUtils.buildAppBar(context, + title: UiUtils.translate(context, "contactUs"), showBackButton: true), + body: BlocBuilder(builder: (context, state) { + if (state is CompanyFetchProgress) { + return const Center( + child: CircularProgressIndicator(), + ); + } else if (state is CompanyFetchSuccess) { + return Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(UiUtils.translate(context, "howCanWeHelp")) + .color(context.color.textColorDark) + .size(context.font.larger) + .bold(weight: FontWeight.w700), + SizedBox( + height: 10.rh(context), + ), + Text(UiUtils.translate(context, "itLooksLikeYouHasError")) + .size(context.font.small) + .color(context.color.textLightColor), + SizedBox( + height: 15.rh(context), + ), + customTile(context, + title: UiUtils.translate(context, "callBtnLbl"), + onTap: () async { + var number1 = state.companyData.companyTel1; + var number2 = state.companyData.companyTel2; + + UiUtils.showBlurredDialoge(context, + dialoge: BlurredDialogBox( + title: "chooseNumber".translate(context), + showCancleButton: false, + barrierDismissable: true, + acceptTextColor: context.color.buttonColor, + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ListTile( + title: Text(number1.toString()).centerAlign(), + onTap: () async { + await launchUrl(Uri.parse("tel:${number1}")); + }, + ), + ListTile( + title: Text(number2.toString()).centerAlign(), + onTap: () async { + await launchUrl(Uri.parse("tel:${number2}")); + }, + ), + ], + ), + )); + }, svgImagePath: AppIcons.call), + SizedBox( + height: 15.rh(context), + ), + customTile(context, + title: UiUtils.translate(context, "companyEmailLbl"), + onTap: () { + var email = state.companyData.companyEmail; + showEmailDialoge(email); + }, svgImagePath: AppIcons.message) + ], + ), + ); + } else if (state is CompanyFetchFailure) { + return Center( + child: Text(state.errmsg), + ); + } else { + return const SizedBox.shrink(); + } + }), + ); + } + + showEmailDialoge(email) { + Navigator.push( + context, + BlurredRouter( + builder: (context) => EmailSendWidget(email: email), + )); + } + + Widget customTile(BuildContext context, + {required String title, + required String svgImagePath, + bool? isSwitchBox, + Function(dynamic value)? onTapSwitch, + dynamic switchValue, + required VoidCallback onTap}) { + return GestureDetector( + onTap: onTap, + child: Row( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity( + 0.10000000149011612, + ), + borderRadius: BorderRadius.circular(10), + ), + child: FittedBox( + fit: BoxFit.none, + child: UiUtils.getSvg(svgImagePath, + color: context.color.tertiaryColor)), + ), + SizedBox( + width: 25.rw(context), + ), + Text(title) + .bold(weight: FontWeight.w700) + .color(context.color.textColorDark), + const Spacer(), + if (isSwitchBox != true) + Container( + width: 32, + height: 32, + decoration: BoxDecoration( + border: + Border.all(color: context.color.borderColor, width: 1.5), + color: context.color.secondaryColor + .withOpacity(0.10000000149011612), + borderRadius: BorderRadius.circular(10), + ), + child: FittedBox( + fit: BoxFit.none, + child: SizedBox( + width: 8, + height: 15, + child: UiUtils.getSvg( + AppIcons.arrowRight, + color: context.color.textColorDark, + ), + ), + ), + ), + if (isSwitchBox ?? false) + Switch( + value: switchValue ?? false, + onChanged: (value) { + onTapSwitch?.call(value); + }, + ) + ], + ), + ); + } + + launchPathURL(bool isTel, String value) async { + late Uri redirecturi; + if (isTel) { + redirecturi = Uri.parse("tel: $value"); + } else { + redirecturi = Uri( + scheme: 'mailto', + path: value, + query: + 'subject=${Constant.appName}&body=${UiUtils.translate(context, "mailMsgLbl")}'); + } + + if (await canLaunchUrl(redirecturi)) { + await launchUrl(redirecturi); + } else { + throw 'Could not launch $redirecturi'; + } + } +} + +class EmailSendWidget extends StatefulWidget { + final String email; + const EmailSendWidget({ + Key? key, + required this.email, + }) : super(key: key); + + @override + State createState() => _EmailSendWidgetState(); +} + +class _EmailSendWidgetState extends State { + final TextEditingController _subject = TextEditingController(); + late final TextEditingController _email = + TextEditingController(text: widget.email); + final TextEditingController _text = TextEditingController(); + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white.withOpacity(0.0), + body: Center( + child: Container( + clipBehavior: Clip.antiAlias, + width: MediaQuery.of(context).size.width - 40, + decoration: BoxDecoration( + boxShadow: const [ + BoxShadow( + blurRadius: 3, color: ui.Color.fromARGB(255, 201, 201, 201)) + ], + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular( + 10, + ), + ), + child: Padding( + padding: const EdgeInsets.all( + 20, + ), + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + GestureDetector( + onTap: () { + Navigator.pop( + context, + ); + }, + child: Container( + decoration: BoxDecoration( + color: + context.color.tertiaryColor.withOpacity(0.0), + shape: BoxShape.circle, + ), + width: 40, + height: 40, + child: FittedBox( + fit: BoxFit.none, + child: UiUtils.getSvg(AppIcons.arrowLeft, + color: context.color.tertiaryColor))), + ) + ], + ), + SizedBox( + height: 20.rh(context), + ), + Text(UiUtils.translate(context, "sendEmail")), + SizedBox( + height: 15.rh(context), + ), + CustomTextFormField( + controller: _subject, + hintText: UiUtils.translate(context, "subject"), + ), + SizedBox( + height: 15.rh(context), + ), + CustomTextFormField( + controller: _email, + isReadOnly: true, + hintText: UiUtils.translate(context, "companyEmailLbl"), + ), + SizedBox( + height: 15.rh(context), + ), + CustomTextFormField( + controller: _text, + maxLine: 100, + hintText: UiUtils.translate(context, "writeSomething"), + minLine: 5, + ), + SizedBox( + height: 15.rh(context), + ), + UiUtils.buildButton(context, onPressed: () async { + Uri redirecturi = Uri( + scheme: 'mailto', + path: _email.text, + query: 'subject=${_subject.text}&body=${_text.text}'); + await launchUrl(redirecturi); + }, + height: 50.rh(context), + buttonTitle: UiUtils.translate(context, "sendEmail")) + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/settings/notification_detail.dart b/lib/Ui/screens/settings/notification_detail.dart new file mode 100644 index 0000000..7a516b9 --- /dev/null +++ b/lib/Ui/screens/settings/notification_detail.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; + +import '../../../app/routes.dart'; +import '../../../data/helper/designs.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import 'notifications.dart'; + +class NotificationDetail extends StatefulWidget { + const NotificationDetail({Key? key}) : super(key: key); + + @override + State createState() => _NotificationDetailState(); + + static Route route(RouteSettings routeSettings) { + return BlurredRouter( + builder: (_) => const NotificationDetail(), + ); + } +} + +class _NotificationDetailState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.primaryColor, + appBar: UiUtils.buildAppBar(context, + title: UiUtils.translate(context, "notifications"), + showBackButton: true), + body: ListView(children: [ + if (selectedNotification.image!.isNotEmpty) + setNetworkImg(selectedNotification.image!, + width: double.maxFinite, + height: 200.rh(context), + boxFit: BoxFit.cover), + const SizedBox(height: 10), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: detailWidget()) + ]), + ); + } + + @override + void dispose() { + Routes.currentRoute = Routes.previousCustomerRoute; + super.dispose(); + } + + detailWidget() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + selectedNotification.title!, + style: Theme.of(context) + .textTheme + .titleMedium! + .merge(const TextStyle(fontWeight: FontWeight.w500)), + ), + const SizedBox(height: 5), + Text( + selectedNotification.message!, + style: Theme.of(context).textTheme.bodySmall!, + ), + ]); + } +} diff --git a/lib/Ui/screens/settings/notifications.dart b/lib/Ui/screens/settings/notifications.dart new file mode 100644 index 0000000..b6a3ff1 --- /dev/null +++ b/lib/Ui/screens/settings/notifications.dart @@ -0,0 +1,258 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../app/routes.dart'; +import '../../../data/cubits/fetch_notifications_cubit.dart'; +import '../../../data/helper/custom_exception.dart'; +import '../../../data/helper/design_configs.dart'; +import '../../../data/model/notification_data.dart'; +import '../../../data/model/property_model.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/api.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/Erros/no_data_found.dart'; +import '../widgets/Erros/no_internet.dart'; +import '../widgets/Erros/something_went_wrong.dart'; +import '../widgets/shimmerLoadingContainer.dart'; + +late NotificationData selectedNotification; + +class Notifications extends StatefulWidget { + const Notifications({Key? key}) : super(key: key); + + @override + NotificationsState createState() => NotificationsState(); + + static Route route(RouteSettings routeSettings) { + return BlurredRouter( + builder: (_) => const Notifications(), + ); + } +} + +class NotificationsState extends State { + late final ScrollController _pageScrollController = ScrollController() + ..addListener(() { + if (_pageScrollController.isEndReached()) { + if (context.read().hasMoreData()) { + context.read().fetchNotificationsMore(); + } + } + }); + List propertyData = []; + @override + void initState() { + super.initState(); + context.read().fetchNotifications(); + } + + @override + void dispose() { + Routes.currentRoute = Routes.previousCustomerRoute; + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Theme.of(context).colorScheme.primaryColor, + appBar: UiUtils.buildAppBar( + context, + title: UiUtils.translate(context, "notifications"), + showBackButton: true, + ), + body: BlocBuilder( + builder: (context, state) { + if (state is FetchNotificationsInProgress) { + return buildNotificationShimmer(); + } + if (state is FetchNotificationsFailure) { + if (state.errorMessage is ApiException) { + if (state.errorMessage.errorMessage == "no-internet") { + return NoInternet( + onRetry: () { + context.read().fetchNotifications(); + }, + ); + } + } + + return const SomethingWentWrong(); + } + + if (state is FetchNotificationsSuccess) { + if (state.notificationdata.isEmpty) { + return NoDataFound( + onTap: () { + context.read().fetchNotifications(); + }, + ); + } + + return buildNotificationlistWidget(state); + } + + return const SizedBox.square(); + }), + ); + } + + Widget buildNotificationShimmer() { + return ListView.separated( + padding: const EdgeInsets.all(10), + separatorBuilder: (context, index) => const SizedBox( + height: 10, + ), + itemCount: 20, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + return SizedBox( + height: 55, + child: Row( + children: [ + const CustomShimmer( + width: 50, + height: 50, + borderRadius: 11, + ), + const SizedBox( + width: 5, + ), + Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomShimmer( + height: 7, + width: 200.rw(context), + ), + const SizedBox(height: 5), + CustomShimmer( + height: 7, + width: 100.rw(context), + ), + const SizedBox(height: 5), + CustomShimmer( + height: 7, + width: 150.rw(context), + ) + ], + ) + ], + ), + ); + }); + } + + buildNotificationlistWidget(FetchNotificationsSuccess state) { + return Column( + children: [ + Expanded( + child: ListView.separated( + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.all(10), + separatorBuilder: (context, index) => const SizedBox( + height: 2, + ), + itemCount: state.notificationdata.length, + itemBuilder: (context, index) { + NotificationData notificationData = + state.notificationdata[index]; + return GestureDetector( + onTap: () { + selectedNotification = notificationData; + if (notificationData.type == Constant.enquiryNotification) { + } else { + HelperUtils.goToNextPage( + Routes.notificationDetailPage, context, false); + } + }, + child: Container( + decoration: DesignConfig.boxDecorationBorder( + color: Theme.of(context).colorScheme.secondaryColor, + borderWidth: 1.5, + borderColor: context.color.borderColor, + radius: 10), + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 8), + child: Row(children: [ + ClipRRect( + clipBehavior: Clip.antiAliasWithSaveLayer, + borderRadius: + const BorderRadius.all(Radius.circular(15)), + child: UiUtils.getImage(notificationData.image!, + height: 53.rh(context), + width: 53.rw(context), + fit: BoxFit.fill), + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + notificationData.title!.firstUpperCase(), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .titleMedium! + .merge(const TextStyle( + fontWeight: FontWeight.w500)), + ), + Text( + notificationData.message!.firstUpperCase(), + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodySmall!, + ), + Text(notificationData.createdAt! + .formatDate() + .toString()) + .size(context.font.smaller) + .color(context.color.textLightColor) + ])), + ]), + ), + ); + }), + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ); + } + + Future> getPropertyById() async { + Map body = { + // ApiParams.id: propertysId,//String propertysId + }; + + var response = await HelperUtils.sendApiRequest( + Api.apiGetProprty, body, true, context, + passUserid: true); + var getdata = json.decode(response); + if (getdata != null) { + if (!getdata[Api.error]) { + List list = getdata['data']; + propertyData = + list.map((model) => PropertyModel.fromMap(model)).toList(); + } else { + throw CustomException(getdata[Api.message]); + } + } else { + Future.delayed( + Duration.zero, + () { + throw CustomException(UiUtils.translate(context, "nodatafound")); + }, + ); + } + return propertyData; + } +} diff --git a/lib/Ui/screens/settings/profile_setting.dart b/lib/Ui/screens/settings/profile_setting.dart new file mode 100644 index 0000000..910f751 --- /dev/null +++ b/lib/Ui/screens/settings/profile_setting.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../../../data/cubits/profile_setting_cubit.dart'; +import '../../../data/helper/widgets.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; + +class ProfileSettings extends StatefulWidget { + final String? title; + final String? param; + const ProfileSettings({Key? key, this.title, this.param}) : super(key: key); + + @override + ProfileSettingsState createState() => ProfileSettingsState(); + + static Route route(RouteSettings routeSettings) { + Map? arguments = routeSettings.arguments as Map?; + return BlurredRouter( + builder: (_) => ProfileSettings( + title: arguments?['title'] as String, + param: arguments?['param'] as String, + ), + ); + } +} + +class ProfileSettingsState extends State { + @override + void initState() { + super.initState(); + Future.delayed(Duration.zero, () { + context + .read() + .fetchProfileSetting(context, widget.param!, forceRefresh: true); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Theme.of(context).colorScheme.primaryColor, + appBar: UiUtils.buildAppBar(context, + title: widget.title!, showBackButton: true), + // appBar: Widgets.setAppbar(widget.title!, context, []), + body: BlocBuilder( + builder: (context, state) { + if (state is ProfileSettingFetchProgress) { + return Center( + child: UiUtils.progress( + normalProgressColor: context.color.tertiaryColor), + ); + } else if (state is ProfileSettingFetchSuccess) { + return contentWidget(state, context); + } else if (state is ProfileSettingFetchFailure) { + // log("HELLo"); + // return Center(child: Text(state.errmsg)); + return Widgets.noDataFound(state.errmsg); + } else { + return const SizedBox.shrink(); + } + }), + ); + } +} + +Widget contentWidget(ProfileSettingFetchSuccess state, BuildContext context) { + return SingleChildScrollView( + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Html( + data: state.data.toString(), + onAnchorTap: ( + url, + context, + attributes, + ) { + launchUrl(Uri.parse(url!), mode: LaunchMode.externalApplication); + }, + style: { + "table": Style( + backgroundColor: Colors.grey[50], + ), + "p": Style(color: context.color.textColorDark), + "p strong": Style( + color: context.color.tertiaryColor, fontSize: FontSize.larger), + "tr": Style( + // border: Border(bottom: BorderSide(color: Colors.grey)), + ), + "th": Style( + backgroundColor: Colors.grey, + border: const Border(bottom: BorderSide(color: Colors.black)), + ), + "td": Style(border: Border.all(color: Colors.grey, width: 0.5)), + 'h5': Style(maxLines: 2, textOverflow: TextOverflow.ellipsis), + }, + ), + /* child: WebView( + backgroundColor: ColorPrefs.lightBtnBGColor, + javascriptMode: JavascriptMode.unrestricted, + initialUrl: Uri.dataFromString(profileSettingData!, mimeType: 'text/html') + .toString(), //state.profileSettingData! + ),*/ + ); +} diff --git a/lib/Ui/screens/splash_screen.dart b/lib/Ui/screens/splash_screen.dart new file mode 100644 index 0000000..7f737a7 --- /dev/null +++ b/lib/Ui/screens/splash_screen.dart @@ -0,0 +1,331 @@ +// import 'dart:async'; + +import 'dart:async'; +import 'dart:developer'; + +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:ebroker/Ui/screens/widgets/Erros/no_internet.dart'; +import 'package:ebroker/app/default_app_setting.dart'; +import 'package:ebroker/app/routes.dart'; +// import 'package:flutter/services.dart'; +// import 'package:flutter_svg/flutter_svg.dart'; + +// import '../app/routes.dart'; +import 'package:ebroker/data/cubits/profile_setting_cubit.dart'; +import 'package:ebroker/data/cubits/system/fetch_language_cubit.dart'; +import 'package:ebroker/data/cubits/system/fetch_system_settings_cubit.dart'; +import 'package:ebroker/data/model/system_settings_model.dart'; +import 'package:ebroker/utils/AppIcon.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/api.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +// import 'package:ebroker/main.dart'; +// import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hive_flutter/adapters.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; +import 'package:permission_handler/permission_handler.dart'; + +import '../../app/app.dart'; +import '../../app/app_theme.dart'; +import '../../data/Repositories/system_repository.dart'; +import '../../data/cubits/auth/auth_state_cubit.dart'; +import '../../data/cubits/category/fetch_category_cubit.dart'; +import '../../data/cubits/outdoorfacility/fetch_outdoor_facility_list.dart'; +import '../../data/cubits/system/app_theme_cubit.dart'; +import '../../utils/constant.dart'; +import '../../utils/hive_keys.dart'; +import '../../utils/hive_utils.dart'; + +class SplashScreen extends StatefulWidget { + const SplashScreen({Key? key}) : super(key: key); + + @override + SplashScreenState createState() => SplashScreenState(); +} + +class SplashScreenState extends State + with TickerProviderStateMixin { + late AuthenticationState authenticationState; + + bool isTimerCompleted = false; + bool isSettingsLoaded = false; + bool isLanguageLoaded = false; + + @override + void initState() { + context.read().fetchCategories(); + context.read().fetch(); + locationPermission(); + + super.initState(); + getLanguage().then((value) { + isLanguageLoaded = true; + setState(() {}); + }); + + // languageSettingReceivePort.listen((message) { + // + // }); + // getDefaultLanguage( + // () { + // isLanguageLoaded = true; + // setState(() {}); + // }, + // ); + + checkIsUserAuthenticated(); + bool isDataAvailable = checkPersistedDataAvailibility(); + Connectivity().checkConnectivity().then((value) { + if (value == ConnectivityResult.none && !isDataAvailable) { + Navigator.pushReplacement(context, MaterialPageRoute( + builder: (context) { + return NoInternet( + onRetry: () async { + try { + await LoadAppSettings().load(); + if (context.color.brightness == Brightness.light) { + context.read().changeTheme(AppTheme.light); + } else { + context.read().changeTheme(AppTheme.dark); + } + } catch (e) { + print("no internet"); + } + Future.delayed( + Duration.zero, + () { + Navigator.pushReplacementNamed( + context, + Routes.splash, + ); + }, + ); + }, + ); + }, + )); + } + }); + startTimer(); + //get Currency Symbol from Admin Panel + Future.delayed(Duration.zero, () { + context.read().fetchProfileSetting( + context, + Api.currencySymbol, + ); + }); + } + + Future locationPermission() async { + if ((await Permission.location.status) == PermissionStatus.denied) { + await Permission.location.request(); + } + } + + @override + void dispose() { + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark); + super.dispose(); + } + + void checkIsUserAuthenticated() async { + authenticationState = context.read().state; + if (authenticationState == AuthenticationState.authenticated) { + ///Only load sensitive details if user is authenticated + ///This call will load sensitive details with settings + context.read().fetchSettings( + isAnonymouse: false, + forceRefresh: true, + ); + completeProfileCheck(); + } else { + //This call will hide sensitive details. + context.read().fetchSettings( + isAnonymouse: true, + forceRefresh: true, + ); + } + } + + Future startTimer() async { + Timer(const Duration(seconds: 1), () { + isTimerCompleted = true; + if (mounted) setState(() {}); + }); + } + + void navigateCheck() { + ({ + "timer": isTimerCompleted, + "setting": isSettingsLoaded, + "language": isLanguageLoaded + }).logg; + + if (isTimerCompleted && isSettingsLoaded && isLanguageLoaded) { + navigateToScreen(); + } + } + + void completeProfileCheck() { + if (HiveUtils.getUserDetails().name == "" || + HiveUtils.getUserDetails().email == "") { + Future.delayed( + const Duration(milliseconds: 100), + () { + Navigator.pushReplacementNamed( + context, + Routes.completeProfile, + arguments: { + "from": "login", + }, + ); + }, + ); + } + } + + void navigateToScreen() { + if (context + .read() + .getSetting(SystemSetting.maintenanceMode) == + "1") { + Future.delayed(Duration.zero, () { + Navigator.of(context).pushReplacementNamed( + Routes.maintenanceMode, + ); + }); + } else if (authenticationState == AuthenticationState.authenticated) { + Future.delayed(Duration.zero, () { + Navigator.of(context) + .pushReplacementNamed(Routes.main, arguments: {'from': "main"}); + }); + } else if (authenticationState == AuthenticationState.unAuthenticated) { + if (Hive.box(HiveKeys.userDetailsBox).get("isGuest") == true) { + Future.delayed(Duration.zero, () { + Navigator.of(context) + .pushReplacementNamed(Routes.main, arguments: {"from": "splash"}); + }); + } else { + Future.delayed(Duration.zero, () { + Navigator.of(context).pushReplacementNamed(Routes.login); + }); + } + } else if (authenticationState == AuthenticationState.firstTime) { + Future.delayed(Duration.zero, () { + Navigator.of(context).pushReplacementNamed(Routes.onboarding); + }); + } + } + + @override + Widget build(BuildContext context) { + SystemChrome.setEnabledSystemUIMode( + SystemUiMode.manual, + overlays: SystemUiOverlay.values, + ); + + navigateCheck(); + + return BlocListener( + listener: (context, state) {}, + child: BlocListener( + listener: (context, state) { + if (state is FetchSystemSettingsFailure) {} + if (state is FetchSystemSettingsSuccess) { + var setting = []; + if ((setting as List).isNotEmpty) { + if ((setting[0] as Map).containsKey("package_id")) { + Constant.subscriptionPackageId = ""; + } + } + + if (state.settings['data'].containsKey("demo_mode")) { + Constant.isDemoModeOn = state.settings['data']['demo_mode']; + } + isSettingsLoaded = true; + setState(() {}); + } + }, + child: AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: context.color.tertiaryColor, + ), + child: Scaffold( + backgroundColor: context.color.tertiaryColor, + body: Stack( + children: [ + Align( + alignment: Alignment.center, + child: SizedBox( + width: 150, + height: 150, + child: LoadAppSettings().svg( + appSettings.splashLogo!, + // color: context.color.secondaryColor, + )), + ) + ], + ), + ), + ), + ), + ); + } +} + +Future getDefaultLanguage(VoidCallback onSuccess) async { + try { + // await Hive.initFlutter();v + + await Hive.openBox(HiveKeys.languageBox); + await Hive.openBox(HiveKeys.userDetailsBox); + await Hive.openBox(HiveKeys.authBox); + + if (HiveUtils.getLanguage() == null || + HiveUtils.getLanguage()?['data'] == null) { + Map result = await SystemRepository().fetchSystemSettings( + isAnonymouse: true, + ); + + var code = (result['data']['default_language']); + + await Api.get( + url: Api.getLanguagae, + queryParameters: { + Api.languageCode: code, + }, + useAuthToken: false, + ).then((value) { + HiveUtils.storeLanguage({ + "code": value['data']['code'], + "data": value['data']['file_name'], + "name": value['data']['name'] + }); + onSuccess.call(); + }); + } else { + onSuccess.call(); + } + } catch (e, st) { + log("Error while load default language $st"); + throw st; + } +} + +bool checkPersistedDataAvailibility() { + int dataAvailibile = 0; + for (Type cubit in Constant.hydratedCubits) { + if (HydratedBloc.storage.read('$cubit') == null) { + } else { + dataAvailibile++; + } + } + if (dataAvailibile == Constant.hydratedCubits.length) { + return true; + } else { + return false; + } +} diff --git a/lib/Ui/screens/subscription/packages_list.dart b/lib/Ui/screens/subscription/packages_list.dart new file mode 100644 index 0000000..8db0d40 --- /dev/null +++ b/lib/Ui/screens/subscription/packages_list.dart @@ -0,0 +1,898 @@ +import 'dart:io'; + +import 'package:ebroker/Ui/screens/subscription/widget/current_package_card.dart'; +import 'package:ebroker/Ui/screens/subscription/widget/package_tile.dart'; +import 'package:ebroker/Ui/screens/subscription/widget/subscripton_feature_line.dart'; +import 'package:ebroker/data/cubits/subscription/assign_free_package.dart'; +import 'package:ebroker/data/cubits/subscription/assign_package.dart'; +import 'package:ebroker/data/helper/widgets.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/api.dart'; +import 'package:ebroker/utils/helper_utils.dart'; +import 'package:ebroker/utils/payment/InAppPurchase/inAppPurchaseManager.dart'; +import 'package:ebroker/utils/payment/lib/payment.dart'; +import 'package:ebroker/utils/payment/lib/payment_service.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; + +import '../../../data/model/subscription_pacakage_model.dart'; +import '../../../utils/AdMob/bannerAdLoadWidget.dart'; +import '../../../utils/AdMob/interstitialAdManager.dart'; +import '../../../utils/AppIcon.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/LiquidIndicator/src/liquid_circular_progress_indicator.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/Erros/no_data_found.dart'; +import '../widgets/Erros/no_internet.dart'; +import '../widgets/Erros/something_went_wrong.dart'; +import '../widgets/shimmerLoadingContainer.dart'; +import 'payment_gatways.dart'; + +class SubscriptionPackageListScreen extends StatefulWidget { + final String? from; + const SubscriptionPackageListScreen({super.key, this.from}); + static Route route(RouteSettings settings) { + Map? arguments = settings.arguments as Map?; + return BlurredRouter( + builder: (context) { + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => GetSubsctiptionPackageLimitsCubit(), + ), + BlocProvider( + create: (context) => AssignFreePackageCubit(), + ), + BlocProvider( + create: (context) => AssignInAppPackageCubit(), + ), + ], + child: SubscriptionPackageListScreen( + from: arguments?['from'], + ), + ); + + // return BlocProvider( + // create: (context) => GetSubsctiptionPackageLimitsCubit(), + // child: const SubscriptionPackageListScreen(), + // ); + }, + ); + } + + @override + State createState() => + _SubscriptionPackageListScreenState(); +} + +class _SubscriptionPackageListScreenState + extends State { + InterstitialAdManager interstitialAdManager = InterstitialAdManager(); + InAppPurchaseManager inAppPurchase = InAppPurchaseManager(); + final ScrollController _scrollController = ScrollController(); + @override + void initState() { + // logServerInit(); + _scrollController.addListener(() { + if (_scrollController.isEndReached()) { + if (context.read().hasMore()) { + context.read().fetchMorePackages(); + } + } + }); + + context.read().fetchPackages(); + interstitialAdManager.load(); + InAppPurchaseManager.getPendings(); + inAppPurchase.listenIAP(context); + PaymentGatways.initPaystack(); + + super.initState(); + } + + dynamic ifServiceUnlimited(dynamic text, {dynamic remining}) { + if (text == "unlimited") { + return UiUtils.translate(context, "unlimited"); + } + if (text == "not_available") { + return ""; + } + if (remining != null) { + return ""; + } + + return text; + } + + bool isUnlimited(int text, {dynamic remining}) { + if (text == 0) { + return true; + } + if (remining != null) { + return false; + } + + return false; + } + + int selectedPage = 0; + Future _onTapSubscribe(subscriptionPackage) async { + if (subscriptionPackage.price?.toInt() == 0) { + context.read().assign(subscriptionPackage.id!); + return; + } + if (Platform.isIOS) { + inAppPurchase.buy(subscriptionPackage!.iosProductId!, + subscriptionPackage.id!.toString()); + return; + } + if (!isPaymentGatewayOpen) { + PaymentService paymentService = PaymentService(); + paymentService.targetGatwayKey = AppSettings.enabledPaymentGatway; + paymentService.attachedGatways(gatways); + paymentService.setContext(context); + paymentService.setPackage(subscriptionPackage); + paymentService.pay(); + } + } + + @override + Widget build(BuildContext context) { + return RefreshIndicator( + backgroundColor: context.color.primaryColor, + color: context.color.tertiaryColor, + onRefresh: () async { + context.read().fetchPackages(); + + /// + /// + // mySubscriptions = context + // .read() + // .getSetting(SystemSetting.subscription); + + // if (mySubscriptions.isNotEmpty) { + // isLifeTimeSubscription = mySubscriptions[0]['end_date'] == null; + // } + // hasAlreadyPackage = mySubscriptions.isNotEmpty; + }, + child: Scaffold( + backgroundColor: context.color.primaryColor, + appBar: UiUtils.buildAppBar( + context, + showBackButton: true, + title: "Rencana Langganan", + ), + bottomNavigationBar: const BottomAppBar( + child: BannerAdWidget(bannerSize: AdSize.banner), + ), + body: WillPopScope( + onWillPop: () async { + await interstitialAdManager.show(); + return true; + }, + child: MultiBlocListener( + listeners: [ + BlocListener( + listener: (context, state) { + if (state is AssignInAppPackageSuccess) { + // Widgets.hideLoder(context); + context + .read() + .fetchSettings(isAnonymouse: false, forceRefresh: true); + HelperUtils.showSnackBarMessage( + context, "Package Assigned"); + } + }, + ), + ], + child: Builder(builder: (context) { + return BlocListener( + listener: (context, state) { + if (state is AssignFreePackageInProgress) { + Widgets.showLoader(context); + } + + if (state is AssignFreePackageSuccess) { + Widgets.hideLoder(context); + context + .read() + .fetchPackages(); + + HelperUtils.showSnackBarMessage( + context, "Free package is assigned"); + } + + if (state is AssignFreePackageFail) { + Widgets.hideLoder(context); + + HelperUtils.showSnackBarMessage( + context, "Failed to assign free package"); + } + }, + child: BlocConsumer( + listener: (context, FetchSubscriptionPackagesState state) {}, + builder: (context, state) { + if (state is FetchSubscriptionPackagesInProgress) { + return ListView.builder( + itemCount: 10, + shrinkWrap: true, + padding: const EdgeInsets.all(16), + itemBuilder: (context, index) { + return const Padding( + padding: EdgeInsets.symmetric( + vertical: 8.0, + ), + child: CustomShimmer( + height: 160, + ), + ); + }, + ); + } + if (state is FetchSubscriptionPackagesFailure) { + if (state.errorMessage is ApiException) { + if (state.errorMessage.errorMessage == "no-internet") { + return NoInternet( + onRetry: () { + context + .read() + .fetchPackages(); + }, + ); + } + } + print("THe error is ${state.errorMessage}"); + return const SomethingWentWrong(); + } + if (state is FetchSubscriptionPackagesSuccess) { + if (state.subscriptionPacakges.isEmpty) { + return NoDataFound( + onTap: () { + context + .read() + .fetchPackages(); + + setState(() {}); + }, + ); + } + + // return Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // CarouselSlider.builder( + // itemCount: state.subscriptionPacakges.length, + // itemBuilder: (BuildContext context, int itemIndex, + // int pageViewIndex) { + // SubscriptionPackageModel subscriptionPacakge = + // state.subscriptionPacakges[itemIndex]; + // return Container( + // width: context.screenWidth * 0.98, + // decoration: BoxDecoration( + // color: context.color.secondaryColor, + // borderRadius: BorderRadius.circular(18), + // border: Border.all( + // color: context.color.borderColor, + // width: 1.5)), + // child: Column( + // children: [ + // const SizedBox( + // height: 24, + // ), + // Text(subscriptionPacakge.name.toString()) + // .size(context.font.extraLarge) + // .color(context.color.tertiaryColor) + // .bold(weight: FontWeight.w600), + // const SizedBox( + // height: 14, + // ), + // Container( + // width: 186, + // height: 186, + // decoration: BoxDecoration( + // color: context.color.tertiaryColor + // .withOpacity(0.1), + // shape: BoxShape.circle, + // ), + // child: SvgPicture.asset( + // AppIcons.placeHolder), + // ), + // const SizedBox( + // height: 10, + // ), + // Padding( + // padding: const EdgeInsets.fromLTRB( + // 20.0, 10, 20, 10), + // child: Container( + // height: 75, + // decoration: BoxDecoration( + // borderRadius: + // BorderRadius.circular(14), + // border: Border.all( + // color: context + // .color.tertiaryColor, + // width: 1.5)), + // child: Padding( + // padding: const EdgeInsets.symmetric( + // horizontal: 18.0, vertical: 14), + // child: Row( + // mainAxisAlignment: + // MainAxisAlignment + // .spaceBetween, + // crossAxisAlignment: + // CrossAxisAlignment.center, + // children: [ + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // Text("30 Days") + // .size( + // context.font.larger) + // .bold( + // weight: FontWeight + // .w600), + // Text("50% Off").color( + // context.color + // .textLightColor) + // ], + // ), + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.end, + // children: [ + // Text(r"$549") + // .size( + // context.font.larger) + // .bold( + // weight: FontWeight + // .w600), + // Text(r"800").color(context + // .color.textLightColor) + // ], + // ) + // ], + // ), + // ), + // ), + // ), + // Padding( + // padding: const EdgeInsets.all(19.0), + // child: Column( + // children: [ + // PlanFacilityRow( + // count: subscriptionPacakge + // .advertisementLimit + // .toString(), + // facilityTitle: + // "Advertisement limit is", + // icon: AppIcons.ads), + // const SizedBox( + // height: 12, + // ), + // PlanFacilityRow( + // count: subscriptionPacakge + // .propertyLimit + // .toString(), + // facilityTitle: + // "Property limit is", + // icon: AppIcons.propertyLimites), + // const SizedBox( + // height: 12, + // ), + // PlanFacilityRow( + // count: + // "${subscriptionPacakge.duration}", + // facilityTitle: "Validity ", + // icon: AppIcons.days), + // ], + // ), + // ), + // ], + // ), + // ); + // }, + // options: CarouselOptions( + // autoPlay: false, + // enlargeCenterPage: true, + // onPageChanged: (index, reason) { + // selectedPage = index; + // setState(() {}); + // }, + // viewportFraction: 0.8, + // initialPage: 0, + // height: 420 + 72 + 15, + // // clipBehavior: Clip.antiAlias, + // disableCenter: true, + // enableInfiniteScroll: false), + // ), + // const SizedBox( + // height: 38, + // ), + // Indicator(state, context), + // const SizedBox( + // height: 38, + // ), + // MaterialButton( + // onPressed: () {}, + // height: 50, + // minWidth: context.screenWidth * 0.8, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(12), + // ), + // color: context.color.tertiaryColor, + // child: const Text("Subscribe Now") + // .color(context.color.buttonColor) + // .size(context.font.larger), + // ), + // ], + // ); + + return SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Column( + children: [ + ListView.builder( + controller: _scrollController, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: state.subscriptionPacakges.length, + padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), + itemBuilder: (context, index) { + SubscriptionPackageModel subscriptionPacakge = + state.subscriptionPacakges[index]; + + if (subscriptionPacakge.isActive == 1) { + return CurrentPackageTileCard( + package: subscriptionPacakge, + ); + } + + return Padding( + padding: + const EdgeInsets.symmetric(vertical: 8.0), + child: SubscriptionPackageTile( + package: subscriptionPacakge, + onTap: () { + _onTapSubscribe.call(subscriptionPacakge); + }, + ), + // child: buildPackageTile( + // context, + // subscriptionPacakge, + // ), + ); + }, + ), + if (state.isLoadingMore) UiUtils.progress(), + if (state.hasError) + const Text("Something went wrong") + ], + ), + ); + } + + return Container(); + }, + ), + ); + }), + ), + ), + ), + ); + } + + Row Indicator(FetchSubscriptionPackagesSuccess state, BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ...List.generate((state.subscriptionPacakges.length), (index) { + bool isSelected = selectedPage == index; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 3), + child: Container( + width: isSelected ? 24 : 8, + height: 8, + decoration: BoxDecoration( + border: isSelected + ? Border() + : Border.all(color: context.color.textColorDark), + color: isSelected + ? context.color.tertiaryColor + : Colors.transparent, + borderRadius: BorderRadius.circular(10), + ), + ), + ); + }) + ], + ); + } + + Widget PlanFacilityRow( + {required String icon, + required String facilityTitle, + required String count}) { + return Row( + children: [ + SvgPicture.asset( + icon, + width: 24, + height: 24, + color: context.color.tertiaryColor, + ), + const SizedBox( + width: 11, + ), + Text(facilityTitle + " " + count) + .size(context.font.large) + .color(context.color.textColorDark.withOpacity(0.8)) + ], + ); + } + + Widget currentPackageTile( + {required String name, + dynamic advertismentLimit, + dynamic propertyLimit, + dynamic duration, + dynamic startDate, + dynamic endDate, + dynamic advertismentRemining, + dynamic propertyRemining, + required String price}) { + /// + if (endDate != null) { + endDate = endDate.toString().formatDate(); + } + + return Container( + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(18), + ), + child: Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + SizedBox( + width: context.screenWidth, + child: UiUtils.getSvg( + AppIcons.headerCurve, + color: context.color.tertiaryColor, + fit: BoxFit.fitWidth, + ), + ), + PositionedDirectional( + start: 10.rw(context), + top: 8.rh(context), + child: Text(UiUtils.translate(context, "currentPackage")) + .size(context.font.larger) + .color(context.color.secondaryColor) + .bold(weight: FontWeight.w600), + ) + ], + ), + const SizedBox( + height: 5, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Text(name) + .size(context.font.larger) + .color(context.color.textColorDark) + .bold(weight: FontWeight.w600), + ), + const SizedBox( + height: 20, + ), + Row( + children: [ + SubscriptionFeatureLine( + title: UiUtils.translate(context, "adLimitIs"), + limit: PackageLimit(advertismentLimit), + isTime: false, + ), + // bulletPoint(context, + // "${UiUtils.getTranslatedLabel(context, "adLimitIs")} ${advertismentLimit == '' ? UiUtils.getTranslatedLabel(context, "lifetime") : ifServiceUnlimited(advertismentLimit, remining: advertismentRemining)}"), + const Spacer(), + if (!isUnlimited(advertismentLimit, + remining: advertismentRemining) && + advertismentLimit != "") + SizedBox( + height: 60, + width: 60, + child: LiquidCircularProgressIndicator( + value: double.parse(advertismentRemining) / + advertismentLimit, // Defaults to 0.5. + valueColor: AlwaysStoppedAnimation( + context.color.tertiaryColor.withOpacity(0.3), + ), // Defaults to the current Theme's accentColor. + backgroundColor: Colors + .white, // Defaults to the current Theme's backgroundColor. + borderColor: context.color.tertiaryColor, + borderWidth: 3.0, + direction: Axis.vertical, + + // The direction the liquid moves (Axis.vertical = bottom to top, Axis.horizontal = left to right). Defaults to Axis.vertical. + center: Text("$advertismentRemining/$advertismentLimit"), + ), + ), + ], + ), + SizedBox( + height: 5.rh(context), + ), + Row( + children: [ + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + // if (propertyLimit != null) SubscriptionFeatureLine(), + + SubscriptionFeatureLine( + title: UiUtils.translate(context, "propertyLimit"), + limit: PackageLimit(propertyLimit), + isTime: false, + ), + + // bulletPoint(context, + // "${UiUtils.getTranslatedLabel(context, "propertyLimit")} ${propertyLimit == '' ? UiUtils.getTranslatedLabel(context, "lifetime") : ifServiceUnlimited(propertyLimit, remining: propertyRemining)}"), + SizedBox( + height: 5.rh(context), + ), + // if (isLifeTimeSubscription) + // Row( + // children: [ + // SubscriptionFeatureLine(), + // SubscriptionFeatureLine( + // title: UiUtils.getTranslatedLabel( + // context, "propertyLimit"), + // limit: PackageLimit(endDate), + // isTime: true, + // ), + // // bulletPoint(context, + // // "${UiUtils.getTranslatedLabel(context, "validity")} ${endDate ?? UiUtils.getTranslatedLabel(context, "lifetime")} "), + // ], + // ), + SubscriptionFeatureLine( + title: UiUtils.translate(context, "validity"), + limit: null, + isTime: true, + timeLimit: UiUtils.translate(context, "packageStartedOn") + + startDate + + UiUtils.translate(context, "andPackageWillEndOn") + + endDate.toString(), + ), + ]), + const Spacer(), + if (!isUnlimited(propertyLimit, remining: propertyRemining) && + propertyLimit != "") + SizedBox( + height: 60, + width: 60, + child: LiquidCircularProgressIndicator( + value: double.parse(advertismentRemining) / + advertismentLimit, // Defaults to 0.5. + valueColor: AlwaysStoppedAnimation( + context.color.tertiaryColor.withOpacity(0.3), + ), // Defaults to the current Theme's accentColor. + backgroundColor: Colors + .white, // Defaults to the current Theme's backgroundColor. + borderColor: context.color.tertiaryColor, + borderWidth: 3.0, + direction: Axis.vertical, + + // The direction the liquid moves (Axis.vertical = bottom to top, Axis.horizontal = left to right). Defaults to Axis.vertical. + center: Text("$advertismentRemining/$advertismentLimit"), + ), + ), + ], + ), + ], + ), + ), + ); + } + + Widget buildPackageTile( + BuildContext context, + SubscriptionPackageModel subscriptionPacakge, + ) { + return Container( + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(18)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Stack( + children: [ + SizedBox( + width: context.screenWidth, + child: UiUtils.getSvg(AppIcons.headerCurve, + color: context.color.tertiaryColor, fit: BoxFit.fitWidth), + ), + PositionedDirectional( + start: 10.rw(context), + top: 8.rh(context), + child: Text(subscriptionPacakge.name ?? "") + .size(context.font.larger) + .color(context.color.secondaryColor) + .bold(weight: FontWeight.w600), + ) + ], + ), + const SizedBox( + height: 20, + ), + // if (subscriptionPacakge.advertisementLimit != "not_available") + SubscriptionFeatureLine( + limit: PackageLimit(subscriptionPacakge.advertisementLimit), + isTime: false, + title: UiUtils.translate(context, "adLimitIs"), + ), + // bulletPoint(context, + // "${UiUtils.getTranslatedLabel(context, "adLimitIs")} ${subscriptionPacakge.advertisementLimit == '' ? UiUtils.getTranslatedLabel(context, "lifetime") : ifServiceUnlimited(subscriptionPacakge.advertisementLimit)}"), + SizedBox( + height: 5.rh(context), + ), + Row( + children: [ + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + // if (subscriptionPacakge.propertyLimit != "not_available") + // SubscriptionFeatureLine(), + + SubscriptionFeatureLine( + limit: PackageLimit(subscriptionPacakge.propertyLimit), + isTime: false, + title: UiUtils.translate(context, "propertyLimit"), + ), + // bulletPoint(context, + // "${UiUtils.getTranslatedLabel(context, "propertyLimit")} ${subscriptionPacakge.propertyLimit == '' ? UiUtils.getTranslatedLabel(context, "lifetime") : ifServiceUnlimited(subscriptionPacakge.propertyLimit)}"), + SizedBox( + height: 5.rh(context), + ), + + SubscriptionFeatureLine( + limit: null, + isTime: true, + timeLimit: + "${subscriptionPacakge.duration} ${UiUtils.translate(context, "days")}", + title: UiUtils.translate(context, "validity"), + ), + // SubscriptionFeatureLine(), + ]), + // const Spacer(), + Expanded( + child: Padding( + padding: const EdgeInsetsDirectional.only(end: 15.0), + child: Container( + alignment: Alignment.center, + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(8)), + height: 39.rh(context), + constraints: BoxConstraints( + minWidth: 80.rw(context), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: Text( + ("${subscriptionPacakge.price}") + .toString() + .formatAmount(prefix: true), + style: const TextStyle(fontFamily: "ROBOTO"), + ) + .color(context.color.tertiaryColor) + .bold() + .size(context.font.large), + )), + ), + ) + ], + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: UiUtils.buildButton(context, onPressed: () async { + if (subscriptionPacakge.price?.toInt() == 0) { + context + .read() + .assign(subscriptionPacakge.id!); + return; + } + if (Platform.isIOS) { + inAppPurchase.buy( + // "android.test.purchased" ?? + subscriptionPacakge.iosProductId!, + subscriptionPacakge.id!.toString()); + return; + } + + PaymentService paymentService = PaymentService(); + paymentService.targetGatwayKey = AppSettings.enabledPaymentGatway; + paymentService.attachedGatways(gatways); + paymentService.setContext(context); + paymentService.setPackage(subscriptionPacakge); + paymentService.pay(); + // } else { + // var proceed = await UiUtils.showBlurredDialoge( + // context, + // sigmaX: 3, + // sigmaY: 3, + // dialoge: BlurredDialogBox( + // title: UiUtils.getTranslatedLabel(context, "warning"), + // cancelTextColor: context.color.textColorDark, + // acceptButtonName: + // UiUtils.getTranslatedLabel(context, "proceed"), + // content: Text( + // UiUtils.getTranslatedLabel( + // context, "currentPacakgeActiveWarning"), + // ), + // ), + // ); + // + // if (proceed == true) { + // Future.delayed( + // Duration.zero, + // () { + // ///This is to assign free package + // if (subscriptionPacakge.price?.toInt() == 0) { + // context + // .read() + // .assign(subscriptionPacakge.id!); + // return; + // } + // + // ///if the platform is IOS always open in app purchase + // if (Platform.isIOS) { + // inAppPurchase.buy( + // // "android.test.purchased" ?? + // subscriptionPacakge.iosProductId!, + // subscriptionPacakge.id!.toString(), + // ); + // return; + // } + // + // ///This is to pay via payment gateway + // PaymentService paymentService = PaymentService(); + // paymentService.targetGatwayKey = + // AppSettings.enabledPaymentGatway; + // paymentService.attachedGatways(gatways); + // paymentService.setContext(context); + // paymentService.setPackage(subscriptionPacakge); + // paymentService.pay(); + // // PaymentGatways.openEnabled(context, + // // subscriptionPacakge.price, subscriptionPacakge); + // }, + // ); + // } + // } + }, + radius: 9, + height: 33.rh(context), + buttonTitle: UiUtils.translate(context, "subscribe")), + ), + ], + ), + ); + } +} + +// logServerInit() async { +// socket = await WebSocket.connect( +// 'ws://192.168.0.103:5566/', +// ); +// } +// +// logS(String value, {String? name}) async { +// socket?.add(json.encode({"name": name, "log": value})); +// } diff --git a/lib/Ui/screens/subscription/payment_gatways.dart b/lib/Ui/screens/subscription/payment_gatways.dart new file mode 100644 index 0000000..615a2dc --- /dev/null +++ b/lib/Ui/screens/subscription/payment_gatways.dart @@ -0,0 +1,266 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:io'; + +import 'package:ebroker/app/app.dart'; +import 'package:ebroker/app/default_app_setting.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_paystack/flutter_paystack.dart'; +import 'package:razorpay_flutter/razorpay_flutter.dart'; + +import '../../../data/cubits/subscription/fetch_subscription_packages_cubit.dart'; +import '../../../data/cubits/system/fetch_system_settings_cubit.dart'; +import '../../../settings.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/hive_utils.dart'; +import '../../../utils/payment/gatways/paypal.dart'; +import '../../../utils/payment/gatways/stripe_service.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/blurred_dialoge_box.dart'; + +class PaymentGatways { + static PaystackPlugin paystackPlugin = PaystackPlugin(); + + static openEnabled(BuildContext context, dynamic price, dynamic package) { + if (AppSettings.enabledPaymentGatway == "paystack") { + paystack(context, price, package.id); + } else if (AppSettings.enabledPaymentGatway == "paypal") { + paypal(context, package); + } else if (AppSettings.enabledPaymentGatway == "razorpay") { + razorpay(context, price: price, package: package); + } else if (AppSettings.enabledPaymentGatway == "stripe") { + stripe(context, + packageId: package, price: double.parse(price.toString())); + } + } + + static String generateReference(String email) { + late String platform; + if (Platform.isIOS) { + platform = 'I'; + } else if (Platform.isAndroid) { + platform = 'A'; + } + String reference = + '${platform}_${email.split("@").first}_${DateTime.now().millisecondsSinceEpoch}'; + return reference; + } + + static void initPaystack() { + if (AppSettings.enabledPaymentGatway == "paystack") { + if (!paystackPlugin.sdkInitialized) { + paystackPlugin.initialize(publicKey: Constant.paystackKey); + } + } + } + + static void stripe(BuildContext context, + {required double price, required dynamic packageId}) { + openStripePaymentGateway( + amount: price, + onError: (message) {}, + onSuccess: () { + _purchase(context); + }, + metadata: {"packageId": packageId.id, "userId": HiveUtils.getUserId()}); + } + + static Future paystack( + BuildContext context, dynamic price, dynamic packageId) async { + Charge paystackCharge = Charge() + ..amount = (price! * 100).toInt() + ..email = HiveUtils.getUserDetails().email + ..currency = Constant.paystackCurrency + ..reference = generateReference(HiveUtils.getUserDetails().email!) + ..putMetaData("username", HiveUtils.getUserDetails().name) + ..putMetaData("package_id", packageId) + ..putMetaData("user_id", HiveUtils.getUserId()); + + CheckoutResponse checkoutResponse = await paystackPlugin.checkout(context, + logo: SizedBox( + height: 50, + width: 50, + child: LoadAppSettings().svg( + appSettings.splashLogo!, + )), + charge: paystackCharge, + method: CheckoutMethod.card); + + if (checkoutResponse.status) { + if (checkoutResponse.verify) { + Future.delayed( + Duration.zero, + () async { + await _purchase(context); + }, + ); + } + } else { + Future.delayed( + Duration.zero, + () { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "purchaseFailed")); + }, + ); + } + } + + static void paypal(BuildContext context, dynamic package) { + Navigator.push(context, BlurredRouter( + builder: (context) { + return PaypalWidget( + pacakge: package, + onSuccess: (msg) { + Navigator.pop(context, {"msg": msg, "type": "success"}); + }, + onFail: (msg) { + Navigator.pop(context, {"msg": msg, "type": "fail"}); + }, + ); + }, + )).then((dynamic value) { + //push and show dialog box about paypal success or failed, after that we call purchase method it will refresh API and check if package is purchased or not + if (value != null) { + Future.delayed( + const Duration(milliseconds: 1000), + () { + UiUtils.showBlurredDialoge(context, + dialoge: BlurredDialogBox( + title: UiUtils.translate(context, + value['type'] == 'success' ? "success" : "Failed"), + onAccept: () async { + if (value['type'] == 'success') { + _purchase(context); + } + }, + onCancel: () { + if (value['type'] == 'success') { + _purchase(context); + } + }, + isAcceptContainesPush: true, + content: Text(value['msg']))); + }, + ); + } + }); + } + + static void razorpay( + BuildContext context, { + required price, + required package, + }) { + final Razorpay razorpay = Razorpay(); + + var options = { + 'key': Constant.razorpayKey, + 'amount': price! * 100, + 'name': package.name, + 'description': '', + 'prefill': { + 'contact': HiveUtils.getUserDetails().mobile, + 'email': HiveUtils.getUserDetails().email + }, + "notes": {"package_id": package.id, "user_id": HiveUtils.getUserId()}, + }; + + if (Constant.razorpayKey != "") { + razorpay.open(options); + razorpay.on( + Razorpay.EVENT_PAYMENT_SUCCESS, + ( + PaymentSuccessResponse response, + ) async { + await _purchase(context); + }, + ); + razorpay.on( + Razorpay.EVENT_PAYMENT_ERROR, + (PaymentFailureResponse response) { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "purchaseFailed")); + }, + ); + razorpay.on( + Razorpay.EVENT_EXTERNAL_WALLET, + (e) {}, + ); + } else { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "setAPIkey")); + } + } + + static Future _purchase(BuildContext context) async { + try { + Future.delayed( + Duration.zero, + () { + context + .read() + .fetchSettings(isAnonymouse: false); + context.read().fetchPackages(); + + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "success"), + type: MessageType.success, messageDuration: 5); + + Navigator.of(context).popUntil((route) => route.isFirst); + }, + ); + } catch (e) { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "purchaseFailed"), + type: MessageType.error); + } + } +} + +// class PaymentService { +// BuildContext? _context; +// SubscriptionPackageModel? _modal; +// String? _targetGatwayKey; +// Gatway? _currentGatway; +// set targetGatwayKey(String key) { +// _targetGatwayKey = key; +// } + +// PaymentService setPackage(SubscriptionPackageModel modal) { +// _modal = modal; +// return this; +// } + +// PaymentService setContext(BuildContext context) { +// _context = context; +// return this; +// } + +// PaymentService attachedGatways(List paymentGatways) { +// if (_targetGatwayKey == null) { +// throw "Please set target gatway key"; +// } +// for (Gatway gatway in paymentGatways) { +// if (gatway.key == _targetGatwayKey) { +// _currentGatway = gatway; +// } +// } +// return this; +// } + +// void pay() async { +// if (_context == null) { +// throw "Please call setContext before use this"; +// } +// if (_modal == null) { +// throw "Please call setPackage"; +// } +// if (_currentGatway == null) { +// throw "please attach gatways"; +// } +// _currentGatway!.instance.setPackage(_modal!).pay(_context!); +// } +// } diff --git a/lib/Ui/screens/subscription/subscribe_screen.dart b/lib/Ui/screens/subscription/subscribe_screen.dart new file mode 100644 index 0000000..b0e52be --- /dev/null +++ b/lib/Ui/screens/subscription/subscribe_screen.dart @@ -0,0 +1,382 @@ +// ignore_for_file: must_be_immutable, depend_on_referenced_packages + +import 'dart:io'; + +import 'package:ebroker/app/app.dart'; +import 'package:ebroker/app/default_app_setting.dart'; +import 'package:ebroker/data/model/subscription_pacakage_model.dart'; +import 'package:ebroker/utils/AppIcon.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/api.dart'; +import 'package:ebroker/utils/hive_utils.dart'; +import 'package:ebroker/utils/payment/gatways/paypal.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_paystack/flutter_paystack.dart'; +import 'package:razorpay_flutter/razorpay_flutter.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +import '../../../data/cubits/subscription/fetch_subscription_packages_cubit.dart'; +import '../../../data/cubits/system/fetch_system_settings_cubit.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/helper_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/blurred_dialoge_box.dart'; + +/////THIS SCREEN IS NOT IN USE NOW + +class SubscriptionScreen extends StatefulWidget { + SubscriptionPackageModel pacakge; + final bool isPackageAlready; + SubscriptionScreen( + {super.key, required this.pacakge, required this.isPackageAlready}); + static Route route(RouteSettings settings) { + Map arguments = settings.arguments as Map; + return BlurredRouter( + builder: (context) { + return SubscriptionScreen( + pacakge: arguments['package'], + isPackageAlready: arguments['isPackageAlready'], + ); + }, + ); + } + + @override + State createState() => _SubscriptionScreenState(); +} + +class _SubscriptionScreenState extends State { + PaystackPlugin paystackPlugin = PaystackPlugin(); + final Razorpay _razorpay = Razorpay(); + int selectedPaymentMethod = 1; + late WebViewController controllerGlobal; + late Map paymentMethodIndex = { + 1: _paystack, + 2: _openPaypal, + 3: _openRazorPay + }; + + _openPaypal() { + Navigator.push(context, BlurredRouter( + builder: (context) { + return PaypalWidget( + pacakge: widget.pacakge, + onSuccess: (msg) { + Navigator.pop( + context, + { + "msg": msg, + "type": "success", + }, + ); + }, + onFail: (msg) { + Navigator.pop( + context, + { + "msg": msg, + "type": "fail", + }, + ); + }, + ); + }, + )).then((dynamic value) { + //push and show dialog box about paypal success or failed, after that we call purchase method it will refresh API and check if package is purchased or not + if (value != null) { + Future.delayed( + const Duration(milliseconds: 1000), + () { + UiUtils.showBlurredDialoge(context, + dialoge: BlurredDialogBox( + title: UiUtils.translate(context, + value['type'] == 'success' ? "success" : "Failed"), + onAccept: () async { + if (value['type'] == 'success') { + _purchase(); + } + }, + onCancel: () { + if (value['type'] == 'success') { + _purchase(); + } + }, + isAcceptContainesPush: true, + content: Text(value['msg']))); + }, + ); + } + }); + } + + _purchase() async { + try { + Future.delayed( + Duration.zero, + () { + context + .read() + .fetchSettings(isAnonymouse: false); + context.read().fetchPackages(); + + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "success"), + type: MessageType.success, messageDuration: 5); + + Navigator.of(context).popUntil((route) => route.isFirst); + }, + ); + } catch (e) { + HelperUtils.showSnackBarMessage(context, "purchaseFailed", + type: MessageType.error); + } + } + + getPaypalURL() async { + await Api.get(url: Api.paypal, queryParameters: { + Api.packageId: widget.pacakge.id, + "amount": widget.pacakge.price.toString() + }); + } + + _openRazorPay() async { + var options = { + 'key': Constant.razorpayKey, + 'amount': widget.pacakge.price! * 100, + 'name': widget.pacakge.name, + 'description': '', + 'prefill': { + 'contact': HiveUtils.getUserDetails().mobile, + 'email': HiveUtils.getUserDetails().email + }, + "notes": { + "package_id": widget.pacakge.id, + "user_id": HiveUtils.getUserId() + }, + }; + + if (Constant.razorpayKey != "") { + _razorpay.open(options); + _razorpay.on( + Razorpay.EVENT_PAYMENT_SUCCESS, + _razorpayHandlePaymentSuccess, + ); + _razorpay.on( + Razorpay.EVENT_PAYMENT_ERROR, + _razorpayHandlePaymentError, + ); + _razorpay.on( + Razorpay.EVENT_EXTERNAL_WALLET, + _razorpayHandleExternalWallet, + ); + } else { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "setAPIkey")); + } + } + + _paystack() async { + Charge paystackCharge = Charge() + ..amount = (widget.pacakge.price! * 100).toInt() + ..email = HiveUtils.getUserDetails().email + ..currency = Constant.paystackCurrency + ..reference = generateReference(HiveUtils.getUserDetails().email!) + ..putMetaData("username", HiveUtils.getUserDetails().name) + ..putMetaData("package_id", widget.pacakge.id) + ..putMetaData("user_id", HiveUtils.getUserId()); + + CheckoutResponse checkoutResponse = await paystackPlugin.checkout(context, + logo: SizedBox( + height: 50, + width: 50, + child: LoadAppSettings().svg(appSettings.placeholderLogo!, + color: context.color.tertiaryColor)), + charge: paystackCharge, + method: CheckoutMethod.card); + + if (checkoutResponse.status) { + if (checkoutResponse.verify) { + await _purchase(); + } + } else { + Future.delayed( + Duration.zero, + () { + HelperUtils.showSnackBarMessage(context, "purchaseFailed"); + }, + ); + } + } + + String generateReference(String email) { + late String platform; + if (Platform.isIOS) { + platform = 'I'; + } else if (Platform.isAndroid) { + platform = 'A'; + } + String reference = + '${platform}_${email.split("@").first}_${DateTime.now().millisecondsSinceEpoch}'; + return reference; + } + + _onTapSubscribe() async { + paymentMethodIndex[selectedPaymentMethod]?.call(); + } + + @override + void initState() { + paystackPlugin.initialize(publicKey: Constant.paystackKey); + super.initState(); + } + + void _razorpayHandlePaymentSuccess(PaymentSuccessResponse response) async { + await _purchase(); + } + + void _razorpayHandlePaymentError(PaymentFailureResponse response) { + HelperUtils.showSnackBarMessage(context, "purchaseFailed"); + } + +//not in use + void _razorpayHandleExternalWallet(ExternalWalletResponse response) {} + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.primaryColor, + appBar: UiUtils.buildAppBar(context, + showBackButton: true, + title: UiUtils.translate(context, "selectPaymentMethod")), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ScrollConfiguration( + behavior: RemoveGlow(), + child: ListView( + padding: const EdgeInsets.symmetric(horizontal: 16), + shrinkWrap: true, + children: [ + RadioListTile( + title: Text( + UiUtils.translate( + context, + "paystack", + ), + ), + activeColor: context.color.tertiaryColor, + secondary: paymentIcon(context, AppIcons.paystack), + controlAffinity: ListTileControlAffinity.trailing, + value: 1, + groupValue: selectedPaymentMethod, + onChanged: (dynamic v) { + selectedPaymentMethod = v; + setState( + () {}, + ); + }, + ), + const SizedBox( + height: 5, + ), + RadioListTile( + title: Text( + UiUtils.translate(context, "paypal"), + ), + activeColor: context.color.tertiaryColor, + secondary: paymentIcon( + context, + AppIcons.paypal, + ), + controlAffinity: ListTileControlAffinity.trailing, + value: 2, + groupValue: selectedPaymentMethod, + onChanged: (dynamic v) { + selectedPaymentMethod = v; + setState(() {}); + }), + const SizedBox( + height: 5, + ), + RadioListTile( + title: Text( + UiUtils.translate(context, "razorpay"), + ), + activeColor: context.color.tertiaryColor, + secondary: paymentIcon(context, AppIcons.razorpay), + controlAffinity: ListTileControlAffinity.trailing, + value: 3, + groupValue: selectedPaymentMethod, + onChanged: (dynamic v) { + selectedPaymentMethod = v; + setState(() {}); + }), + ], + ), + ), + const Spacer(), + Padding( + padding: const EdgeInsets.all(16.0), + child: UiUtils.buildButton(context, + onPressed: _onTapSubscribe, + radius: 12, + height: 48.rh(context), + buttonTitle: UiUtils.translate(context, "subscribe")), + ) + ], + ), + ); + } + + Widget paymentIcon(BuildContext context, String icon) { + return Container( + width: 60.rw(context), + height: 41.rh(context), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xff072654), + ), + child: UiUtils.getSvg(icon, fit: BoxFit.none), + ); + } + + Widget buildPackageTile( + BuildContext context, SubscriptionPackageModel subscriptionPacakge) { + return Container( + color: Theme.of(context).colorScheme.tertiaryColor, + width: context.screenWidth, + height: 160, + child: Padding( + padding: const EdgeInsets.all(25.0), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text(subscriptionPacakge.name.toString()) + .bold(weight: FontWeight.w600) + .size(24) + .color( + Theme.of(context).colorScheme.backgroundColor, + ), + Text("Validity:${subscriptionPacakge.duration.toString()}") + .bold(weight: FontWeight.w600) + .color( + Theme.of(context).colorScheme.backgroundColor, + ), + ], + ), + ), + Text("${Constant.currencySymbol}${subscriptionPacakge.price}") + .size(35) + .color(Theme.of(context).colorScheme.backgroundColor) + ], + ), + ), + ); + } +} diff --git a/lib/Ui/screens/subscription/subscription_status.dart b/lib/Ui/screens/subscription/subscription_status.dart new file mode 100644 index 0000000..bd86cc1 --- /dev/null +++ b/lib/Ui/screens/subscription/subscription_status.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +enum SubscriptionStaus { success, failed } + +class SubscriptionStatusScreen extends StatelessWidget { + final SubscriptionStaus status; + const SubscriptionStatusScreen({super.key, required this.status}); + + @override + Widget build(BuildContext context) => const Scaffold( + body: Center( + child: Text("Success"), + ), + ); +} diff --git a/lib/Ui/screens/subscription/transaction_history_screen.dart b/lib/Ui/screens/subscription/transaction_history_screen.dart new file mode 100644 index 0000000..936078d --- /dev/null +++ b/lib/Ui/screens/subscription/transaction_history_screen.dart @@ -0,0 +1,291 @@ +import 'package:ebroker/Ui/screens/widgets/Erros/no_internet.dart'; +import 'package:ebroker/utils/api.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../data/cubits/Utility/fetch_transactions_cubit.dart'; +import '../../../data/model/transaction_model.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/Erros/no_data_found.dart'; +import '../widgets/Erros/something_went_wrong.dart'; + +class TransactionHistory extends StatefulWidget { + const TransactionHistory({super.key}); + static Route route(RouteSettings settings) { + return BlurredRouter( + builder: (context) { + return BlocProvider( + create: (context) { + return FetchTransactionsCubit(); + }, + child: const TransactionHistory(), + ); + }, + ); + } + + @override + State createState() => _TransactionHistoryState(); +} + +class _TransactionHistoryState extends State { + late final ScrollController _pageScrollController = ScrollController() + ..addListener(_pageScrollListener); + + late Map statusMap; + @override + void initState() { + context.read().fetchTransactions(); + super.initState(); + } + + _pageScrollListener() { + if (_pageScrollController.isEndReached()) { + if (context.read().hasMoreData()) { + context.read().fetchTransactionsMore(); + } + } + } + + @override + void didChangeDependencies() { + statusMap = { + 1: UiUtils.translate(context, "statusSuccess"), + 2: UiUtils.translate(context, "statusFail") + }; + super.didChangeDependencies(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: UiUtils.buildAppBar(context, + showBackButton: true, + title: UiUtils.translate(context, "transactionHistory")), + body: BlocBuilder( + builder: (context, state) { + if (state is FetchTransactionsInProgress) { + return Center( + child: UiUtils.progress(), + ); + } + if (state is FetchTransactionsFailure) { + ; + if (state.errorMessage is ApiException) { + if ((state.errorMessage as dynamic).errorMessage == + "no-internet") { + return NoInternet( + onRetry: () { + context.read().fetchTransactions(); + }, + ); + } + } + + return const SomethingWentWrong(); + } + if (state is FetchTransactionsSuccess) { + if (state.transactionmodel.isEmpty) { + return NoDataFound( + onTap: () { + context.read().fetchTransactions(); + }, + ); + } + return Column( + children: [ + Expanded( + child: ListView.builder( + controller: _pageScrollController, + itemCount: state.transactionmodel.length, + itemBuilder: (context, index) { + TransactionModel transaction = + state.transactionmodel[index]; + + return Padding( + padding: const EdgeInsets.symmetric( + vertical: 4.0, horizontal: 16), + child: Container( + // height: 100, + decoration: BoxDecoration( + color: context.color.secondaryColor, + border: Border.all( + color: context.color.borderColor, + width: 1.5), + borderRadius: BorderRadius.circular(10)), + child: customTransactionItem(context, transaction) + + // ListTile( + // contentPadding: + // const EdgeInsetsDirectional.fromSTEB( + // 16, 5, 16, 5), + // style: ListTileStyle.list, + // subtitle: Row( + // children: [ + // Expanded( + // child: Text( + // transaction.createdAt + // .toString() + // .formatDate(), + // ).size(context.font.small), + // ), + // ], + // ), + // trailing: Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Text( + // "${Constant.currencySymbol}${transaction.amount}"), + // Text(statusMap[int.parse(transaction.status)] + // .toString()) + // ], + // ), + // title: Row( + // children: [ + // Expanded( + // child: Text(transaction.transactionId + // .toString())), + // const SizedBox( + // width: 5, + // ), + // GestureDetector( + // onTap: () async { + // await HapticFeedback.vibrate(); + // var clipboardData = ClipboardData( + // text: transaction.transactionId ?? + // ""); + // + // Clipboard.setData(clipboardData) + // .then((_) { + // ScaffoldMessenger.of(context) + // .showSnackBar(SnackBar( + // content: Text(UiUtils + // .getTranslatedLabel( + // context, "copied")))); + // }); + // }, + // child: Icon( + // Icons.copy, + // size: context.font.larger, + // )) + // ], + // )), + // + + ), + ); + }, + ), + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ); + } + + return Container(); + }, + ), + ); + } + + Widget customTransactionItem( + BuildContext context, TransactionModel transaction) { + return Builder(builder: (context) { + return Padding( + padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 16, 12), + child: Row( + children: [ + Container( + width: 4, + height: 41, + decoration: BoxDecoration( + color: context.color.tertiaryColor, + borderRadius: const BorderRadiusDirectional.only( + topEnd: Radius.circular(4), + bottomEnd: Radius.circular(4), + ), + ), + // padding: const EdgeInsets.symmetric(vertical: 2.0), + // margin: EdgeInsets.all(4), + // height:, + ), + const SizedBox( + width: 16, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + transaction.transactionId.toString(), + ), + ), + const SizedBox(width: 5), + ], + ), + const SizedBox(height: 4), + Text( + transaction.createdAt.toString().formatDate(), + ).size(context.font.small), + ], + ), + ), + GestureDetector( + onTap: () async { + await HapticFeedback.vibrate(); + var clipboardData = + ClipboardData(text: transaction.transactionId ?? ""); + Clipboard.setData(clipboardData).then((_) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(UiUtils.translate(context, "copied")), + ), + ); + }); + }, + child: Container( + height: 30, + width: 30, + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: context.color.borderColor, width: 1.5)), + child: Padding( + padding: const EdgeInsets.all(5.0), + child: Icon( + Icons.copy, + size: context.font.larger, + ), + ), + ), + ), + const SizedBox( + width: 15, + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("${Constant.currencySymbol}${transaction.amount}") + .bold(weight: FontWeight.w700) + .color(context.color.tertiaryColor), + SizedBox( + height: 6, + ), + Text(statusMap[int.parse(transaction.status)].toString()), + ], + ), + ], + ), + ); + }); + } +} diff --git a/lib/Ui/screens/subscription/widget/current_package_card.dart b/lib/Ui/screens/subscription/widget/current_package_card.dart new file mode 100644 index 0000000..b7a38e6 --- /dev/null +++ b/lib/Ui/screens/subscription/widget/current_package_card.dart @@ -0,0 +1,358 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_firstutils/Extensions/extensions.dart'; +import 'package:ebroker/Ui/screens/subscription/widget/package_tile.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/constant.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +import '../../../../data/model/subscription_pacakage_model.dart'; +import '../../../../utils/AppIcon.dart'; +import '../../../../utils/LiquidIndicator/src/liquid_circular_progress_indicator.dart'; +import '../../../../utils/ui_utils.dart'; + +class CurrentPackageTileCard extends StatefulWidget { + final SubscriptionPackageModel package; + const CurrentPackageTileCard({ + super.key, + required this.package, + }); + + @override + State createState() => _CurrentPackageTileCardState(); +} + +class _CurrentPackageTileCardState extends State { + String endDate = ""; + bool isLifeTimeValidity = false; + String endDay = ""; + + // int? getDaysRemining() { + // if (widget.endDate != null) { + // DateTime currentDate = DateTime.now(); + // Duration remainingDuration = + // DateTime.parse(widget.endDate).difference(currentDate); + // int daysLeft = remainingDuration.inDays; + // return daysLeft; + // } + // return null; + // } + + @override + Widget build(BuildContext context) { + // if (widget.package / endDate != null) { + // endDate = widget.endDate.toString().formatDate(format: "d MMM yyyy"); + // endDay = widget.endDate.toString().formatDate(format: "EEEE"); + // } + // isLifeTimeValidity = widget.endDate == null; + return Container( + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(18)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildCurvePatternWidget(context), + const SizedBox( + height: 5, + ), + buildPriceAndTitleWidget(context), + buildValidityWidget(context), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: widget.package.type == "premium_user" + ? CrossAxisAlignment.end + : CrossAxisAlignment.center, + children: [ + if (widget.package.type == "premium_user") + Expanded(child: ViewOnlyPackageCard()), + + if (widget.package.type != "premium_user") + LiquidProgressContainer( + title: "property".translate(context), + countUsed: widget.package.usedLimitForProperty ?? 0, + countLimit: widget.package.propertyLimit), + // LiquidProgressContainer( + // title: "property".translate(context), + // countRemining: widget.propertyRemining, + // countLimit: widget.propertyLimit, + // ), + if (widget.package.type != "premium_user") + LiquidProgressContainer( + title: "advertisement".translate(context), + countUsed: widget.package.usedLimitForAdvertisement ?? 0, + countLimit: widget.package.advertisementLimit), + // LiquidProgressContainer( + // title: "advertisement".translate(context), + // countRemining: widget.advertismentRemining, + // countLimit: widget.advertismentLimit, + // ), + LiquidProgressContainer( + title: "daysRemining".translate(context), + countUsed: widget.package.remainingDays!, + isValidity: true, + countLimit: widget.package.duration!), + // LiquidProgressContainer( + // title: "daysRemining".translate(context), + // countRemining: (getDaysRemining() ?? 0).toString(), + // countLimit: widget.duration, + // isValidity: true, + // validityCount: widget.duration.toString(), + // ), + ], + ), + ), + if (isLifeTimeValidity) + const SizedBox( + height: 6, + ), + if (!isLifeTimeValidity) + Padding( + padding: const EdgeInsetsDirectional.fromSTEB(16.0, 5, 16, 16), + child: Row( + children: [ + Expanded( + child: DateCard( + title: "startedOn".translate(context), + day: widget.package.startDate!.formatDate(format: "EEEE"), + date: widget.package.startDate ?? "", + )), + const SizedBox( + width: 16, + ), + Expanded( + child: DateCard( + day: widget.package.endDate!.formatDate(format: "EEEE"), + title: "willEndOn".translate(context), + date: widget.package.endDate ?? "", + ), + ), + ], + ), + ) + ], + ), + ); + } + + Stack buildCurvePatternWidget(BuildContext context) { + return Stack( + children: [ + SizedBox( + width: context.screenWidth, + child: UiUtils.getSvg(AppIcons.headerCurve, + color: context.color.tertiaryColor, fit: BoxFit.fitWidth), + ), + PositionedDirectional( + start: 10.rw(context), + top: 8.rh(context), + child: Text(UiUtils.translate(context, "currentPackage")) + .size(context.font.larger) + .color(context.color.secondaryColor) + .bold(weight: FontWeight.w600), + ) + ], + ); + } + + Padding buildPriceAndTitleWidget(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 5), + child: Row( + children: [ + Expanded( + flex: 2, + child: SizedBox( + child: Text(widget.package.name!) + .size(context.font.larger) + .color(context.color.textColorDark) + .bold(weight: FontWeight.w600), + ), + ), + const SizedBox( + width: 5, + ), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Text( + "price".translate(context), + style: const TextStyle( + textBaseline: TextBaseline.alphabetic), + ).size(context.font.small).bold(weight: FontWeight.w300), + const SizedBox( + height: 5, + ), + buildPriceWidget(context) + ], + ), + ], + ), + ) + ], + ), + ); + } + + Widget buildPriceWidget(BuildContext context) { + return Row( + children: [ + if (widget.package.price != 0) + Text( + Constant.currencySymbol, + style: const TextStyle( + height: 0.6, + ), + ).color(context.color.tertiaryColor).size(context.font.larger), + Text( + (widget.package.price == 0 ? "Free" : widget.package.price) + .toString(), + style: const TextStyle( + height: 0.6, + ), + ).size(context.font.larger).bold(weight: FontWeight.w500) + ], + ); + } + + Widget buildValidityWidget(BuildContext context) { + return Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 16, 0, 16, widget.package.type == "premium_user" ? 0 : 10), + child: Text( + "${"packageValidity".translate(context)} ${widget.package.duration} ${"Days".translate(context)}") + .size(context.font.large), + ); + } +} + +class DateCard extends StatelessWidget { + final String title; + final String date; + final String day; + const DateCard( + {super.key, required this.title, required this.date, required this.day}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title), + Container( + height: 53, + decoration: BoxDecoration( + color: context.color.tertiaryColor, + borderRadius: BorderRadius.circular(4), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset( + AppIcons.calender, + width: 26, + height: 26, + color: context.color.buttonColor, + ), + const SizedBox( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(day).color(context.color.buttonColor), + Text( + date, + style: const TextStyle( + fontWeight: FontWeight.w200, + ), + ).color(context.color.buttonColor) + ], + ), + ) + ], + ), + ), + ), + ], + ); + } +} + +class LiquidProgressContainer extends StatelessWidget { + final String title; + final int countUsed; + final dynamic countLimit; + final bool? isValidity; + final String? validityCount; + const LiquidProgressContainer( + {super.key, + required this.title, + required this.countUsed, + required this.countLimit, + this.isValidity, + this.validityCount}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Text(title).size(context.font.normal), + const SizedBox( + height: 7, + ), + getLiquidProgress( + context, + ) + ], + ); + } + + Widget getLiquidProgress(BuildContext context) { + double persontage = 0; + String value = ""; + if (countLimit == "not_available" || countLimit == null) { + value = "X"; + } else if (countLimit == "unlimited") { + value = "unlimited".translate(context); + } else { + value = "$countUsed/$countLimit"; + persontage = (countUsed / countLimit); + } + if (isValidity == true) { + value = "$countUsed ${"Days".translate(context)}"; + } + + return SizedBox( + width: 60, + height: 60, + child: LiquidCircularProgressIndicator( + value: persontage, + valueColor: AlwaysStoppedAnimation( + context.color.tertiaryColor.withOpacity(0.3), + ), + backgroundColor: context.color.secondaryColor, + borderColor: context.color.tertiaryColor, + borderWidth: 3.0, + direction: Axis.vertical, + center: Padding( + padding: const EdgeInsets.all(5.0), + child: FittedBox( + fit: BoxFit.fitWidth, + child: Text(value).size(context.font.small)), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/subscription/widget/package_tile.dart b/lib/Ui/screens/subscription/widget/package_tile.dart new file mode 100644 index 0000000..0b821ec --- /dev/null +++ b/lib/Ui/screens/subscription/widget/package_tile.dart @@ -0,0 +1,216 @@ +import 'package:ebroker/Ui/screens/subscription/widget/subscripton_feature_line.dart'; +import 'package:ebroker/data/model/subscription_pacakage_model.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:flutter/material.dart'; + +import '../../../../utils/AppIcon.dart'; +import '../../../../utils/ui_utils.dart'; + +abstract class Limit { + abstract final T value; +} + +class StringLimit extends Limit { + @override + final String value; + + StringLimit(this.value); +} + +class IntLimit extends Limit { + @override + final int value; + + IntLimit(this.value); +} + +class NotAvailable extends Limit { + @override + void value; + + NotAvailable(); +} + +class PackageLimit { + final dynamic limit; + + PackageLimit(this.limit); + + Limit get(context) { + if (limit is int) { + return IntLimit(limit); + } else { + if (isAvailable(context, limit)) { + if (isUnLimited(context, limit)) { + return StringLimit("unlimited".translate(context)); + } else { + //Will not execute but added + return StringLimit(limit); + } + } else { + return NotAvailable(); + } + } + } + + bool isUnLimited(BuildContext context, String value) { + if (value == "unlimited") { + return true; + } + return false; + } + + bool isAvailable(BuildContext context, String? value) { + if (value == "not_available" || value == null) { + return false; + } + return true; + } +} + +class SubscriptionPackageTile extends StatelessWidget { + final SubscriptionPackageModel package; + final VoidCallback onTap; + const SubscriptionPackageTile( + {super.key, required this.onTap, required this.package}); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(18)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Stack( + children: [ + SizedBox( + width: context.screenWidth, + child: UiUtils.getSvg(AppIcons.headerCurve, + color: context.color.tertiaryColor, fit: BoxFit.fitWidth), + ), + PositionedDirectional( + start: 10.rw(context), + top: 8.rh(context), + child: Text(package.name ?? "") + .size(context.font.larger) + .color(context.color.secondaryColor) + .bold(weight: FontWeight.w600), + ) + ], + ), + const SizedBox( + height: 20, + ), + SubscriptionFeatureLine( + limit: PackageLimit(package.advertisementLimit), + isTime: false, + title: UiUtils.translate(context, "adLimitIs"), + ), + SizedBox( + height: 5.rh(context), + ), + Row( + children: [ + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + SubscriptionFeatureLine( + limit: PackageLimit(package.propertyLimit), + isTime: false, + title: UiUtils.translate(context, "propertyLimit"), + ), + SizedBox( + height: 5.rh(context), + ), + SubscriptionFeatureLine( + limit: null, + isTime: true, + timeLimit: + "${package.duration} ${UiUtils.translate(context, "days")}", + title: UiUtils.translate(context, "validity"), + ), + // SubscriptionFeatureLine(), + ]), + Expanded( + child: Padding( + padding: const EdgeInsetsDirectional.only(end: 15.0), + child: Container( + alignment: Alignment.center, + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(8)), + height: 39.rh(context), + constraints: BoxConstraints( + minWidth: 80.rw(context), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: Text( + package.price == 0 + ? "Free".translate(context) + : ("${package.price}") + .toString() + .formatAmount(prefix: true), + style: const TextStyle(fontFamily: "ROBOTO"), + ) + .color(context.color.tertiaryColor) + .bold() + .size(context.font.large), + )), + ), + ) + ], + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: UiUtils.buildButton(context, + onPressed: onTap, + radius: 9, + height: 33.rh(context), + buttonTitle: UiUtils.translate(context, "subscribe")), + ), + ], + ), + ); + } +} + +class ViewOnlyPackageCard extends StatelessWidget { + const ViewOnlyPackageCard({super.key}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.zero, + child: SizedBox( + height: 60, + child: Center( + child: Container( + height: 50, + decoration: BoxDecoration( + border: Border.all( + color: context.color.tertiaryColor, + width: 2.5, + ), + borderRadius: BorderRadius.circular(12)), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Icon( + Icons.remove_red_eye_outlined, + color: context.color.tertiaryColor, + ), + ), + Text("Unlocked Private Properties".translate(context)) + .bold(weight: FontWeight.w500) + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/subscription/widget/subscripton_feature_line.dart b/lib/Ui/screens/subscription/widget/subscripton_feature_line.dart new file mode 100644 index 0000000..c92f9ff --- /dev/null +++ b/lib/Ui/screens/subscription/widget/subscripton_feature_line.dart @@ -0,0 +1,65 @@ +import 'package:ebroker/Ui/screens/subscription/widget/package_tile.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:flutter/material.dart'; + +class SubscriptionFeatureLine extends StatelessWidget { + final String title; + final PackageLimit? limit; + final bool? isTime; + final String? timeLimit; + const SubscriptionFeatureLine( + {super.key, + required this.title, + required this.limit, + this.isTime, + this.timeLimit}); + + @override + Widget build(BuildContext context) { + if (limit?.get(context) is NotAvailable) { + return const SizedBox.shrink(); + } + + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 20, + ), + child: SizedBox( + width: context.screenWidth * 0.57, + child: Row( + children: [ + bulletPoint(context), + SizedBox( + width: 5.rw(context), + ), + if (isTime == true) ...{ + Text("$title "), + Text("$timeLimit "), + } else ...{ + Text("$title "), + Text((limit?.get(context).value ?? "").toString()) + } + ], + )), + ); + } + + Widget bulletPoint(BuildContext context) { + return SizedBox( + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 4, + height: 4, + decoration: BoxDecoration( + color: context.color.textColorDark, + shape: BoxShape.circle, + ), + ), + ], + ), + ); + } +} diff --git a/lib/Ui/screens/userprofile/edit_profile.dart b/lib/Ui/screens/userprofile/edit_profile.dart new file mode 100644 index 0000000..2c48ab3 --- /dev/null +++ b/lib/Ui/screens/userprofile/edit_profile.dart @@ -0,0 +1,686 @@ +import 'dart:io'; + +import 'package:ebroker/Ui/screens/Personalized/personalized_property_screen.dart'; +import 'package:ebroker/Ui/screens/widgets/custom_text_form_field.dart'; +import 'package:ebroker/Ui/screens/widgets/image_cropper.dart'; +import 'package:ebroker/data/cubits/auth/auth_cubit.dart'; +import 'package:ebroker/data/cubits/property/fetch_most_viewed_properties_cubit.dart'; +import 'package:ebroker/data/cubits/property/fetch_nearby_property_cubit.dart'; +import 'package:ebroker/data/cubits/property/fetch_promoted_properties_cubit.dart'; +import 'package:ebroker/data/cubits/slider_cubit.dart'; +import 'package:ebroker/data/cubits/system/user_details.dart'; +import 'package:ebroker/data/helper/custom_exception.dart'; +import 'package:ebroker/data/helper/designs.dart'; +import 'package:ebroker/data/model/user_model.dart'; +import 'package:ebroker/utils/AppIcon.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/constant.dart'; +import 'package:ebroker/utils/hive_utils.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hive/hive.dart'; +import 'package:image_cropper/image_cropper.dart'; +import 'package:image_picker/image_picker.dart'; + +import '../../../app/routes.dart'; +import '../../../data/helper/widgets.dart'; +import '../../../data/model/google_place_model.dart'; +import '../../../utils/guestChecker.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/hive_keys.dart'; +import '../widgets/AnimatedRoutes/blur_page_route.dart'; +import '../widgets/BottomSheets/choose_location_bottomsheet.dart'; + +class UserProfileScreen extends StatefulWidget { + final String from; + final bool? navigateToHome; + final bool? popToCurrent; + const UserProfileScreen({ + Key? key, + required this.from, + this.navigateToHome, + this.popToCurrent, + }) : super(key: key); + + @override + State createState() => UserProfileScreenState(); + + static Route route(RouteSettings routeSettings) { + Map arguments = routeSettings.arguments as Map; + return BlurredRouter( + builder: (_) => UserProfileScreen( + from: arguments['from'] as String, + popToCurrent: arguments['popToCurrent'] as bool?, + navigateToHome: arguments['navigateToHome'] as bool?, + ), + ); + } +} + +class UserProfileScreenState extends State { + final GlobalKey _formKey = GlobalKey(); + final TextEditingController phoneController = TextEditingController(); + final TextEditingController nameController = TextEditingController(); + final TextEditingController emailController = TextEditingController(); + final TextEditingController addressController = TextEditingController(); + dynamic size; + dynamic city, _state, country, placeid; + String? name, email, address; + File? fileUserimg; + bool isNotificationsEnabled = true; + double? latitude, longitude; + @override + void initState() { + super.initState(); + if (widget.from == "login") { + GuestChecker.set(isGuest: false); + } + city = HiveUtils.getCityName(); + _state = HiveUtils.getStateName(); + country = HiveUtils.getCountryName(); + placeid = HiveUtils.getCityPlaceId() ?? ""; + phoneController.text = _saperateNumber(); + nameController.text = (HiveUtils.getUserDetails().name) ?? ""; + emailController.text = HiveUtils.getUserDetails().email ?? ""; + addressController.text = HiveUtils.getUserDetails().address ?? ""; + isNotificationsEnabled = + HiveUtils.getUserDetails().notification == 1 ? true : false; + //} + + _saperateNumber(); + } + + String _saperateNumber() { + // FirebaseAuth.instance.currentUser.sendEmailVerification(); + String? mobile = HiveUtils.getUserDetails().mobile; + + String? countryCode = HiveUtils.getCountryCode(); + + int countryCodeLength = (countryCode?.length ?? 0); + + String mobileNumber = mobile!.substring(countryCodeLength, mobile.length); + + mobileNumber = "+${countryCode!} $mobileNumber"; + return mobileNumber; + } + + @override + void dispose() { + super.dispose(); + phoneController.dispose(); + nameController.dispose(); + emailController.dispose(); + addressController.dispose(); + } + + void _onTapChooseLocation() async { + FocusManager.instance.primaryFocus?.unfocus(); + + if (!const bool.fromEnvironment("force-disable-demo-mode", + defaultValue: false)) { + if (Constant.isDemoModeOn) { + HelperUtils.showSnackBarMessage(context, "Not valid in demo mode"); + + return; + } + } + + var result = await showModalBottomSheet( + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20), topRight: Radius.circular(20))), + context: context, + builder: (context) { + return const ChooseLocatonBottomSheet(); + }, + ); + if (result != null) { + GooglePlaceModel place = (result as GooglePlaceModel); + print("LATTTT is ${place.longitude}"); + latitude = double.parse(place.latitude); + longitude = double.parse(place.longitude); + city = place.city; + country = place.country; + _state = place.state; + placeid = place.placeId; + } + } + + @override + Widget build(BuildContext context) { + size = MediaQuery.of(context).size; + return GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: safeAreaCondition( + child: Scaffold( + backgroundColor: context.color.primaryColor, + appBar: widget.from == "login" + ? null + : UiUtils.buildAppBar(context, showBackButton: true), + body: ScrollConfiguration( + behavior: RemoveGlow(), + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + keyboardDismissBehavior: + ScrollViewKeyboardDismissBehavior.onDrag, + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Align( + alignment: Alignment.center, + child: buildProfilePicture(), + ), + buildTextField( + context, + title: "fullName", + controller: nameController, + validator: CustomTextFieldValidator.nullCheck, + ), + buildTextField( + context, + title: "companyEmailLbl", + controller: emailController, + validator: CustomTextFieldValidator.email, + ), + buildTextField( + context, + title: "phoneNumber", + controller: phoneController, + validator: CustomTextFieldValidator.nullCheck, + readOnly: true, + ), + buildAddressTextField( + context, + title: "addressLbl", + controller: addressController, + validator: CustomTextFieldValidator.nullCheck, + ), + const SizedBox( + height: 10, + ), + Text("enablesNewSection".translate(context)) + .size(context.font.small) + .bold(weight: FontWeight.w300) + .color( + context.color.textColorDark.withOpacity(0.8), + ), + SizedBox( + height: 20.rh(context), + ), + Text( + UiUtils.translate(context, "notification"), + ), + SizedBox( + height: 10.rh(context), + ), + buildNotificationEnableDisableSwitch(context), + SizedBox( + height: 25.rh(context), + ), + UiUtils.buildButton( + context, + onPressed: () { + if (city != null && city != "") { + HiveUtils.setLocation( + city: city, + state: _state, + latitude: latitude, + longitude: longitude, + country: country, + placeId: placeid); + Hive.box(HiveKeys.userDetailsBox) + .put(HiveKeys.cityTeemp, city); + context + .read() + .fetch(forceRefresh: true); + + context + .read() + .fetch(); + context + .read() + .fetch(); + context + .read() + .fetchSlider(context); + } else { + HiveUtils.clearLocation(); + context + .read() + .fetch(); + context + .read() + .fetch(forceRefresh: true); + + context + .read() + .fetch(); + context + .read() + .fetchSlider(context); + } + validateData(); + }, + height: 48.rh(context), + buttonTitle: + UiUtils.translate(context, "updateProfile"), + ) + ])), + )), + ), + ), + ), + ); + } + + Widget locationWidget(BuildContext context) { + return Padding( + padding: const EdgeInsets.only( + top: 10.0, + ), + child: Row( + children: [ + Expanded( + child: Container( + height: 55, + decoration: BoxDecoration( + color: context.color.textLightColor.withOpacity(00.01), + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: context.color.borderColor, + width: 1.5, + )), + child: Row( + children: [ + Padding( + padding: const EdgeInsetsDirectional.only(start: 10.0), + child: Align( + alignment: Alignment.centerLeft, + child: (city != "" && city != null) + ? Text("$city,$_state,$country") + : Text(UiUtils.translate( + context, "selectLocationOptional"))), + ), + const Spacer(), + if (city != "" && city != null) + Padding( + padding: const EdgeInsetsDirectional.only(end: 10.0), + child: GestureDetector( + onTap: () { + city = ""; + _state = ""; + country = ""; + HiveUtils.clearLocation(); + setState(() {}); + }, + child: Icon( + Icons.close, + color: context.color.textColorDark, + ), + ), + ) + ], + ), + ), + ), + const SizedBox( + width: 10, + ), + GestureDetector( + onTap: _onTapChooseLocation, + child: Container( + height: 55, + width: 55, + decoration: BoxDecoration( + color: context.color.textLightColor.withOpacity(00.01), + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: context.color.borderColor, + width: 1.5, + )), + child: Icon( + Icons.location_searching_sharp, + color: context.color.tertiaryColor, + ), + ), + ), + ], + ), + ); + } + + Widget safeAreaCondition({required Widget child}) { + if (widget.from == "login") { + return SafeArea(child: child); + } + return child; + } + + Widget buildNotificationEnableDisableSwitch(BuildContext context) { + return Container( + decoration: BoxDecoration( + border: Border.all( + color: context.color.borderColor, + ), + borderRadius: BorderRadius.circular(10), + color: context.color.textLightColor.withOpacity(00.01)), + height: 55.rh(context), + width: double.infinity, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text(UiUtils.translate( + context, isNotificationsEnabled ? "enabled" : "disabled")) + .size(context.font.large), + ), + CupertinoSwitch( + activeColor: context.color.tertiaryColor, + value: isNotificationsEnabled, + onChanged: (value) { + isNotificationsEnabled = value; + setState(() {}); + }, + ) + ], + ), + ); + } + + Widget buildTextField(BuildContext context, + {required String title, + required TextEditingController controller, + CustomTextFieldValidator? validator, + bool? readOnly}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 10.rh(context), + ), + Text(UiUtils.translate(context, title)), + SizedBox( + height: 10.rh(context), + ), + CustomTextFormField( + controller: controller, + isReadOnly: readOnly, + validator: validator, + // formaters: [FilteringTextInputFormatter.deny(RegExp(","))], + fillColor: context.color.textLightColor.withOpacity(00.01), + ), + ], + ); + } + + Widget buildAddressTextField(BuildContext context, + {required String title, + required TextEditingController controller, + CustomTextFieldValidator? validator, + bool? readOnly}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 10.rh(context), + ), + Text(UiUtils.translate(context, title)), + SizedBox( + height: 10.rh(context), + ), + CustomTextFormField( + controller: controller, + maxLine: 5, + action: TextInputAction.newline, + isReadOnly: readOnly, + validator: validator, + fillColor: context.color.textLightColor.withOpacity(00.01), + ), + const SizedBox( + width: 10, + ), + locationWidget(context), + ], + ); + } + + Widget getProfileImage() { + if (fileUserimg != null) { + return Image.file( + fileUserimg!, + fit: BoxFit.cover, + ); + } else { + if (widget.from == "login") { + if (HiveUtils.getUserDetails().profile != "" && + HiveUtils.getUserDetails().profile != null) { + return UiUtils.getImage( + HiveUtils.getUserDetails().profile!, + fit: BoxFit.cover, + ); + } + + return UiUtils.getSvg( + AppIcons.defaultPersonLogo, + color: context.color.tertiaryColor, + fit: BoxFit.none, + ); + } else { + if ((HiveUtils.getUserDetails().profile ?? "").isEmpty) { + return UiUtils.getSvg( + AppIcons.defaultPersonLogo, + color: context.color.tertiaryColor, + fit: BoxFit.none, + ); + } else { + return UiUtils.getImage( + HiveUtils.getUserDetails().profile!, + fit: BoxFit.cover, + ); + } + } + } + } + + Widget buildProfilePicture() { + return Stack( + children: [ + Container( + height: 124.rh(context), + width: 124.rw(context), + alignment: Alignment.center, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all(color: context.color.tertiaryColor, width: 2)), + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity(0.2), + shape: BoxShape.circle, + ), + width: 106.rw(context), + height: 106.rh(context), + child: getProfileImage(), + ), + ), + PositionedDirectional( + bottom: 0, + end: 0, + child: InkWell( + onTap: showPicker, + child: Container( + height: 37.rh(context), + width: 37.rw(context), + alignment: Alignment.center, + decoration: BoxDecoration( + border: Border.all( + color: context.color.buttonColor, width: 1.5), + shape: BoxShape.circle, + color: context.color.tertiaryColor), + child: SizedBox( + width: 15.rw(context), + height: 15.rh(context), + child: UiUtils.getSvg(AppIcons.edit))), + ), + ) + ], + ); + } + + Future validateData() async { + if (_formKey.currentState!.validate()) { + bool checkinternet = await HelperUtils.checkInternet(); + if (!checkinternet) { + Future.delayed( + Duration.zero, + () { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "lblchecknetwork")); + }, + ); + + return; + } + process(); + } + } + + process() async { + if (Constant.isDemoModeOn) { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "thisActionNotValidDemo")); + return; + } + Widgets.showLoader(context); + try { + var response = await context.read().updateUserData(context, + name: nameController.text.trim(), + email: emailController.text.trim(), + fileUserimg: fileUserimg, + latitude: latitude, + longitude: longitude, + city: city, + state: _state, + country: country, + address: addressController.text, + notification: isNotificationsEnabled == true ? "1" : "0"); + + Future.delayed(Duration.zero, () { + context + .read() + .copy(UserModel.fromJson(response['data'])); + }); + + Future.delayed( + Duration.zero, + () { + Widgets.hideLoder(context); + HelperUtils.showSnackBarMessage( + context, + UiUtils.translate(context, "profileupdated"), + onClose: () { + if (mounted) Navigator.pop(context); + }, + ); + if (widget.navigateToHome ?? false) { + Navigator.pop(context); + } + }, + ); + + if (widget.from == "login" && widget.popToCurrent != true) { + Future.delayed( + Duration.zero, + () { + HelperUtils.killPreviousPages( + context, Routes.personalizedPropertyScreen, { + "type": PersonalizedVisitType.FirstTime, + }); + + // HelperUtils.killPreviousPages( + // context, Routes.main, {"from": widget.from}); + }, + ); + } else if (widget.from == "login" && widget.popToCurrent == true) { + Future.delayed(Duration.zero, () { + Navigator.of(context) + ..pop() + ..pop(); + }); + } + } on CustomException catch (e) { + Future.delayed(Duration.zero, () { + Widgets.hideLoder(context); + HelperUtils.showSnackBarMessage(context, e.toString()); + }); + } + } + + void showPicker() { + showModalBottomSheet( + context: context, + shape: setRoundedBorder(10), + builder: (BuildContext bc) { + return SafeArea( + child: Wrap( + children: [ + ListTile( + leading: const Icon(Icons.photo_library), + title: Text(UiUtils.translate(context, "gallery")), + onTap: () { + _imgFromGallery(ImageSource.gallery); + Navigator.of(context).pop(); + }), + ListTile( + leading: const Icon(Icons.photo_camera), + title: Text(UiUtils.translate(context, "camera")), + onTap: () { + _imgFromGallery(ImageSource.camera); + Navigator.of(context).pop(); + }, + ), + if (fileUserimg != null && widget.from == 'login') + ListTile( + leading: const Icon(Icons.clear_rounded), + title: Text(UiUtils.translate(context, "lblremove")), + onTap: () { + fileUserimg = null; + + Navigator.of(context).pop(); + setState(() {}); + }, + ), + ], + ), + ); + }); + } + + _imgFromGallery(ImageSource imageSource) async { + CropImage.init(context); + + final pickedFile = await ImagePicker().pickImage(source: imageSource); + + if (pickedFile != null) { + CroppedFile? croppedFile; + croppedFile = await CropImage.crop(filePath: pickedFile.path); + if (croppedFile == null) { + fileUserimg = null; + } else { + fileUserimg = File(croppedFile.path); + } + } else { + fileUserimg = null; + } + setState(() {}); + } +} diff --git a/lib/Ui/screens/userprofile/profile_screen.dart b/lib/Ui/screens/userprofile/profile_screen.dart new file mode 100644 index 0000000..5be1858 --- /dev/null +++ b/lib/Ui/screens/userprofile/profile_screen.dart @@ -0,0 +1,909 @@ +import 'dart:io'; + +import 'package:ebroker/Ui/screens/Personalized/personalized_property_screen.dart'; +import 'package:ebroker/Ui/screens/main_activity.dart'; +import 'package:ebroker/data/model/user_model.dart'; +import 'package:ebroker/utils/guestChecker.dart'; +import 'package:ebroker/utils/hive_keys.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hive/hive.dart'; +import 'package:launch_review/launch_review.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../../../app/app_theme.dart'; +import '../../../app/routes.dart'; +import '../../../data/cubits/Utility/like_properties.dart'; +import '../../../data/cubits/system/app_theme_cubit.dart'; +import '../../../data/cubits/system/fetch_system_settings_cubit.dart'; +import '../../../data/cubits/system/user_details.dart'; +import '../../../data/model/system_settings_model.dart'; +import '../../../utils/AppIcon.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/Network/apiCallTrigger.dart'; +import '../../../utils/api.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/hive_utils.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import '../widgets/blurred_dialoge_box.dart'; + +class ProfileScreen extends StatefulWidget { + const ProfileScreen({Key? key}) : super(key: key); + + @override + State createState() => _ProfileScreenState(); +} + +class _ProfileScreenState extends State + with AutomaticKeepAliveClientMixin { + ValueNotifier isDarkTheme = ValueNotifier(false); + // with SingleTickerProviderStateMixin { + bool isGuest = false; + @override + void initState() { + var settings = context.read(); + isGuest = GuestChecker.value; + GuestChecker.listen().addListener(() { + isGuest = GuestChecker.value; + if (mounted) setState(() {}); + }); + if (!const bool.fromEnvironment("force-disable-demo-mode", + defaultValue: false)) { + Constant.isDemoModeOn = + settings.getSetting(SystemSetting.demoMode) ?? false; + } + super.initState(); + } + + @override + void didChangeDependencies() { + isDarkTheme.value = context.read().isDarkMode(); + super.didChangeDependencies(); + } + + @override + void dispose() { + isDarkTheme.dispose(); + super.dispose(); + } + + @override + bool get wantKeepAlive => true; + int? a; + @override + Widget build(BuildContext context) { + super.build(context); + print(Hive.box(HiveKeys.userDetailsBox).toMap().toString()); + + // log(a!.toString()); + var settings = context.watch(); + + if (!const bool.fromEnvironment("force-disable-demo-mode", + defaultValue: false)) { + Constant.isDemoModeOn = + settings.getSetting(SystemSetting.demoMode) ?? false; + } + + var username = "Anonymous"; + var email = "Not logged in"; + if (!isGuest) { + UserModel? user = context.watch().state.user; + username = user?.name!.firstUpperCase() ?? "Anonymous"; + email = (user?.email) ?? "Login first"; + } + return AnnotatedRegion( + value: SystemUiOverlayStyle( + systemNavigationBarDividerColor: Colors.transparent, + // systemNavigationBarColor: Theme.of(context).colorScheme.secondaryColor, + systemNavigationBarIconBrightness: + context.watch().state.appTheme == AppTheme.dark + ? Brightness.light + : Brightness.dark, + // + statusBarColor: Theme.of(context).colorScheme.secondaryColor, + statusBarBrightness: + context.watch().state.appTheme == AppTheme.dark + ? Brightness.dark + : Brightness.light, + statusBarIconBrightness: + context.watch().state.appTheme == AppTheme.dark + ? Brightness.light + : Brightness.dark), + child: Scaffold( + backgroundColor: context.color.primaryColor, + appBar: UiUtils.buildAppBar( + context, + title: UiUtils.translate(context, "myProfile"), + ), + body: ScrollConfiguration( + behavior: RemoveGlow(), + child: SingleChildScrollView( + controller: profileScreenController, + physics: const BouncingScrollPhysics(), + child: Padding( + padding: const EdgeInsets.all(18.0), + child: Column(children: [ + Container( + height: 91, + decoration: BoxDecoration( + border: Border.all( + width: 1.5, + color: context.color.borderColor, + ), + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(18), + ), + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(18), + child: profileImgWidget(), + ), + SizedBox( + width: context.screenWidth * 0.015, + ), + SizedBox( + // height: 77, + child: Column( + // crossAxisAlignment: CrossAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: context.screenWidth * 0.35, + child: Text(username) + .color(context.color.textColorDark) + .size(context.font.large) + .bold(weight: FontWeight.w700) + .setMaxLines(lines: 1), + ), + SizedBox( + width: context.screenWidth * 0.35, + child: Text(email) + .color(context.color.textColorDark) + .size(context.font.small) + .setMaxLines(lines: 1), + ), + ], + ), + ), + const Spacer(), + GuestChecker.updateUI( + onChangeStatus: (bool? isGuest) { + if (isGuest == true) { + return MaterialButton( + shape: RoundedRectangleBorder( + side: BorderSide( + color: context.color.borderColor, + width: 1.5, + ), + borderRadius: BorderRadius.circular(10), + ), + onPressed: () { + Navigator.pushNamed( + context, + Routes.login, + arguments: {"popToCurrent": true}, + ); + }, + child: const Text("Login"), + ); + } + + return InkWell( + onTap: () { + HelperUtils.goToNextPage( + Routes.completeProfile, context, false, + args: {"from": "profile"}); + }, + child: Container( + width: 40.rw(context), + height: 40.rh(context), + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: context.color.borderColor, + width: 1.5, + ), + ), + child: FittedBox( + fit: BoxFit.none, + child: SizedBox( + width: 12.rw(context), + height: 22.rh(context), + child: UiUtils.getSvg( + AppIcons.arrowRight, + color: context.color.textColorDark, + ), + ), + ), + ), + ); + }, + ) + ], + ), + ), + ), + const SizedBox( + height: 20, + ), + Container( + decoration: BoxDecoration( + border: Border.all( + width: 1.5, + color: context.color.borderColor, + ), + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(18), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const SizedBox( + height: 20, + ), + // customTile( + // context, + // title: "ONLY FOR DEVELOPMENT", + // svgImagePath: AppIcons.enquiry, + // onTap: () async { + // var s = await FirebaseMessaging.instance.getToken(); + // Navigator.push(context, MaterialPageRoute( + // builder: (context) { + // return Scaffold( + // body: Padding( + // padding: const EdgeInsets.all(20.0), + // child: Center( + // child: SelectableText(s.toString()), + // ), + // ), + // ); + // }, + // )); + // }, + // ), + // dividerWithSpacing(), + // customTile( + // context, + // title: UiUtils.getTranslatedLabel(context, "myEnquiry"), + // svgImagePath: AppIcons.enquiry, + // onTap: () { + // Navigator.pushNamed(context, Routes.myEnquiry); + // }, + // ), + // dividerWithSpacing(), + //THIS IS EXPERIMENTAL + // if (false) ...[ + // customTile( + // context, + // title: UiUtils.translate(context, "Dashboard"), + // svgImagePath: AppIcons.promoted, + // onTap: () { + // Navigator.pushNamed(context, Routes.dashboard); + // }, + // ), + // dividerWithSpacing(), + // ], + + customTile( + context, + title: UiUtils.translate(context, "myProjects"), + svgImagePath: AppIcons.upcomingProject, + onTap: () async { + // APICallTrigger.trigger(); + GuestChecker.check( + onNotGuest: () async { + Navigator.pushNamed( + context, Routes.projectListScreen); + }, + ); + }, + ), + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate(context, "myAds"), + svgImagePath: AppIcons.promoted, + onTap: () async { + APICallTrigger.trigger(); + GuestChecker.check( + onNotGuest: () async { + Navigator.pushNamed( + context, Routes.myAdvertisment); + }, + ); + }, + ), + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate(context, "subscription"), + svgImagePath: AppIcons.subscription, + onTap: () async { + GuestChecker.check(onNotGuest: () { + Navigator.pushNamed( + context, Routes.subscriptionPackageListRoute); + }); + }, + ), + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate(context, "transactionHistory"), + svgImagePath: AppIcons.transaction, + onTap: () { + GuestChecker.check(onNotGuest: () { + Navigator.pushNamed( + context, Routes.transactionHistory); + }); + }, + ), + dividerWithSpacing(), + + customTile( + context, + title: UiUtils.translate( + context, + "personalized", + ), + svgImagePath: AppIcons.magic, + onTap: () { + GuestChecker.check(onNotGuest: () { + Navigator.pushNamed( + context, Routes.personalizedPropertyScreen, + arguments: { + "type": PersonalizedVisitType.Normal + }); + }); + }, + ), + dividerWithSpacing(), + + customTile( + context, + title: UiUtils.translate(context, "language"), + svgImagePath: AppIcons.language, + onTap: () { + Navigator.pushNamed( + context, Routes.languageListScreenRoute); + }, + ), + dividerWithSpacing(), + ValueListenableBuilder( + valueListenable: isDarkTheme, + builder: (context, v, c) { + return customTile( + context, + title: UiUtils.translate(context, "darkTheme"), + svgImagePath: AppIcons.darkTheme, + isSwitchBox: true, + onTapSwitch: (value) { + context.read().changeTheme( + value == true + ? AppTheme.dark + : AppTheme.light); + setState(() { + isDarkTheme.value = value; + }); + }, + switchValue: v, + onTap: () {}, + ); + }), + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate(context, "notifications"), + svgImagePath: AppIcons.notification, + onTap: () { + GuestChecker.check(onNotGuest: () { + Navigator.pushNamed( + context, Routes.notificationPage); + }); + }, + ), + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate(context, "articles"), + svgImagePath: AppIcons.articles, + onTap: () { + Navigator.pushNamed( + context, + Routes.articlesScreenRoute, + ); + }, + ), + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate(context, "favorites"), + svgImagePath: AppIcons.favorites, + onTap: () { + GuestChecker.check(onNotGuest: () { + Navigator.pushNamed( + context, Routes.favoritesScreen); + }); + }, + ), + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate(context, "areaConvertor"), + svgImagePath: AppIcons.areaConvertor, + onTap: () { + Navigator.pushNamed( + context, Routes.areaConvertorScreen); + }, + ), + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate(context, "shareApp"), + svgImagePath: AppIcons.shareApp, + onTap: shareApp, + ), + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate(context, "rateUs"), + svgImagePath: AppIcons.rateUs, + onTap: rateUs, + ), + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate(context, "contactUs"), + svgImagePath: AppIcons.contactUs, + onTap: () { + Navigator.pushNamed( + context, + Routes.contactUs, + ); + // Navigator.pushNamed(context, Routes.ab); + }, + ), + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate(context, "aboutUs"), + svgImagePath: AppIcons.aboutUs, + onTap: () { + Navigator.pushNamed(context, Routes.profileSettings, + arguments: { + 'title': UiUtils.translate(context, "aboutUs"), + 'param': Api.aboutApp + }); + // Navigator.pushNamed(context, Routes.ab); + }, + ), + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate( + context, + "termsConditions", + ), + svgImagePath: AppIcons.terms, + onTap: () { + Navigator.pushNamed(context, Routes.profileSettings, + arguments: { + 'title': UiUtils.translate( + context, "termsConditions"), + 'param': Api.termsAndConditions + }); + }, + ), + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate(context, "privacyPolicy"), + svgImagePath: AppIcons.privacy, + onTap: () { + Navigator.pushNamed( + context, + Routes.profileSettings, + arguments: { + 'title': + UiUtils.translate(context, "privacyPolicy"), + 'param': Api.privacyPolicy + }, + ); + }, + ), + if (Constant.isUpdateAvailable == true) ...[ + dividerWithSpacing(), + updateTile( + context, + isUpdateAvailable: Constant.isUpdateAvailable, + title: UiUtils.translate(context, "update"), + newVersion: Constant.newVersionNumber, + svgImagePath: AppIcons.update, + onTap: () async { + if (Platform.isIOS) { + await launchUrl( + Uri.parse(Constant.appstoreURLios)); + } else if (Platform.isAndroid) { + await launchUrl( + Uri.parse(Constant.playstoreURLAndroid)); + } + }, + ), + ], + + if (isGuest == false) ...[ + dividerWithSpacing(), + customTile( + context, + title: UiUtils.translate(context, "deleteAccount"), + svgImagePath: AppIcons.delete, + onTap: () { + if (Constant.isDemoModeOn) { + HelperUtils.showSnackBarMessage( + context, + UiUtils.translate( + context, "thisActionNotValidDemo")); + return; + } + + deleteConfirmWidget( + UiUtils.translate( + context, "deleteProfileMessageTitle"), + UiUtils.translate( + context, "deleteProfileMessageContent"), + true); + }, + ), + ], + const SizedBox( + height: 20, + ) + ], + ), + ), + const SizedBox( + height: 25, + ), + if (isGuest == false) ...[ + UiUtils.buildButton(context, onPressed: () { + logOutConfirmWidget(); + }, + height: 52.rh(context), + prefixWidget: Padding( + padding: const EdgeInsetsDirectional.only(end: 16.0), + child: Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular(10)), + child: FittedBox( + fit: BoxFit.none, + child: UiUtils.getSvg(AppIcons.logout, + color: context.color.tertiaryColor)), + ), + ), + buttonTitle: UiUtils.translate(context, "logout")) + ], + // profileInfo(), + // Expanded( + // child: profileMenus(), + // ) + ]), + ), + ), + ), + ), + ); + } + + Padding dividerWithSpacing() { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10.0), + child: UiUtils.getDivider(), + ); + } + + Widget updateTile(BuildContext context, + {required String title, + required String newVersion, + required bool isUpdateAvailable, + required String svgImagePath, + Function(dynamic value)? onTapSwitch, + dynamic switchValue, + required VoidCallback onTap}) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 25.0), + child: GestureDetector( + onTap: () { + if (isUpdateAvailable) { + onTap.call(); + } + }, + child: Row( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: context.color.tertiaryColor + .withOpacity(0.10000000149011612), + borderRadius: BorderRadius.circular(10), + ), + child: FittedBox( + fit: BoxFit.none, + child: isUpdateAvailable == false + ? const Icon(Icons.done) + : UiUtils.getSvg(svgImagePath, + color: context.color.tertiaryColor)), + ), + SizedBox( + width: 25.rw(context), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(isUpdateAvailable == false + ? "uptoDate".translate(context) + : title) + .bold(weight: FontWeight.w700) + .color(context.color.textColorDark), + if (isUpdateAvailable) + Text("v$newVersion") + .bold(weight: FontWeight.w300) + .color(context.color.textColorDark) + .size(context.font.small) + .italic() + ], + ), + if (isUpdateAvailable) ...[ + const Spacer(), + Container( + width: 32, + height: 32, + decoration: BoxDecoration( + border: + Border.all(color: context.color.borderColor, width: 1.5), + color: context.color.secondaryColor + .withOpacity(0.10000000149011612), + borderRadius: BorderRadius.circular(10), + ), + child: FittedBox( + fit: BoxFit.none, + child: SizedBox( + width: 8, + height: 15, + child: UiUtils.getSvg( + AppIcons.arrowRight, + color: context.color.textColorDark, + ), + ), + ), + ), + ] + ], + ), + ), + ); + } + + Widget customTile(BuildContext context, + {required String title, + required String svgImagePath, + bool? isSwitchBox, + Function(dynamic value)? onTapSwitch, + dynamic switchValue, + required VoidCallback onTap}) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 25.0), + child: GestureDetector( + onTap: onTap, + child: AbsorbPointer( + absorbing: !(isSwitchBox ?? false), + child: Row( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: context.color.tertiaryColor + .withOpacity(0.10000000149011612), + borderRadius: BorderRadius.circular(10), + ), + child: FittedBox( + fit: BoxFit.none, + child: UiUtils.getSvg( + svgImagePath, + height: 24, + width: 24, + color: context.color.tertiaryColor, + ), + ), + ), + SizedBox( + width: 25.rw(context), + ), + Expanded( + flex: 3, + child: Text(title) + .bold(weight: FontWeight.w700) + .color(context.color.textColorDark), + ), + const Spacer(), + if (isSwitchBox != true) + Container( + width: 32, + height: 32, + decoration: BoxDecoration( + border: Border.all( + color: context.color.borderColor, width: 1.5), + color: context.color.secondaryColor + .withOpacity(0.10000000149011612), + borderRadius: BorderRadius.circular(10), + ), + child: FittedBox( + fit: BoxFit.none, + child: SizedBox( + width: 8, + height: 15, + child: UiUtils.getSvg( + AppIcons.arrowRight, + color: context.color.textColorDark, + ), + ), + ), + ), + if (isSwitchBox ?? false) + // CupertinoSwitch(value: value, onChanged: onChanged) + SizedBox( + height: 40, + width: 30, + child: CupertinoSwitch( + activeColor: context.color.tertiaryColor, + value: switchValue ?? false, + onChanged: (value) { + onTapSwitch?.call(value); + }, + ), + ) + ], + ), + ), + ), + ); + } + + deleteConfirmWidget(String title, String desc, bool callDel) { + UiUtils.showBlurredDialoge( + context, + dialoge: BlurredDialogBox( + title: title, + content: Text(desc, textAlign: TextAlign.center), + acceptButtonName: "deleteBtnLbl".translate(context), + cancelTextColor: context.color.textColorDark, + svgImagePath: AppIcons.deleteIcon, + isAcceptContainesPush: true, + onAccept: () async { + Navigator.of(context).pop(); + if (callDel) { + Future.delayed( + const Duration(microseconds: 100), + () { + Navigator.pushNamed(context, Routes.login, + arguments: {"isDeleteAccount": true}); + }, + ); + } else { + HiveUtils.logoutUser( + context, + onLogout: () {}, + ); + } + }, + ), + ); + } + + Widget profileImgWidget() { + return GestureDetector( + onTap: () { + if (HiveUtils.getUserDetails().profile != "" && + HiveUtils.getUserDetails().profile != null) { + UiUtils.showFullScreenImage( + context, + provider: NetworkImage( + context.read().state.user?.profile ?? ""), + ); + } + }, + child: (context.watch().state.user?.profile ?? "") + .trim() + .isEmpty + ? buildDefaultPersonSVG(context) + : Image.network( + context.watch().state.user?.profile ?? "", + fit: BoxFit.cover, + width: 49, + height: 49, + errorBuilder: (BuildContext context, Object exception, + StackTrace? stackTrace) { + return buildDefaultPersonSVG(context); + }, + loadingBuilder: (BuildContext context, Widget? child, + ImageChunkEvent? loadingProgress) { + if (loadingProgress == null) return child!; + return buildDefaultPersonSVG(context); + }, + ), + ); + } + + Widget buildDefaultPersonSVG(BuildContext context) { + return Container( + width: 49, + height: 49, + color: context.color.tertiaryColor.withOpacity(0.1), + child: FittedBox( + fit: BoxFit.none, + child: UiUtils.getSvg(AppIcons.defaultPersonLogo, + color: context.color.tertiaryColor, width: 30, height: 30), + ), + ); + } + + void shareApp() { + try { + if (Platform.isAndroid) { + Share.share( + '${Constant.appName}\n${Constant.playstoreURLAndroid}\n${Constant.shareappText}', + subject: Constant.appName); + } else { + Share.share( + '${Constant.appName}\n${Constant.appstoreURLios}\n${Constant.shareappText}', + subject: Constant.appName); + } + } catch (e) { + HelperUtils.showSnackBarMessage(context, e.toString()); + } + } + + Future rateUs() async { + LaunchReview.launch( + androidAppId: Constant.androidPackageName, + iOSAppId: Constant.iOSAppId, + ); + } + + void logOutConfirmWidget() { + UiUtils.showBlurredDialoge(context, + dialoge: BlurredDialogBox( + title: UiUtils.translate(context, "confirmLogoutTitle"), + onAccept: () async { + Future.delayed( + Duration.zero, + () { + HiveUtils.clear(); + Constant.favoritePropertyList.clear(); + context.read().clear(); + context.read().state.liked.clear(); + + context.read().clear(); + HiveUtils.logoutUser(context, onLogout: () {}); + }, + ); + }, + cancelTextColor: context.color.textColorDark, + svgImagePath: AppIcons.logoutIcon, + content: Text(UiUtils.translate(context, "confirmLogOutMsg")))); + } +} diff --git a/lib/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart b/lib/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart new file mode 100644 index 0000000..dd62a26 --- /dev/null +++ b/lib/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart @@ -0,0 +1,74 @@ +import 'dart:io'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +///This router will be show blurred transations +///this will provide fade transiton also. +///this is also being use to show background blur dialoge boxes +class BlurredRouter extends PageRoute { + final double? sigmaX; + final double? sigmaY; + final bool? barrierDismiss; + BlurredRouter( + {required this.builder, + this.barrierDismiss, + RouteSettings? settings, + this.sigmaX, + this.sigmaY}) + : super(settings: settings, fullscreenDialog: false); + + final WidgetBuilder builder; + + @override + bool get opaque => false; + @override + Color get barrierColor => Colors.transparent; + @override + bool get barrierDismissible => barrierDismiss ?? super.barrierDismissible; + + @override + String get barrierLabel => "blurred"; + + @override + bool get maintainState => true; + + @override + Duration get transitionDuration => const Duration(milliseconds: 350); + + @override + Widget buildPage(BuildContext context, Animation animation, + Animation secondaryAnimation) { + final result = builder(context); +// Tween tween = Tween(begin: 0,end: 0); + + ///We have to show swipe gesture in ios wo we are making condition here + if (Platform.isIOS) { + var theme = Theme.of(context).pageTransitionsTheme; + + return theme.buildTransitions( + this, + context, + animation, + Animation.fromValueListenable(ValueNotifier(0)), + BackdropFilter( + filter: ImageFilter.blur( + sigmaX: sigmaX ?? 5, + sigmaY: sigmaY ?? 10, + ), + child: result, + ), + ); + } + return FadeTransition( + opacity: Tween(begin: 0, end: 1).animate(animation), + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: sigmaX ?? 5, + sigmaY: sigmaY ?? 10, + ), + child: result, + ), + ); + } +} diff --git a/lib/Ui/screens/widgets/AnimatedRoutes/scale_up_route.dart b/lib/Ui/screens/widgets/AnimatedRoutes/scale_up_route.dart new file mode 100644 index 0000000..1acab5d --- /dev/null +++ b/lib/Ui/screens/widgets/AnimatedRoutes/scale_up_route.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; + +class ScaleUpRouter extends PageRoute { + final WidgetBuilder builder; + final Widget current; + ScaleUpRouter({ + required this.builder, + required this.current, + }); + + @override + Color? get barrierColor => Colors.transparent; + + @override + String? get barrierLabel => null; + + @override + bool get maintainState => true; + + @override + Duration get transitionDuration => const Duration(milliseconds: 400); + + @override + Widget buildPage(BuildContext context, Animation animation, + Animation secondaryAnimation) { + return builder(context); + } + + @override + Widget buildTransitions(BuildContext context, Animation animation, + Animation secondaryAnimation, Widget child) { + return Stack( + children: [ + ScaleTransition( + scale: Tween( + begin: 1, + end: 5, + ).animate( + CurvedAnimation( + parent: animation, + curve: Curves.linear, + ), + ), + child: current, + ), + ScaleTransition( + scale: Tween( + begin: 0, + end: 1, + ).animate( + CurvedAnimation( + parent: animation, + curve: Curves.linear, + ), + ), + child: child, + ) + ], + ); + } +} diff --git a/lib/Ui/screens/widgets/AnimatedRoutes/transparant_route.dart b/lib/Ui/screens/widgets/AnimatedRoutes/transparant_route.dart new file mode 100644 index 0000000..901af43 --- /dev/null +++ b/lib/Ui/screens/widgets/AnimatedRoutes/transparant_route.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +///This router will be show blurred transations +///this will provide fade transiton also. +///this is also being use to show background blur dialoge boxes +class TransparantRoute extends PageRoute { + final double? sigmaX; + final double? sigmaY; + final bool? barrierDismiss; + TransparantRoute( + {required this.builder, + this.barrierDismiss, + RouteSettings? settings, + this.sigmaX, + this.sigmaY}) + : super(settings: settings, fullscreenDialog: false); + + final WidgetBuilder builder; + + @override + bool get opaque => false; + @override + Color get barrierColor => Colors.transparent; + @override + bool get barrierDismissible => true; + + @override + String get barrierLabel => "blurred"; + + @override + bool get maintainState => true; + + @override + Duration get transitionDuration => const Duration(milliseconds: 350); + + @override + Widget buildPage(BuildContext context, Animation animation, + Animation secondaryAnimation) { + final result = builder(context); + + return result; + } +} diff --git a/lib/Ui/screens/widgets/BottomSheets/choose_location_bottomsheet.dart b/lib/Ui/screens/widgets/BottomSheets/choose_location_bottomsheet.dart new file mode 100644 index 0000000..0f23818 --- /dev/null +++ b/lib/Ui/screens/widgets/BottomSheets/choose_location_bottomsheet.dart @@ -0,0 +1,186 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../../data/Repositories/location_repository.dart'; +import '../../../../data/cubits/Utility/google_place_autocomplate_cubit.dart'; +import '../../../../data/model/google_place_model.dart'; +import '../../../../utils/Extensions/extensions.dart'; +import '../../../../utils/ui_utils.dart'; + +///This will show when you will need to fill your location, +/// +class ChooseLocatonBottomSheet extends StatefulWidget { + const ChooseLocatonBottomSheet({Key? key}) : super(key: key); + + @override + State createState() => + ChooseLocatonBottomSheetState(); +} + +class ChooseLocatonBottomSheetState extends State { + final TextEditingController _searchLocation = TextEditingController(); + Timer? delayTimer; + dynamic cubitReferance; + int previouseLength = 0; + + @override + void initState() { + super.initState(); + + ///This will create listener which will listen to out text change in text field + _searchLocation.addListener(() { + ///If there is no text in text field so we don't need to call an API. + ///Therefor we are cancel this timer + /// + if (_searchLocation.text.isEmpty) { + delayTimer?.cancel(); + } + + ///If our timer is already active so we will cancel it, + /*For eg, API will call after 500 miliseconds when we write text in TextField and + we wait, If we try to write in that field again so timer is already active and it is + on 300 miliseconds , Now we have not completed our writing and API will call on 500 miliseconds + , To prevent this we cancel timer when we write again in that field + */ + if (delayTimer?.isActive ?? false) delayTimer?.cancel(); + + ///Create new timer after cancel previous one + delayTimer = Timer(const Duration(milliseconds: 500), () { + ///Search only if text field is not empty otherwise it will call when we tap on search field, + if (_searchLocation.text.isNotEmpty) { + ///Only call when our text doesn't match with our previous text, + ///When we search `Hello` then it will call API and search city named hello, when we write again hello so it will call again, So why do we need to call it when we have it's data already available? + if (_searchLocation.text.length != previouseLength) { + context + .read() + .getLocationFromText(text: _searchLocation.text); + + ///set previous text length + previouseLength = _searchLocation.text.length; + } + } + }); + }); + } + + @override + void dispose() { + _searchLocation.dispose(); + delayTimer?.cancel(); + cubitReferance.clearCubit(); + super.dispose(); + } + + @override + void didChangeDependencies() { + cubitReferance = context.read(); + super.didChangeDependencies(); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + height: MediaQuery.of(context).size.height * 0.7, + child: Padding( + padding: const EdgeInsets.all(15.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + UiUtils.translate( + context, + "selectLocation", + ), + style: TextStyle(fontSize: context.font.larger), + ), + const SizedBox(height: 20), + TextField( + controller: _searchLocation, + onChanged: (e) {}, + cursorColor: context.color.tertiaryColor, + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderSide: + BorderSide(color: context.color.tertiaryColor)), + fillColor: context.color.tertiaryColor.withOpacity(0.01), + filled: true, + prefixIcon: Icon( + Icons.search, + color: context.color.tertiaryColor, + ), + hintText: UiUtils.translate(context, "enterLocation"), + border: const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(11))))), + BlocBuilder( + builder: + (context, GooglePlaceAutocompleteState googlePlaceState) { + if (googlePlaceState is GooglePlaceAutocompleteSuccess) { + if (googlePlaceState.autocompleteResult.isNotEmpty) { + return ListView.builder( + itemCount: googlePlaceState.autocompleteResult.length, + shrinkWrap: true, + itemBuilder: (context, int i) { + return ListTile( + onTap: () async { + ///This will fetch place details from given PlaceId + var cordinates = await GooglePlaceRepository() + .getPlaceDetailsFromPlaceId(googlePlaceState + .autocompleteResult[i].placeId); + + GooglePlaceModel placeModel = + googlePlaceState.autocompleteResult[i]; + + ///Now we have place Model + placeModel = placeModel.copyWith( + latitude: cordinates['lat'].toString(), + longitude: cordinates['lng'].toString()); + + Future.delayed( + Duration.zero, + () { + WidgetsBinding.instance + .addPostFrameCallback((timeStamp) { + Navigator.pop( + context, + placeModel, + ); + }); + }, + ); + }, + leading: const Icon(Icons.location_city), + title: Text(googlePlaceState + .autocompleteResult[i].description + .toString()), + ); + }); + } + return Padding( + padding: const EdgeInsetsDirectional.only(top: 8.0), + child: Center( + child: Text(UiUtils.translate(context, 'nodatafound'))), + ); + } + + ///Show progress when loading + if (googlePlaceState is GooglePlaceAutocompleteInProgress) { + return Padding( + padding: const EdgeInsetsDirectional.only(top: 8.0), + child: Center( + child: UiUtils.progress( + normalProgressColor: context.color.tertiaryColor), + ), + ); + } + return Container(); + }, + ) + ], + ), + ), + ); + } +} diff --git a/lib/Ui/screens/widgets/DynamicField/dynamic_field.dart b/lib/Ui/screens/widgets/DynamicField/dynamic_field.dart new file mode 100644 index 0000000..33ace0e --- /dev/null +++ b/lib/Ui/screens/widgets/DynamicField/dynamic_field.dart @@ -0,0 +1,1329 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +// ignore_for_file: invalid_use_of_visible_for_testing_member + +import 'dart:convert'; +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:dotted_border/dotted_border.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/helper_utils.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:ebroker/utils/ui_utils.dart'; +import 'package:file_icon/src/data.dart' as d; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +import '../../../../utils/AppIcon.dart'; +import '../custom_text_form_field.dart'; + +/// Note: Here i have used abstract factory pattern and builder pattern +/// You can learn design patterns from internet +/// so don't be confuse +List kDoNotReBuildThese = []; +List kDoNotReBuildDropdown = []; + +abstract class AbstractField { + final BuildContext context; + static Map fieldsData = {}; + AbstractField(this.context); + Widget createField(Map parameters); +} + +class AbstractTextField extends AbstractField { + AbstractTextField(BuildContext context) + : super( + context, + ); + // TextEditingController? _controller; + + ///Here Builder pattern to set values, + /// because when if we get it from constructor it will be messed in Factory class so it + + ///You can uncomment it if you want to use controller out side of the class + + // AbstractTextField setController(TextEditingController controller) { + // _controller = controller; + // return this; + // } + + @override + Widget createField(parameters) { + return Column( + children: [ + Row( + children: [ + Container( + width: 48.rw(context), + height: 48.rh(context), + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Container( + height: 24, + width: 24, + child: FittedBox( + fit: BoxFit.none, + child: UiUtils.imageType(parameters['image'], + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null, + width: 24, + height: 24, + fit: BoxFit.cover), + ), + ), + ), + SizedBox( + width: 10.rw(context), + ), + Text(parameters['name']) + .size(context.font.large) + .bold(weight: FontWeight.w500) + .color(context.color.textColorDark) + ], + ), + SizedBox( + height: 14.rh(context), + ), + CustomTextFieldDynamic( + action: TextInputAction.next, + initController: parameters['value'] != null ? true : false, + value: parameters['value'].toString(), + hintText: "writeSomething".translate(context), + id: parameters['id'], + ) + ], + ); + } +} + +class AbstractTextAreaField extends AbstractField { + AbstractTextAreaField(BuildContext context) + : super( + context, + ); + // TextEditingController? _controller; + + /// Here Builder pattern to set values, + /// because when if we get it from constructor it will be messed in Factory class so it + /// You can uncomment it if you want to use controller out side of class + //AbstractTextAreaField setController(TextEditingController controller) { + // // _controller = controller; + // return this; + //} + + @override + Widget createField(parameters) { + return Padding( + padding: const EdgeInsets.symmetric( + vertical: .0, + ), + child: Column( + children: [ + Row( + children: [ + Container( + width: 48.rw(context), + height: 48.rh(context), + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Container( + height: 24, + width: 24, + child: FittedBox( + fit: BoxFit.none, + child: UiUtils.imageType(parameters['image'], + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null, + width: 24, + height: 24, + fit: BoxFit.cover), + ), + ), + ), + SizedBox( + width: 10.rw(context), + ), + Text(parameters['name']) + .size(context.font.large) + .color( + context.color.textColorDark, + ) + .bold(weight: FontWeight.w500) + ], + ), + SizedBox( + height: 14.rh(context), + ), + CustomTextFormField( + hintText: "Write something...", + minLine: 4, + maxLine: 100, + validator: CustomTextFieldValidator.maxFifty, + controller: parameters['value'] != null + ? TextEditingController(text: parameters['value'].toString()) + : null, + onChange: (value) { + AbstractField.fieldsData.addAll({parameters['id']: value}); + }, + ) + ], + ), + ); + + // return TextFormField( + // maxLines: null, + // minLines: 5, + // onChanged: (value) { + // AbstractField.fieldsData.addAll({parameters['id']: value}); + // }, + // decoration: InputDecoration(hintText: parameters['name']), + // ); + } +} + +class AbstractNumberField extends AbstractField { + AbstractNumberField(BuildContext context) + : super( + context, + ); + // TextEditingController? _controller; + + ///Here, Builder pattern to set values, + /// because when if we get it from constructor it will be messed in Factory class so it + + ///You can uncomment it if you want to use controller out side of class + // AbstractNumberField setController(TextEditingController controller) { + // _controller = controller; + // return this; + // } + + @override + Widget createField(parameters) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 0.0), + child: Column( + children: [ + Row( + children: [ + Container( + width: 48.rw(context), + height: 48.rh(context), + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: SizedBox( + height: 24, + width: 24, + child: FittedBox( + fit: BoxFit.none, + child: UiUtils.imageType(parameters['image'], + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null, + width: 24, + height: 24, + fit: BoxFit.cover), + ), + ), + ), + SizedBox( + width: 10.rw(context), + ), + Text(parameters['name']) + .size(context.font.large) + .bold(weight: FontWeight.w500) + .color(context.color.textColorDark) + ], + ), + SizedBox( + height: 14.rh(context), + ), + CustomTextFieldDynamic( + initController: parameters['value'] != null ? true : false, + value: parameters['value'].toString(), + hintText: "addNumerical".translate(context), + formaters: [ + FilteringTextInputFormatter.allow( + RegExp("[0-9]"), + ), + ], + action: TextInputAction.next, + keyboardType: TextInputType.number, + id: parameters['id'], + ), + ], + )); + } +} + +class AbstractDropdown extends AbstractField { + Function(dynamic onData)? _onChange; + List kDropdownSelected = []; + UniqueKey _key = UniqueKey(); + List? _items; + dynamic selectedItem; + AbstractDropdown( + BuildContext context, + ) : super(context); + + ///We can say it method chaining + ///Here this is builder pattern used here it will return it self after assign value so new class has already assigned value + AbstractDropdown setOnChange(Function(dynamic onChange) onChange) { + _onChange = onChange; + return this; + } + + AbstractDropdown setItems(List items) { + _items = items; + return this; + } + + AbstractDropdown setSelectedItem(dynamic item) { + if (item != null) { + if (!kDoNotReBuildDropdown.contains(_key)) { + kDoNotReBuildDropdown.add(_key); + dropDownItemChange.value = item; + } + } + + return this; + } + + late ValueNotifier dropDownItemChange = + ValueNotifier(_items?.first); + + @override + Widget createField(parameters) { + return CustomDropdownState( + key: _key, + dropDownItemChange: dropDownItemChange, + parameters: parameters, + items: _items ?? [], + ); + + return ValueListenableBuilder( + valueListenable: dropDownItemChange, + builder: (context, value, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 48.rw(context), + height: 48.rh(context), + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity( + 0.1, + ), + borderRadius: BorderRadius.circular( + 10, + ), + ), + child: UiUtils.imageType(parameters['image'], + fit: BoxFit.none), + ), + SizedBox( + width: 10.rw( + context, + ), + ), + Text( + parameters['name'], + ) + .size( + context.font.large, + ) + .color( + context.color.textColorDark, + ) + ], + ), + SizedBox( + height: 10.rh(context), + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 3, + ), + child: Container( + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular( + 10, + ), + border: Border.all( + width: 1.5, + color: context.color.borderColor, + )), + child: Padding( + padding: const EdgeInsets.all( + 8.0, + ), + child: SizedBox( + width: double.infinity, + child: DropdownButton( + value: value, + isExpanded: true, + padding: EdgeInsets.symmetric(vertical: 5), + icon: SvgPicture.asset(AppIcons.downArrow), + isDense: true, + borderRadius: BorderRadius.circular( + 10, + ), + underline: const SizedBox.shrink(), + items: _items + ?.map((e) => DropdownMenuItem( + value: e, + child: Text( + e, + ), + )) + .toList(), + onChanged: (dynamic v) { + dropDownItemChange.value = v; + AbstractField.fieldsData.addAll( + { + parameters['id']: v, + }, + ); + + _onChange?.call(v); + }, + ), + ), + ), + ), + ), + SizedBox( + height: 10.rh( + context, + ), + ) + ], + ); + }); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is AbstractDropdown && + runtimeType == other.runtimeType && + _onChange == other._onChange && + kDropdownSelected == other.kDropdownSelected && + _key == other._key && + _items == other._items && + selectedItem == other.selectedItem && + dropDownItemChange == other.dropDownItemChange; + + @override + int get hashCode => + _onChange.hashCode ^ + kDropdownSelected.hashCode ^ + _key.hashCode ^ + _items.hashCode ^ + selectedItem.hashCode ^ + dropDownItemChange.hashCode; +} + +class AbstractRadioButton extends AbstractField { + AbstractRadioButton(BuildContext context) : super(context); + List? _radioValues; + // late ValueNotifier selectedRadio; + + AbstractRadioButton setValues( + List values, + ) { + _radioValues = values; + // selectedRadio = ValueNotifier(_radioValues?.first); + return this; + } + + @override + Widget createField(parameters) { + return CustomRadioButtonWidget( + parameters: parameters, + radioValues: _radioValues, + ); + // return ValueListenableBuilder( + // valueListenable: selectedRadio, + // builder: (context, value, child) { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Row( + // children: [ + // Container( + // width: 48.rw(context), + // height: 48.rh(context), + // decoration: BoxDecoration( + // color: context.color.teritoryColor.withOpacity(0.1), + // borderRadius: BorderRadius.circular(10), + // ), + // child: UiUtils.imageType(parameters['image'], + // fit: BoxFit.none), + // ), + // SizedBox( + // width: 10.rw(context), + // ), + // Text(parameters['name']) + // .size(context.font.large) + // .color(context.color.textColorDark) + // ], + // ), + // SizedBox( + // height: 10.rh(context), + // ), + // Wrap( + // alignment: WrapAlignment.start, + // runAlignment: WrapAlignment.start, + // crossAxisAlignment: WrapCrossAlignment.start, + // children: List.generate( + // _radioValues?.length ?? 0, + // (index) => Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Radio( + // value: _radioValues?[index], + // groupValue: value, + // fillColor: MaterialStatePropertyAll( + // context.color.teritoryColor), + // onChanged: (dynamic e) { + // selectedRadio.value = e; + // AbstractField.fieldsData + // .addAll({parameters['id']: e}); + // }, + // ), + // Text(_radioValues?[index]) + // ], + // ))) + // ], + // ); + + // // return Column( + // // crossAxisAlignment: CrossAxisAlignment.start, + // // children: [ + // // Text(parameters['name']), + // // Wrap( + // // children: _radioValues + // // ?.map((item) { + // // return Row( + // // mainAxisSize: MainAxisSize.min, + // // children: [ + // // Text(item), + // // Radio( + // // value: item, + // // groupValue: value, + // // onChanged: (dynamic e) { + // // selectedRadio.value = e; + // // AbstractField.fieldsData + // // .addAll({parameters['id']: e}); + // // }), + // // ], + // // ); + // // }) + // // .toList() + // // .cast() ?? + // // [], + // // ), + // // ], + // // ); + // }); + } +} + +class AbstractCheckBoxButton extends AbstractField { + AbstractCheckBoxButton(BuildContext context) : super(context); + + List? _checkValues; + + bool initComplete = false; + AbstractCheckBoxButton setCheckBoxValues(List values) { + _checkValues = values; + return this; + } + + @override + Widget createField(parameters) { + return CustomCheckBox( + parameters: parameters, + checkValues: _checkValues, + index: 0, + initComplete: true, + ); + } + + void dispose() { + // checked.dispose(); + } +} + +class AbstractPickFileButton extends AbstractField { + AbstractPickFileButton(BuildContext context) : super(context); + ValueNotifier filePicked = ValueNotifier(false); + ValueNotifier picked = ValueNotifier(null); + Future pickFile() async { + FilePickerResult? picker = await FilePicker.platform.pickFiles(); + if (picker != null) { + filePicked.value = true; + File file = File( + picker.files.single.path!, + ); + picked.value = file.path; + return file; + } + filePicked.value = false; + picked.value = null; + return null; + } + + @override + Widget createField(parameters) { + picked.value = parameters['value']; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 48.rw(context), + height: 48.rh(context), + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Container( + height: 24, + width: 24, + child: FittedBox( + fit: BoxFit.none, + child: UiUtils.imageType(parameters['image'], + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null, + width: 24, + height: 24, + fit: BoxFit.cover), + ), + ), + ), + SizedBox( + width: 10.rw(context), + ), + Text(parameters['name']) + .size(context.font.large) + .bold(weight: FontWeight.w500) + .color(context.color.textColorDark), + ], + ), + SizedBox( + height: 14.rh(context), + ), + GestureDetector( + onTap: () async { + File? file = await pickFile(); + if (file != null) { + MultipartFile multipartFile = + await MultipartFile.fromFile(file.path); + + /// Add data to static Map + AbstractField.fieldsData + .addAll({parameters['id']: multipartFile}); + } + }, + child: DottedBorder( + borderType: BorderType.RRect, + radius: const Radius.circular(10), + color: context.color.textLightColor, + strokeCap: StrokeCap.round, + padding: const EdgeInsets.all(5), + dashPattern: const [3, 3], + child: Container( + width: double.infinity, + height: 43, + decoration: + BoxDecoration(borderRadius: BorderRadius.circular(10)), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.add), + const SizedBox( + width: 5, + ), + const Text("Add File") + .color(context.color.textLightColor) + .size(context.font.large) + ], + ), + )), + ), + ValueListenableBuilder( + valueListenable: picked, + builder: (context, String? pickedFilePath, c) { + if (pickedFilePath == null) { + return const SizedBox.shrink(); + } + return Container( + child: Row( + children: [ + Icon( + IconData( + d.iconSetMap[".${pickedFilePath.split(".").last}"]! + .codePoint, + fontFamily: 'Seti', + fontPackage: 'file_icon', + ), + color: context.color.tertiaryColor, + size: 35, + ), + const SizedBox( + width: 5, + ), + Expanded( + flex: 2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(pickedFilePath.split("/").last) + .setMaxLines(lines: 1) + .bold(weight: FontWeight.w500), + if (!(pickedFilePath.startsWith("http") || + pickedFilePath.startsWith("https"))) + Text(HelperUtils.getFileSizeString( + bytes: File(pickedFilePath).lengthSync(), + ).toUpperCase()) + .size(context.font.smaller) + ], + )), + const Spacer(flex: 1), + IconButton( + onPressed: () { + AbstractField.fieldsData.remove(parameters['id']); + + picked.value = null; + }, + icon: Icon(Icons.close)) + ], + ), + ); + }) + ], + ); + + // return MaterialButton( + // onPressed: () async { + // File? file = await pickFile(); + // if (file != null) { + // MultipartFile multipartFile = await MultipartFile.fromFile(file.path); + + // /// Add data to static Map + // AbstractField.fieldsData.addAll({parameters['id']: multipartFile}); + // } + // }, + // color: Colors.grey.shade200, + // child: const Text("Pick"), + // ); + } +} + +///Factory class which will return class according to type +class FieldFactory { + static AbstractField getField(BuildContext context, String fieldType) { + if (fieldType == 'textbox') { + return AbstractTextField(context); + } else if (fieldType == 'dropdown') { + return AbstractDropdown(context); + } else if (fieldType == 'radiobutton') { + return AbstractRadioButton(context); + } else if (fieldType == 'number') { + return AbstractNumberField( + context, + ); + } else if (fieldType == "checkbox") { + return AbstractCheckBoxButton(context); + } else if (fieldType == "textarea") { + return AbstractTextAreaField(context); + } else if (fieldType == "file") { + return AbstractPickFileButton(context); + } + throw Exception('Invalid field type: $fieldType'); + } +} + +class CustomRadioButtonWidget extends StatefulWidget { + final dynamic parameters; + final dynamic radioValues; + const CustomRadioButtonWidget({super.key, this.parameters, this.radioValues}); + + @override + State createState() => + _CustomRadioButtonWidgetState(); +} + +class _CustomRadioButtonWidgetState extends State { + late ValueNotifier selectedRadio; + bool isInitialized = false; + @override + void initState() { + selectedRadio = ValueNotifier(widget.radioValues?.first); + + if (widget.parameters['value'] != null && isInitialized == false) { + selectedRadio.value = widget.parameters['value']; + isInitialized = true; + } + super.initState(); + } + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: selectedRadio, + builder: (context, value, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 48.rw(context), + height: 48.rh(context), + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Container( + height: 24, + width: 24, + child: FittedBox( + fit: BoxFit.none, + child: UiUtils.imageType(widget.parameters['image'], + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null, + width: 24, + height: 24, + fit: BoxFit.cover), + ), + ), + ), + SizedBox( + width: 10.rw(context), + ), + Text(widget.parameters['name']) + .size(context.font.large) + .bold(weight: FontWeight.w500) + .color(context.color.textColorDark) + ], + ), + SizedBox( + height: 14.rh(context), + ), + Wrap( + alignment: WrapAlignment.start, + runAlignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.start, + children: + List.generate(widget.radioValues?.length ?? 0, (index) { + return Padding( + padding: EdgeInsetsDirectional.only( + start: index == 0 ? 0 : 4, + end: 4, + bottom: 4, + top: 4, + ), + child: InkWell( + borderRadius: BorderRadius.circular(10), + onTap: () { + selectedRadio.value = widget.radioValues?[index]; + AbstractField.fieldsData.addAll({ + widget.parameters['id']: widget.radioValues?[index] + }); + }, + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: context.color.borderColor, width: 1.5), + color: selectedRadio.value == + widget.radioValues?[index] + ? context.color.tertiaryColor.withOpacity(0.1) + : context.color.secondaryColor, + borderRadius: BorderRadius.circular(10)), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, horizontal: 15), + child: Text(widget.radioValues?[index]).color( + selectedRadio.value == + widget.radioValues?[index] + ? context.color.tertiaryColor + : context.color.textLightColor)), + ), + ), + ); + + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Radio( + value: widget.radioValues?[index], + groupValue: value, + fillColor: MaterialStatePropertyAll( + context.color.tertiaryColor), + onChanged: (dynamic e) { + selectedRadio.value = e; + AbstractField.fieldsData + .addAll({widget.parameters['id']: e}); + }, + ), + Text(widget.radioValues?[index]) + ], + ); + })) + ], + ); + + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text(parameters['name']), + // Wrap( + // children: _radioValues + // ?.map((item) { + // return Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Text(item), + // Radio( + // value: item, + // groupValue: value, + // onChanged: (dynamic e) { + // selectedRadio.value = e; + // AbstractField.fieldsData + // .addAll({parameters['id']: e}); + // }), + // ], + // ); + // }) + // .toList() + // .cast() ?? + // [], + // ), + // ], + // ); + }); + } +} + +class CustomCheckBox extends StatefulWidget { + final dynamic parameters; + final dynamic checkValues; + final dynamic initComplete; + final dynamic index; + const CustomCheckBox( + {super.key, + this.parameters, + this.checkValues, + this.initComplete, + this.index}); + + @override + State createState() => _CustomCheckBoxState(); +} + +class _CustomCheckBoxState extends State { + final ValueNotifier checked = ValueNotifier([]); + @override + void initState() { + // log("VA ${widget.parameters}"); + if (widget.parameters.containsKey("value")) { + List valueList = widget.parameters['value'].toString().split(","); + if (valueList.isNotEmpty) { + checked.value.addAll(valueList); + // checked.value.add(widget.checkValues?[widget.index]); + var entries = checked.value.asMap().entries; + Map data = Map.fromEntries(entries); + Map stringedData = data.map((key, value) { + return MapEntry(key.toString(), value); + }); + + AbstractField.fieldsData + .addAll({widget.parameters['id']: json.encode(stringedData)}); + } + } + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: checked, + builder: (context, List value, Widget? c) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 48.rw(context), + height: 48.rh(context), + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Container( + height: 24, + width: 24, + child: FittedBox( + fit: BoxFit.none, + child: UiUtils.imageType(widget.parameters['image'], + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null, + width: 24, + height: 24, + fit: BoxFit.cover), + ), + ), + ), + SizedBox( + width: 10.rw(context), + ), + Text(widget.parameters['name']) + .size(context.font.large) + .bold(weight: FontWeight.w500) + .color(context.color.textColorDark) + ], + ), + SizedBox( + height: 14.rh(context), + ), + Wrap( + alignment: WrapAlignment.start, + runAlignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.start, + children: List.generate( + widget.checkValues?.length ?? 0, + (index) { + //this variable will prevent adding when state change + ///this will work like init state text + if (widget.initComplete == false && + (!kDoNotReBuildThese.contains(widget.parameters['id']))) { + kDoNotReBuildThese.add(widget.parameters['id']); + + // widget.initComplete = true; + } + + return Padding( + padding: EdgeInsetsDirectional.only( + start: index == 0 ? 0 : 4, bottom: 4, top: 4, end: 4), + child: InkWell( + borderRadius: BorderRadius.circular(10), + onTap: () { + if (checked.value + .contains(widget.checkValues?[index])) { + checked.value.remove(widget.checkValues?[index]); + // ignore: invalid_use_of_protected_member + checked.notifyListeners(); + } else { + checked.value.add(widget.checkValues?[index]); + // ignore: invalid_use_of_protected_member + checked.notifyListeners(); + } + + var entries = checked.value.asMap().entries; + Map data = Map.fromEntries(entries); + Map temp = {}; + data.forEach((key, value) { + temp[key.toString()] = value; + }); + + AbstractField.fieldsData.addAll( + {widget.parameters['id']: json.encode(temp)}); + }, + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: context.color.borderColor, width: 1.5), + color: value.contains(widget.checkValues?[index]) + ? context.color.tertiaryColor.withOpacity(0.1) + : context.color.secondaryColor, + borderRadius: BorderRadius.circular(10)), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8.0, horizontal: 14), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + value.contains(widget.checkValues?[index]) + ? Icons.done + : Icons.add, + color: + value.contains(widget.checkValues?[index]) + ? context.color.tertiaryColor + : context.color.textColorDark, + ), + const SizedBox( + width: 5, + ), + Text(widget.checkValues?[index]).color( + value.contains(widget.checkValues?[index]) + ? context.color.tertiaryColor + : context.color.textLightColor) + ], + ), + ), + ), + ), + ); + + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Checkbox( + fillColor: MaterialStatePropertyAll( + context.color.tertiaryColor), + value: value.contains(widget.checkValues?[index]), + onChanged: (v) { + if (checked.value + .contains(widget.checkValues?[index])) { + checked.value.remove(widget.checkValues?[index]); + // ignore: invalid_use_of_protected_member + checked.notifyListeners(); + } else { + checked.value.add(widget.checkValues?[index]); + // ignore: invalid_use_of_protected_member + checked.notifyListeners(); + } + + var entries = checked.value.asMap().entries; + Map data = Map.fromEntries(entries); + Map temp = {}; + data.forEach((key, value) { + temp[key.toString()] = value; + }); + + AbstractField.fieldsData.addAll( + {widget.parameters['id']: json.encode(temp)}); + }, + ), + Text(widget.checkValues?[index]) + ], + ); + }, + ), + ) + ], + ); + }, + ); + } +} + +class CustomTextFieldDynamic extends StatefulWidget { + final String? value; + final bool initController; + final dynamic id; + final String hintText; + final TextInputType? keyboardType; + final TextInputAction? action; + final List? formaters; + const CustomTextFieldDynamic({ + Key? key, + required this.initController, + required this.value, + this.id, + required this.hintText, + this.keyboardType, + this.action, + this.formaters, + }) : super(key: key); + + @override + State createState() => CustomTextFieldDynamicState(); +} + +class CustomTextFieldDynamicState extends State { + TextEditingController? _controller; + + @override + void initState() { + if (widget.initController) { + _controller = TextEditingController(text: widget.value); + } + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomTextFormField( + hintText: widget.hintText, + action: widget.action, + formaters: widget.formaters, + validator: CustomTextFieldValidator.nullCheck, + keyboard: widget.keyboardType, + controller: _controller, + onChange: (value) { + AbstractField.fieldsData.addAll({widget.id: value}); + }, + ); + } +} + +class CustomDropdownState extends StatefulWidget { + final ValueNotifier dropDownItemChange; + final Map parameters; + final List items; + CustomDropdownState( + {Key? key, + required this.dropDownItemChange, + required this.parameters, + required this.items}) + : super(key: key); + + @override + State createState() => _CustomDropdownStateState(); +} + +class _CustomDropdownStateState extends State + with AutomaticKeepAliveClientMixin { + List>? wid = []; + @override + void initState() { + wid = widget.items + .map((e) => DropdownMenuItem( + value: e, + child: Text( + e, + ), + )) + .toList(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + + return ValueListenableBuilder( + valueListenable: widget.dropDownItemChange, + builder: (context, value, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 48.rw(context), + height: 48.rh(context), + decoration: BoxDecoration( + color: context.color.tertiaryColor.withOpacity( + 0.1, + ), + borderRadius: BorderRadius.circular( + 10, + ), + ), + child: Container( + height: 24, + width: 24, + child: FittedBox( + fit: BoxFit.none, + child: UiUtils.imageType(widget.parameters['image'], + color: Constant.adaptThemeColorSvg + ? context.color.tertiaryColor + : null, + width: 24, + height: 24, + fit: BoxFit.cover), + ), + ), + ), + SizedBox( + width: 10.rw( + context, + ), + ), + Text( + widget.parameters['name'], + ) + .size( + context.font.large, + ) + .bold(weight: FontWeight.w500) + .color( + context.color.textColorDark, + ) + ], + ), + SizedBox( + height: 14.rh(context), + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 0, + ), + child: Container( + decoration: BoxDecoration( + color: context.color.secondaryColor, + borderRadius: BorderRadius.circular( + 10, + ), + border: Border.all( + width: 1.5, + color: context.color.borderColor, + )), + child: Padding( + padding: const EdgeInsets.all( + 8.0, + ), + child: SizedBox( + width: double.infinity, + child: DropdownButton( + value: value, + dropdownColor: context.color.secondaryColor, + isExpanded: true, + padding: const EdgeInsets.symmetric(vertical: 5), + icon: SvgPicture.asset(AppIcons.downArrow), + isDense: true, + borderRadius: BorderRadius.circular( + 10, + ), + style: TextStyle( + color: context.color.textLightColor, + fontSize: context.font.large), + underline: const SizedBox.shrink(), + items: wid, + onChanged: (dynamic v) { + widget.dropDownItemChange.value = v; + AbstractField.fieldsData.addAll( + { + widget.parameters['id']: v, + }, + ); + }, + ), + ), + ), + ), + ), + ], + ); + }); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/Ui/screens/widgets/Erros/no_data_found.dart b/lib/Ui/screens/widgets/Erros/no_data_found.dart new file mode 100644 index 0000000..93c38a5 --- /dev/null +++ b/lib/Ui/screens/widgets/Erros/no_data_found.dart @@ -0,0 +1,49 @@ +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +import '../../../../utils/AppIcon.dart'; + +class NoDataFound extends StatelessWidget { + final double? height; + final VoidCallback? onTap; + final String? title; + final String? description; + const NoDataFound( + {super.key, this.onTap, this.height, this.title, this.description}); + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + // height: height ?? 200, + child: SvgPicture.asset(AppIcons.no_data_found), + ), + SizedBox( + height: 20, + ), + Text(title ?? "nodatafound".translate(context)) + .size(context.font.extraLarge) + .color(context.color.tertiaryColor) + .bold(weight: FontWeight.w600), + const SizedBox( + height: 14, + ), + Text(description??"sorryLookingFor".translate(context)) + .size(context.font.larger) + .centerAlign(), + // Text(UiUtils.getTranslatedLabel(context, "nodatafound")), + // TextButton( + // onPressed: onTap, + // style: ButtonStyle( + // overlayColor: MaterialStateProperty.all( + // context.color.teritoryColor.withOpacity(0.2))), + // child: const Text("Retry").color(context.color.teritoryColor)) + ], + ), + ); + } +} diff --git a/lib/Ui/screens/widgets/Erros/no_internet.dart b/lib/Ui/screens/widgets/Erros/no_internet.dart new file mode 100644 index 0000000..612e637 --- /dev/null +++ b/lib/Ui/screens/widgets/Erros/no_internet.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; + +import '../../../../utils/AppIcon.dart'; +import '../../../../utils/Extensions/extensions.dart'; +import '../../../../utils/ui_utils.dart'; + +class NoInternet extends StatelessWidget { + final VoidCallback? onRetry; + const NoInternet({super.key, this.onRetry}); + + @override + Widget build(BuildContext context) { + return AnnotatedRegion( + value: UiUtils.getSystemUiOverlayStyle(context: context), + child: Scaffold( + backgroundColor: context.color.backgroundColor, + body: SizedBox( + height: context.screenHeight, + width: context.screenWidth, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + // width: 100, + // height: 150, + child: UiUtils.getSvg(AppIcons.no_internet)), + SizedBox( + height: 20, + ), + Text("noInternet".translate(context)) + .size(context.font.extraLarge) + .color(context.color.tertiaryColor) + .bold(weight: FontWeight.w600), + const SizedBox( + height: 10, + ), + SizedBox( + width: context.screenWidth * 0.8, + child: Text( + UiUtils.translate(context, "noInternetErrorMsg"), + textAlign: TextAlign.center, + )), + const SizedBox( + height: 5, + ), + TextButton( + onPressed: onRetry, + style: ButtonStyle( + overlayColor: MaterialStateProperty.all( + context.color.tertiaryColor.withOpacity(0.2))), + child: Text(UiUtils.translate(context, "retry")) + .color(context.color.tertiaryColor)) + ], + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/widgets/Erros/something_went_wrong.dart b/lib/Ui/screens/widgets/Erros/something_went_wrong.dart new file mode 100644 index 0000000..473a807 --- /dev/null +++ b/lib/Ui/screens/widgets/Erros/something_went_wrong.dart @@ -0,0 +1,95 @@ +import 'package:ebroker/utils/AppIcon.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class SomethingWentWrong extends StatelessWidget { + final FlutterErrorDetails? error; + const SomethingWentWrong({super.key, this.error}); + + static void asGlobalErrorBuilder() { + if (kReleaseMode) { + ErrorWidget.builder = + (FlutterErrorDetails flutterErrorDetails) => SomethingWentWrong( + error: flutterErrorDetails, + ); + } + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Center(child: SvgPicture.asset(AppIcons.somethingwentwrong)), + SizedBox( + height: 10, + ), + const Text("Something went wrong!").size(context.font.larger).bold(), + ], + ); + } +} + +class ErrorScreen extends StatelessWidget { + final StackTrace stack; + + const ErrorScreen({super.key, required this.stack}); + void _generateError(context) { + final filteredStackLines = stack.toString().split('\n').where((line) { + return !line.contains('package:flutter'); + }).map((line) { + final parts = line.split(' '); + return parts.length > 1 ? parts[1] : line; + }).toList(); + + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ErrorDetailScreen(stackLines: filteredStackLines), + ), + ); + } + + @override + Widget build(BuildContext context) { + return ElevatedButton( + onPressed: () { + _generateError(context); + }, + child: const Text('Generate Error'), + ); + } +} + +class ErrorDetailScreen extends StatelessWidget { + final List stackLines; + + const ErrorDetailScreen({super.key, required this.stackLines}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Filtered and Prettified Error Stack Trace'), + ), + body: ListView.builder( + itemCount: stackLines.length, + itemBuilder: (context, index) { + return ListTile( + title: Text(_formatStackTraceLine(stackLines[index])), + ); + }, + ), + ); + } +} + +String _formatStackTraceLine(String line) { + // Example format: "at Class.method (file.dart:42:23)" + final startIndex = line.indexOf('at ') + 3; + final endIndex = line.lastIndexOf('('); + return line.substring(startIndex, endIndex); +} diff --git a/lib/Ui/screens/widgets/adaptive_image_picker.dart b/lib/Ui/screens/widgets/adaptive_image_picker.dart new file mode 100644 index 0000000..464a553 --- /dev/null +++ b/lib/Ui/screens/widgets/adaptive_image_picker.dart @@ -0,0 +1,395 @@ +import 'dart:io'; + +import 'package:dotted_border/dotted_border.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/custom_validator.dart'; +import 'package:ebroker/utils/responsiveSize.dart'; +import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; + +import '../../../utils/ui_utils.dart'; +import '../proprties/AddProperyScreens/add_property_details.dart'; + +abstract class ImagePickerValue { + abstract final T value; +} + +class UrlValue extends ImagePickerValue { + @override + final String value; + final dynamic metaData; + UrlValue(this.value, [this.metaData]); +} + +class FileValue extends ImagePickerValue { + @override + final File value; + final FileSize? fileSize; + FileValue(this.value, this.fileSize); +} + +class IdentifyValue extends ImagePickerValue { + @override + dynamic value; + IdentifyValue(this.value) { + if (value is File) { + var file = value; + int fileSizeInBytes = file.lengthSync(); + + value = FileValue(file, formatFileSize(fileSizeInBytes)); + // value = FileValue( + // value, + // ); + } + if (value is String) { + value = UrlValue(value); + } + } +} + +class MultiValue extends ImagePickerValue { + List value; + MultiValue(this.value); +} + +class FileSize { + final double kb; + final double mb; + final double gb; + final int bytes; + + const FileSize({ + required this.bytes, + required this.kb, + required this.mb, + required this.gb, + }); + + @override + String toString() { + return 'FileSize{kb: $kb, mb: $mb, gb: $gb, bytes: $bytes}'; + } +} + +class ImageCount { + final int min; + final int max; + ImageCount(this.min, this.max); +} + +class AdaptiveImagePickerWidget extends StatefulWidget { + final String title; + final ImageCount? count; + final int? allowedSizeBytes; + final bool? isRequired; + final bool? multiImage; + final ImagePickerValue? value; + final void Function(dynamic value)? onRemove; + final void Function(ImagePickerValue? selected) onSelect; + + const AdaptiveImagePickerWidget( + {super.key, + this.value, + required this.onSelect, + required this.title, + this.multiImage, + this.onRemove, + this.isRequired, + this.count, + this.allowedSizeBytes}); + + @override + State createState() => + _AdaptiveImagePickerWidgetState(); +} + +class _AdaptiveImagePickerWidgetState extends State { + ImagePicker imagePicker = ImagePicker(); + + Widget currentWidget = Container(); + ImagePickerValue? imagePickedValue; + dynamic get(ImagePickerValue imagePickerValue) { + if (imagePickerValue is UrlValue) { + return Image.network( + imagePickerValue.value, + fit: BoxFit.cover, + ); + } + if (imagePickerValue is FileValue) { + return Image.file( + imagePickerValue.value, + fit: BoxFit.cover, + ); + } + if (imagePickedValue is IdentifyValue) { + return get(imagePickerValue); + } + } + + @override + void initState() { + if (widget.value != null) { + imagePickedValue = widget.value; + } + super.initState(); + } + + dynamic getProvider(imagePickedValue) { + if (imagePickedValue is FileValue) { + return FileImage(imagePickedValue.value); + } + if (imagePickedValue is UrlValue) { + return NetworkImage(imagePickedValue.value); + } + if (imagePickedValue is IdentifyValue) { + return getProvider(imagePickedValue); + } + } + + _onPick(FormFieldState state) async { + // _pickTitleImage.pick(pickMultiple: false); + // titleImageURL = ""; + + if (widget.multiImage == true) { + List list = await imagePicker.pickMultiImage(); + + List multiImages = list.map((e) { + var file = File(e.path); + int fileSizeInBytes = file.lengthSync(); + FileValue fv = FileValue(file, formatFileSize(fileSizeInBytes)); + return fv; + }).toList(); + + if (imagePickedValue == null) { + imagePickedValue = MultiValue(multiImages); + } else { + (imagePickedValue as MultiValue?)?.value.addAll(multiImages); + } + + state.didChange(imagePickedValue); + + widget.onSelect(imagePickedValue! as MultiValue); + setState(() {}); + return; + } + XFile? xFile = await imagePicker.pickImage(source: ImageSource.gallery); + + if (xFile != null) { + var file = File(xFile.path); + int fileSizeInBytes = file.lengthSync(); + imagePickedValue = FileValue(file, formatFileSize(fileSizeInBytes)); + state.didChange(imagePickedValue); + widget.onSelect(imagePickedValue! as FileValue); + } + + setState(() {}); + } + + _onRemove(dynamic value, FormFieldState state) { + if (widget.multiImage == true) { + if (imagePickedValue is MultiValue) { + (imagePickedValue as MultiValue).value.remove(value); + widget.onRemove?.call(value); + } + + widget.onSelect(imagePickedValue); + } else { + imagePickedValue = null; + state.didChange(null); + widget.onRemove?.call(null); + + widget.onSelect(null); + } + setState(() {}); + } + + @override + Widget build(BuildContext context) { + if (imagePickedValue is MultiValue) {} + if (imagePickedValue != null) { + currentWidget = GestureDetector( + onTap: () { + UiUtils.showFullScreenImage(context, + provider: getProvider(imagePickedValue)); + }, + child: Column( + children: [ + Container( + width: 100, + height: 100, + margin: const EdgeInsets.all(5), + clipBehavior: Clip.antiAlias, + decoration: + BoxDecoration(borderRadius: BorderRadius.circular(10)), + child: get(imagePickedValue!)), + ], + ), + ); + } else { + currentWidget = Container(); + } + return CustomValidator( + initialValue: widget.value, + validator: (value) { + if (widget.isRequired == true) { + if (value == null) { + return "Please pick image"; + } + if (value is MultiValue) { + if (value.value.isEmpty) { + return "Please pick image"; + } + } + + if (value is FileValue) { + if (widget.allowedSizeBytes != null && + value.fileSize!.bytes > widget.allowedSizeBytes!) { + var size = formatFileSize(widget.allowedSizeBytes!); + return "Max ${size.kb ~/ 1}KB your file size: ${value.fileSize!.kb ~/ 1}KB"; + } + } + if (widget.count != null && + widget.multiImage == true && + widget.isRequired == true) { + if (imagePickedValue is MultiValue) { + int images = (imagePickedValue as MultiValue).value.length; + if (widget.count?.min != null && images < widget.count!.min) { + return "Minimum ${widget.count!.min} images required"; + } + + if (widget.count?.max != null && images > widget.count!.max) { + return "Maximum ${widget.count!.max} images are allowed"; + } + } + } + } + + return null; + }, + builder: (state) { + return Wrap( + children: [ + if (imagePickedValue == null) + DottedBorder( + color: state.hasError + ? context.color.error + : context.color.textLightColor, + borderType: BorderType.RRect, + radius: const Radius.circular(12), + child: GestureDetector( + onTap: () { + _onPick(state); + }, + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10)), + alignment: Alignment.center, + height: 48.rh(context), + child: Text(widget.title), + ), + ), + ), + // if (state.hasError) + // Padding( + // padding: const EdgeInsets.symmetric(horizontal: 4), + // child: Text(state.errorText!) + // .color(context.color.error) + // .size(context.font.small), + // ), + if (imagePickedValue is! MultiValue) + Stack( + children: [ + currentWidget, + closeButton(context, () { + _onRemove(null, state); + }) + ], + ), + if (imagePickedValue is MultiValue) ...{ + ...(imagePickedValue! as MultiValue) + .value + .map((ImagePickerValue impvalue) { + return Stack( + children: [ + GestureDetector( + onTap: () { + UiUtils.showFullScreenImage(context, + provider: getProvider(impvalue)); + }, + child: Column( + children: [ + Container( + width: 100, + height: 100, + margin: const EdgeInsets.all(5), + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10)), + child: get(impvalue!)), + ], + ), + ), + closeButton(context, () { + _onRemove(impvalue, state); + }) + ], + ); + }) + }, + if (imagePickedValue != null) + uploadPhotoCard(context, onTap: () { + _onPick(state); + }) + // GestureDetector( + // onTap: () { + // _pickTitleImage.resumeSubscription(); + // _pickTitleImage.pick(pickMultiple: false); + // _pickTitleImage.pauseSubscription(); + // titleImageURL = ""; + // setState(() {}); + // }, + // child: Container( + // width: 100, + // height: 100, + // margin: const EdgeInsets.all(5), + // clipBehavior: Clip.antiAlias, + // decoration: + // BoxDecoration(borderRadius: BorderRadius.circular(10)), + // child: DottedBorder( + // borderType: BorderType.RRect, + // radius: Radius.circular(10), + // child: Container( + // alignment: Alignment.center, + // child: Text("Upload \n Photo"), + // )), + // ), + // ), + , + Row( + children: [ + if (state.hasError) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Text(state.errorText!) + .color(context.color.error) + .size(context.font.small), + ), + ], + ) + ], + ); + }); + } +} + +FileSize formatFileSize(int fileSizeInBytes) { + const int KB = 1024; + const int MB = 1024 * KB; + const int GB = 1024 * MB; + return FileSize( + bytes: fileSizeInBytes, + mb: fileSizeInBytes / MB, + gb: fileSizeInBytes / GB, + kb: fileSizeInBytes / KB); +} diff --git a/lib/Ui/screens/widgets/all_gallary_image.dart b/lib/Ui/screens/widgets/all_gallary_image.dart new file mode 100644 index 0000000..8f1b040 --- /dev/null +++ b/lib/Ui/screens/widgets/all_gallary_image.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; + +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; +import 'video_view_screen.dart'; + +class AllGallaryImages extends StatelessWidget { + final List images; + final String? youtubeThumbnail; + const AllGallaryImages( + {super.key, required this.images, this.youtubeThumbnail}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.backgroundColor, + appBar: UiUtils.buildAppBar( + context, + showBackButton: true, + ), + body: GridView.builder( + itemCount: images.length, + padding: const EdgeInsets.all(16), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, crossAxisSpacing: 5, mainAxisSpacing: 5), + itemBuilder: (context, index) { + return ClipRRect( + borderRadius: BorderRadius.circular(18), + child: GestureDetector( + onTap: () { + if (images[index].isVideo == true) { + Navigator.push(context, MaterialPageRoute( + builder: (context) { + return VideoViewScreen(videoUrl: images[index].image); + }, + )); + } else { + var stringImages = images.map((e) => e.imageUrl).toList(); + UiUtils.imageGallaryView( + context, + images: stringImages, + initalIndex: index, + then: () {}, + ); + } + }, + child: SizedBox( + width: 76.rw(context), + height: 76.rh(context), + child: images[index].isVideo == true + ? Stack( + fit: StackFit.expand, + children: [ + UiUtils.getImage(youtubeThumbnail!, + fit: BoxFit.cover), + const Icon( + Icons.play_arrow, + size: 28, + ) + ], + ) + : UiUtils.getImage(images[index].imageUrl ?? "", + fit: BoxFit.cover), + ), + )); + }, + ), + ); + } +} diff --git a/lib/Ui/screens/widgets/blurred_dialoge_box.dart b/lib/Ui/screens/widgets/blurred_dialoge_box.dart new file mode 100644 index 0000000..79cb01b --- /dev/null +++ b/lib/Ui/screens/widgets/blurred_dialoge_box.dart @@ -0,0 +1,449 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/responsiveSize.dart'; +import '../../../utils/ui_utils.dart'; + +mixin BlurDialoge {} + +///This dialoge box will blur background of screen +///This is normaly a screen which blurs its background we don't show builtin dialog box here instead we push to new route and show container in middle of screen +class BlurredDialogBox extends StatelessWidget implements BlurDialoge { + final String? cancelButtonName; + final String? acceptButtonName; + final VoidCallback? onCancel; + final String? svgImagePath; + final Color? svgImageColor; + final Future Function()? onAccept; + final String title; + final Widget content; + final Color? cancelButtonColor; + final Color? cancelTextColor; + final Color? acceptButtonColor; + final Color? acceptTextColor; + final bool? backAllowedButton; + final bool? showCancleButton; + final bool? barrierDismissable; + final bool? isAcceptContainesPush; + const BlurredDialogBox({ + super.key, + this.cancelButtonName, + this.acceptButtonName, + this.onCancel, + this.onAccept, + required this.title, + required this.content, + this.cancelButtonColor, + this.cancelTextColor, + this.acceptButtonColor, + this.acceptTextColor, + this.backAllowedButton, + this.showCancleButton, + this.svgImagePath, + this.svgImageColor, + this.barrierDismissable, + this.isAcceptContainesPush, + }); + + @override + Widget build(BuildContext context) { + ///This backAllowedButton will help us to prevent back presses from sensitive dialoges + return AnnotatedRegion( + value: SystemUiOverlayStyle( + systemNavigationBarDividerColor: Colors.transparent, + statusBarColor: Colors.black.withOpacity(0)), + child: Stack( + children: [ + //Make dialoge box's background lighter black + GestureDetector( + onTap: () { + if (barrierDismissable ?? false) { + Navigator.pop(context); + } + }, + child: Container( + color: Colors.black.withOpacity(0.14), + ), + ), + WillPopScope( + onWillPop: () async { + if (backAllowedButton == false) { + return false; + } + return true; + }, + child: LayoutBuilder(builder: (context, constraints) { + return AlertDialog( + backgroundColor: context.color.secondaryColor ?? + makeColorDark( + context.color.primaryColor, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15)), + title: Column( + children: [ + if (svgImagePath != null) ...[ + CircleAvatar( + radius: 186 / 2, + backgroundColor: + context.color.tertiaryColor.withOpacity(0.1), + child: SizedBox( + // width: 87 / 2, + // height: 87 / 2, + child: UiUtils.getSvg( + svgImagePath!, + color: svgImageColor, + )), + ), + const SizedBox( + height: 20, + ), + ], + Text(title.firstUpperCase(), textAlign: TextAlign.center), + ], + ), + content: content, + actionsOverflowAlignment: OverflowBarAlignment.center, + actionsAlignment: MainAxisAlignment.center, + actions: [ + if (showCancleButton ?? true) ...[ + button( + context, + constraints: constraints, + buttonColor: + cancelButtonColor ?? context.color.primaryColor, + buttonName: cancelButtonName ?? + UiUtils.translate(context, "cancelBtnLbl"), + textColor: cancelTextColor ?? context.color.textColorDark, + onTap: () { + onCancel?.call(); + Navigator.pop(context, false); + }, + ), + + // const Spacer(), + ], + Builder(builder: (context) { + if (showCancleButton == false) { + return Center( + child: SizedBox( + width: context.screenWidth / 2, + child: button( + context, + constraints: constraints, + buttonColor: acceptButtonColor ?? + context.color.tertiaryColor, + buttonName: acceptButtonName ?? + UiUtils.translate(context, "ok"), + textColor: + acceptTextColor ?? context.color.textColorDark, + onTap: () async { + await onAccept?.call(); + + if (isAcceptContainesPush == false || + isAcceptContainesPush == null) { + Future.delayed( + Duration.zero, + () { + Navigator.pop(context, true); + }, + ); + } + }, + ), + ), + ); + } + return button( + context, + constraints: constraints, + buttonColor: + acceptButtonColor ?? context.color.tertiaryColor, + buttonName: + acceptButtonName ?? UiUtils.translate(context, "ok"), + textColor: acceptTextColor ?? + const Color.fromARGB(255, 255, 255, 255), + onTap: () async { + await onAccept?.call(); + if (isAcceptContainesPush == false || + isAcceptContainesPush == null) { + Future.delayed( + Duration.zero, + () { + Navigator.pop(context, true); + }, + ); + } + }, + ); + }), + ], + ); + }), + ), + ], + ), + ); + } + + Color makeColorDark(Color color) { + Color color0 = color; + + int red = color0.red - 10; + int green = color0.green - 10; + int blue = color0.blue - 10; + + return Color.fromARGB(color0.alpha, red.clamp(0, 255), green.clamp(0, 255), + blue.clamp(0, 255)); + } + + Widget button(BuildContext context, + {required BoxConstraints constraints, + required Color buttonColor, + required String buttonName, + required Color textColor, + required VoidCallback onTap}) { + return SizedBox( + width: (constraints.maxWidth / 3), + child: MaterialButton( + elevation: 0, + + height: 39.rh(context), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + side: BorderSide(color: context.color.borderColor)), + color: buttonColor, + // minWidth: (constraints.maxWidth / 2) - 10, + + onPressed: onTap, + child: Text(buttonName).color(textColor), + ), + ); + } +} + +///This dialoge box will blur background of screen +///This is normaly a screen which blurs its background we don't show builtin dialog box here instead we push to new route and show container in middle of screen +class BlurredDialogBuilderBox extends StatelessWidget implements BlurDialoge { + final String? cancelButtonName; + final String? acceptButtonName; + final VoidCallback? onCancel; + final String? svgImagePath; + final Color? svgImageColor; + final Future Function()? onAccept; + final String title; + final Widget? Function(BuildContext context, BoxConstraints constrains) + contentBuilder; + final Color? cancelButtonColor; + final Color? cancelTextColor; + final Color? acceptButtonColor; + final Color? acceptTextColor; + final bool? backAllowedButton; + final bool? showCancleButton; + final bool? isAcceptContainesPush; + const BlurredDialogBuilderBox({ + super.key, + this.cancelButtonName, + this.acceptButtonName, + this.onCancel, + this.onAccept, + required this.title, + required this.contentBuilder, + this.cancelButtonColor, + this.cancelTextColor, + this.acceptButtonColor, + this.acceptTextColor, + this.backAllowedButton, + this.showCancleButton, + this.svgImagePath, + this.svgImageColor, + this.isAcceptContainesPush, + }); + + @override + Widget build(BuildContext context) { + ///This backAllowedButton will help us to prevent back presses from sensitive dialoges + return AnnotatedRegion( + value: SystemUiOverlayStyle( + systemNavigationBarDividerColor: Colors.transparent, + statusBarColor: Colors.black.withOpacity(0)), + child: Stack( + children: [ + //Make dialoge box's background lighter black + Container( + color: Colors.black.withOpacity(0.14), + ), + WillPopScope( + onWillPop: () async { + if (backAllowedButton == false) { + return false; + } + return true; + }, + child: LayoutBuilder(builder: (context, constraints) { + return AlertDialog( + backgroundColor: makeColorDark(context.color.primaryColor), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15)), + title: Column( + children: [ + if (svgImagePath != null) ...[ + CircleAvatar( + radius: 98 / 2, + backgroundColor: + context.color.tertiaryColor.withOpacity(0.1), + child: SizedBox( + width: 87 / 2, + height: 87 / 2, + child: UiUtils.getSvg(svgImagePath!, + color: svgImageColor)), + ), + const SizedBox( + height: 20, + ), + ], + Text(title.firstUpperCase(), textAlign: TextAlign.center), + ], + ), + content: contentBuilder.call(context, constraints), + actionsOverflowAlignment: OverflowBarAlignment.center, + actionsAlignment: MainAxisAlignment.center, + actions: [ + if (showCancleButton ?? true) ...[ + button( + context, + constraints: constraints, + buttonColor: cancelButtonColor ?? + context.color.tertiaryColor.withOpacity(.10), + buttonName: cancelButtonName ?? + UiUtils.translate(context, "cancelBtnLbl"), + textColor: cancelTextColor ?? context.color.textColorDark, + onTap: () { + onCancel?.call(); + Navigator.pop(context, false); + }, + ), + + // const Spacer(), + ], + Builder(builder: (context) { + if (showCancleButton == false) { + return Center( + child: SizedBox( + width: context.screenWidth / 2, + child: button( + context, + constraints: constraints, + buttonColor: acceptButtonColor ?? + context.color.tertiaryColor, + buttonName: acceptButtonName ?? + UiUtils.translate(context, "ok"), + textColor: + acceptTextColor ?? context.color.textColorDark, + onTap: () async { + await onAccept?.call(); + + if (isAcceptContainesPush == false || + isAcceptContainesPush == null) { + Future.delayed( + Duration.zero, + () { + Navigator.pop(context, true); + }, + ); + } + }, + ), + ), + ); + } + return button( + context, + constraints: constraints, + buttonColor: + acceptButtonColor ?? context.color.tertiaryColor, + buttonName: + acceptButtonName ?? UiUtils.translate(context, "ok"), + textColor: acceptTextColor ?? + const Color.fromARGB(255, 255, 255, 255), + onTap: () async { + await onAccept?.call(); + if (isAcceptContainesPush == false || + isAcceptContainesPush == null) { + Future.delayed( + Duration.zero, + () { + Navigator.pop(context, true); + }, + ); + } + }, + ); + }), + ], + ); + }), + ), + ], + ), + ); + } + + Color makeColorDark(Color color) { + Color color0 = color; + + int red = color0.red - 10; + int green = color0.green - 10; + int blue = color0.blue - 10; + + return Color.fromARGB(color0.alpha, red.clamp(0, 255), green.clamp(0, 255), + blue.clamp(0, 255)); + } + + Widget button(BuildContext context, + {required BoxConstraints constraints, + required Color buttonColor, + required String buttonName, + required Color textColor, + required VoidCallback onTap}) { + return SizedBox( + width: (constraints.maxWidth / 3), + child: MaterialButton( + elevation: 0, + height: 39.rh(context), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), + color: buttonColor, + // minWidth: (constraints.maxWidth / 2) - 10, + + onPressed: onTap, + child: Text(buttonName).color(textColor), + ), + ); + } +} + +class EmptyDialogBox extends StatelessWidget with BlurDialoge { + final Widget child; + final bool? barrierDismisable; + const EmptyDialogBox({Key? key, required this.child, this.barrierDismisable}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Stack( + children: [ + GestureDetector( + onTap: () { + if (barrierDismisable ?? true) Navigator.pop(context); + }, + child: Container( + color: Colors.black.withOpacity(0.3), + ), + ), + Center(child: child), + ], + )); + } +} diff --git a/lib/Ui/screens/widgets/custom_inkWell.dart b/lib/Ui/screens/widgets/custom_inkWell.dart new file mode 100644 index 0000000..a804be9 --- /dev/null +++ b/lib/Ui/screens/widgets/custom_inkWell.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +class CustomInkWell extends StatelessWidget { + final Color? color; + final Widget child; + final BorderRadius? borderRadius; + final BoxShape? shape; + final void Function() onTap; + const CustomInkWell( + {super.key, + this.color, + required this.child, + required this.onTap, + this.borderRadius, + this.shape}); + + @override + Widget build(BuildContext context) { + return Center( + child: Container( + clipBehavior: Clip.antiAlias, + // color: color, + decoration: BoxDecoration( + color: color, + borderRadius: borderRadius, + shape: shape ?? BoxShape.rectangle), + child: Material( + clipBehavior: Clip.antiAlias, + color: Colors.transparent, + child: InkWell( + onTap: onTap, + child: child, + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/widgets/custom_text_form_field.dart b/lib/Ui/screens/widgets/custom_text_form_field.dart new file mode 100644 index 0000000..e6ec89b --- /dev/null +++ b/lib/Ui/screens/widgets/custom_text_form_field.dart @@ -0,0 +1,118 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/validator.dart'; + +enum CustomTextFieldValidator { + nullCheck, + phoneNumber, + email, + password, + maxFifty, + link +} + +class CustomTextFormField extends StatelessWidget { + final String? hintText; + final TextEditingController? controller; + final int? minLine; + final int? maxLine; + final AutovalidateMode? autovalidate; + final bool? isReadOnly; + final List? formaters; + final CustomTextFieldValidator? validator; + final Color? fillColor; + final Function(dynamic value)? onChange; + final Widget? prefix; + final TextInputAction? action; + final TextInputType? keyboard; + final Widget? suffix; + final bool? dense; + const CustomTextFormField({ + Key? key, + this.hintText, + this.controller, + this.minLine, + this.maxLine, + this.formaters, + this.isReadOnly, + this.validator, + this.fillColor, + this.onChange, + this.prefix, + this.keyboard, + this.action, + this.suffix, + this.dense, + this.autovalidate, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return TextFormField( + controller: controller, + autovalidateMode: autovalidate, + inputFormatters: formaters, + textInputAction: action, + keyboardAppearance: Brightness.light, + readOnly: isReadOnly ?? false, + style: TextStyle(fontSize: context.font.large), + minLines: minLine ?? 1, + maxLines: maxLine ?? 1, + onChanged: onChange, + validator: (String? value) { + if (validator == CustomTextFieldValidator.nullCheck) { + return Validator.nullCheckValidator(value); + } + if (validator == CustomTextFieldValidator.link) { + if (value?.isNotEmpty ?? false) { + return Validator.validateUrl(value!); + } else { + return null; + } + } + if (validator == CustomTextFieldValidator.maxFifty) { + if ((value ??= "").length > 50) { + return "You can enter 50 letters max"; + } else { + return null; + } + } + if (validator == CustomTextFieldValidator.email) { + return Validator.validateEmail(value); + } + if (validator == CustomTextFieldValidator.phoneNumber) { + return Validator.validatePhoneNumber(value); + } + if (validator == CustomTextFieldValidator.password) { + return Validator.validatePassword(value); + } + return null; + }, + keyboardType: keyboard, + decoration: InputDecoration( + prefix: prefix, + isDense: dense, + suffixIcon: suffix, + hintText: hintText, + hintStyle: TextStyle( + color: context.color.textColorDark.withOpacity(0.7), + fontSize: context.font.large), + filled: true, + fillColor: fillColor ?? context.color.secondaryColor, + focusedBorder: OutlineInputBorder( + borderSide: + BorderSide(width: 1.5, color: context.color.tertiaryColor), + borderRadius: BorderRadius.circular(10)), + enabledBorder: OutlineInputBorder( + borderSide: + BorderSide(width: 1.5, color: context.color.borderColor), + borderRadius: BorderRadius.circular(10)), + border: OutlineInputBorder( + borderSide: + BorderSide(width: 1.5, color: context.color.borderColor), + borderRadius: BorderRadius.circular(10))), + ); + } +} diff --git a/lib/Ui/screens/widgets/full_screen_image_view.dart b/lib/Ui/screens/widgets/full_screen_image_view.dart new file mode 100644 index 0000000..7174e1f --- /dev/null +++ b/lib/Ui/screens/widgets/full_screen_image_view.dart @@ -0,0 +1,197 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:ebroker/app/app.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:open_filex/open_filex.dart'; +import 'package:path_provider/path_provider.dart'; + +import '../../../app/default_app_setting.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/ui_utils.dart'; + +class FullScreenImageView extends StatefulWidget { + final ImageProvider provider; + final bool? showDownloadButton; + final VoidCallback? onTapDownload; + const FullScreenImageView({ + super.key, + required this.provider, + this.showDownloadButton, + this.onTapDownload, + }); + + @override + State createState() => _FullScreenImageViewState(); +} + +class _FullScreenImageViewState extends State { + String getExtentionOfFile() { + if (widget.provider is NetworkImage) { + return (widget.provider as NetworkImage) + .getURL() + .toString() + .split(".") + .last; + } + return ""; + } + + String getFileName() { + if (widget.provider is NetworkImage) { + return (widget.provider as NetworkImage) + .getURL() + .toString() + .split(".") + .last; + } + return (widget.provider as NetworkImage) + .getURL() + .toString() + .split("/") + .last; + + return ""; + } + + Future downloadFile() async { + try { + // if (!(await Permission.storage.isGranted)) { + // log("PERMISSION ISS ${await Permission.storage.status}"); + // HelperUtils.showSnackBarMessage( + // context, "Please give storage permission"); + // + // return; + // } + + String? downloadPath = await getDownloadPath(); + if (widget.provider is! NetworkImage) { + return; + } + + await Dio().download( + (widget.provider as NetworkImage).getURL().toString(), + "${downloadPath!}/${getFileName()}", + onReceiveProgress: (int count, int total) async { + var persontage = (count) / total; + + if (persontage == 1) { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "fileSavedIn"), + type: MessageType.success); + + await OpenFilex.open("$downloadPath/${getFileName()}"); + } + setState(() {}); + }, + ); + } catch (e) { + print("Download Error is: $e"); + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "errorFileSave"), + type: MessageType.success); + } + } + + Future getDownloadPath() async { + Directory? directory; + try { + if (Platform.isIOS) { + directory = await getApplicationDocumentsDirectory(); + } else { + directory = Directory('/storage/emulated/0/Download'); + // Put file in global download folder, if for an unknown reason it didn't exist, we fallback + // ignore: avoid_slow_async_io + if (!await directory.exists()) { + directory = await getExternalStorageDirectory(); + } + } + } catch (err) { + if (kDebugMode) { + HelperUtils.showSnackBarMessage( + context, UiUtils.translate(context, "fileNotSaved"), + type: MessageType.success); + } + } + return directory?.path; + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark); + + Navigator.pop(context); + }, + child: SafeArea( + child: Scaffold( + extendBodyBehindAppBar: true, + appBar: AppBar( + elevation: 0, + actions: [ + if (widget.showDownloadButton == true && + widget.provider is NetworkImage) + IconButton( + onPressed: () { + downloadFile(); + widget.onTapDownload?.call(); + }, + icon: Icon(Icons.download)), + const SizedBox( + width: 10, + ) + ], + backgroundColor: Colors.transparent, + iconTheme: IconThemeData(color: context.color.tertiaryColor), + ), + backgroundColor: const Color.fromARGB(17, 0, 0, 0), + body: InteractiveViewer( + maxScale: 4, + child: Center( + child: AspectRatio( + aspectRatio: 1 / 1, + child: GestureDetector( + onTap: () {}, + child: Image( + image: widget.provider, + errorBuilder: (context, error, stackTrace) { + return Container( + width: 100, + height: 100, + decoration: BoxDecoration( + color: + context.color.tertiaryColor.withOpacity(0.2), + borderRadius: BorderRadius.circular(10)), + child: LoadAppSettings().svg( + appSettings.placeholderLogo!, + color: context.color.tertiaryColor)); + }, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) return child; + + return FittedBox( + fit: BoxFit.none, + child: SizedBox( + width: 50, height: 50, child: UiUtils.progress()), + ); + }, + ), + ), + ), + ), + ), + ), + ), + ); + } +} + +extension S on NetworkImage { + String getURL() { + return this.url; + } +} diff --git a/lib/Ui/screens/widgets/gallery_view.dart b/lib/Ui/screens/widgets/gallery_view.dart new file mode 100644 index 0000000..0c277d5 --- /dev/null +++ b/lib/Ui/screens/widgets/gallery_view.dart @@ -0,0 +1,80 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; + +import '../../../utils/AdMob/interstitialAdManager.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/ui_utils.dart'; + +class GalleryViewWidget extends StatefulWidget { + final List images; + final int initalIndex; + const GalleryViewWidget({ + super.key, + required this.images, + required this.initalIndex, + }); + + @override + State createState() => _GalleryViewWidgetState(); +} + +class _GalleryViewWidgetState extends State { + List images = []; + late PageController controller = + PageController(initialPage: widget.initalIndex); + late int page = widget.initalIndex; + InterstitialAdManager admanager = InterstitialAdManager(); + + @override + void initState() { + images = List.from(widget.images); + admanager.load(); + super.initState(); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + extendBodyBehindAppBar: true, + appBar: AppBar( + elevation: 0, + backgroundColor: Colors.transparent, + iconTheme: IconThemeData(color: context.color.tertiaryColor), + ), + backgroundColor: const Color.fromARGB(17, 0, 0, 0), + body: ScrollConfiguration( + behavior: RemoveGlow(), + child: PageView.builder( + controller: controller, + onPageChanged: (value) async { + page = value; + if (page % 2 == 0) { + await admanager.show(); + } + setState(() {}); + }, + itemBuilder: (context, index) { + return InteractiveViewer( + // panEnabled: true, + scaleEnabled: true, + maxScale: 5, + child: CachedNetworkImage( + imageUrl: images[index], + ), + ); + }, + itemCount: + (images..removeWhere((element) => (element == ""))).length, + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/widgets/gradient_image_shadow.dart b/lib/Ui/screens/widgets/gradient_image_shadow.dart new file mode 100644 index 0000000..e80510e --- /dev/null +++ b/lib/Ui/screens/widgets/gradient_image_shadow.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:palette_generator/palette_generator.dart'; + +class GradientedShadowImage extends StatefulWidget { + final String imageUrl; + + GradientedShadowImage({required this.imageUrl}); + + @override + _GradientedShadowImageState createState() => _GradientedShadowImageState(); +} + +class _GradientedShadowImageState extends State { + PaletteGenerator? _paletteGenerator; + + @override + void initState() { + super.initState(); + _loadPalette(); + } + + Future _loadPalette() async { + final imageProvider = NetworkImage(widget.imageUrl); + _paletteGenerator = await PaletteGenerator.fromImageProvider(imageProvider); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: _paletteGenerator?.dominantColor?.color.withOpacity(0.4) ?? + Colors.transparent, + blurRadius: 8.0, + spreadRadius: 6.0, + ), + ], + gradient: LinearGradient( + colors: [ + _paletteGenerator?.dominantColor?.color ?? Colors.transparent, + Colors.transparent, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Image.network(widget.imageUrl,width: 200,height: 200,), + ); + } +} diff --git a/lib/Ui/screens/widgets/image_cropper.dart b/lib/Ui/screens/widgets/image_cropper.dart new file mode 100644 index 0000000..33d6c1e --- /dev/null +++ b/lib/Ui/screens/widgets/image_cropper.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:image_cropper/image_cropper.dart'; + +import '../../../utils/Extensions/extensions.dart'; + +//This will open image crop SDK +class CropImage { + static BuildContext? _context; + + static void init(BuildContext context) { + _context = context; + } + + static Future? crop({required String filePath}) async { + if (_context == null) { + return null; + } + + CroppedFile? croppedFile = await ImageCropper().cropImage( + sourcePath: filePath, + aspectRatioPresets: [ + CropAspectRatioPreset.square, + ], + uiSettings: [ + AndroidUiSettings( + toolbarTitle: 'Cropper', + toolbarColor: _context!.color.tertiaryColor, + toolbarWidgetColor: Colors.white, + hideBottomControls: false, + activeControlsWidgetColor: _context!.color.tertiaryColor, + lockAspectRatio: true), + IOSUiSettings( + title: 'Cropper', + ), + WebUiSettings( + context: _context!, + ), + ], + ); + + return croppedFile; + } +} diff --git a/lib/Ui/screens/widgets/like_button_widget.dart b/lib/Ui/screens/widgets/like_button_widget.dart new file mode 100644 index 0000000..d69f9c2 --- /dev/null +++ b/lib/Ui/screens/widgets/like_button_widget.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../data/cubits/Utility/like_properties.dart'; +import '../../../data/cubits/favorite/add_to_favorite_cubit.dart'; +import '../../../data/model/property_model.dart'; +import '../../../utils/AppIcon.dart'; +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/guestChecker.dart'; +import '../../../utils/ui_utils.dart'; + +//This like button is used in app for favorite feature, it is used in all propery so it is very important +class LikeButtonWidget extends StatefulWidget { + final PropertyModel property; + final Function(FavoriteType type)? onLikeChanged; + final Function(AddToFavoriteCubitState state)? onStateChange; + const LikeButtonWidget({ + super.key, + required this.property, + this.onStateChange, + this.onLikeChanged, + }); + + @override + State createState() => _LikeButtonWidgetState(); +} + +class _LikeButtonWidgetState extends State { + @override + void initState() { + //checking is property is already favorite , it will come in api + if (GuestChecker.value != true) { + if (widget.property.isFavourite == 1 && + context + .read() + .state + .liked + .contains(widget.property.id) == + false) { + if (!context + .read() + .getRemovedLikes()! + .contains(widget.property.id)) { + context.read().add(widget.property.id); + } + } + } + + super.initState(); + } + +//this is main like button method + Widget setFavorite(PropertyModel property, BuildContext context) { + return BlocConsumer( + listener: (BuildContext context, AddToFavoriteCubitState state) { + widget.onStateChange?.call(state); + if (state is AddToFavoriteCubitFailure) {} + if (state is AddToFavoriteCubitSuccess) { + //callback + widget.onLikeChanged?.call(state.favorite); + + /// if it is already added then we'll add remove , other wise we'll add it into local list + context.read().changeLike(state.id); + } + }, + builder: (BuildContext context, AddToFavoriteCubitState addState) { + return GestureDetector( + onTap: () { + GuestChecker.check(onNotGuest: () { + ///checking if added then remove or else add it + FavoriteType favoriteType; + + bool contains = context + .read() + .state + .liked + .contains(property.id!); + + if (contains == true || property.isFavourite == 1) { + favoriteType = FavoriteType.remove; + } else { + favoriteType = FavoriteType.add; + } + context.read().setFavroite( + propertyId: property.id!, + type: favoriteType, + ); + }); + }, + child: Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: context.color.primaryColor, + shape: BoxShape.circle, + boxShadow: const [ + BoxShadow( + color: Color.fromARGB(33, 0, 0, 0), + offset: Offset(0, 2), + blurRadius: 15, + spreadRadius: 0) + ], + ), + child: BlocBuilder( + builder: (context, state) { + return Center( + child: (addState is AddToFavoriteCubitInProgress) + ? UiUtils.progress(width: 20, height: 20) + : state.liked.contains(widget.property.id) + ? UiUtils.getSvg( + AppIcons.like_fill, + color: context.color.tertiaryColor, + ) + : UiUtils.getSvg(AppIcons.like, + color: context.color.tertiaryColor)); + }, + ), + ), + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return setFavorite(widget.property, context); + } +} diff --git a/lib/Ui/screens/widgets/maintenance_mode.dart b/lib/Ui/screens/widgets/maintenance_mode.dart new file mode 100644 index 0000000..4f1f992 --- /dev/null +++ b/lib/Ui/screens/widgets/maintenance_mode.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:lottie/lottie.dart'; + +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/constant.dart'; +import '../../../utils/ui_utils.dart'; +import 'AnimatedRoutes/blur_page_route.dart'; + +class MaintenanceMode extends StatelessWidget { + const MaintenanceMode({super.key}); + static Route route(RouteSettings settings) { + return BlurredRouter( + builder: (context) { + return const MaintenanceMode(); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.color.primaryColor, + body: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Lottie.asset( + "assets/lottie/${Constant.maintenanceModeLottieFile}", + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text(UiUtils.translate(context, "maintenanceModeMessage"), + textAlign: TextAlign.center) + .color(context.color.textColorDark), + ) + ], + ), + ); + } +} diff --git a/lib/Ui/screens/widgets/my_maps.dart b/lib/Ui/screens/widgets/my_maps.dart new file mode 100644 index 0000000..d114392 --- /dev/null +++ b/lib/Ui/screens/widgets/my_maps.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart'; + +class MyMaps extends StatelessWidget { + final String latitudeString; + final String longitudeString; + const MyMaps({ + Key? key, + required this.latitudeString, + required this.longitudeString, + }) : super(key: key); + @override + Widget build(BuildContext context) { + final LatLng location = LatLng( + double.parse(latitudeString), + double.parse(longitudeString) + ); + return SafeArea( + child: Scaffold( + body: FlutterMap( + options: MapOptions( + center: location, + zoom: 17.0 + ), + children: [ + TileLayer( + urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + userAgentPackageName: 'com.rumahjo.vds', + ), + MarkerLayer( + markers: [ + Marker( + width: 45.0, + height: 45.0, + point: location, + builder: (ctx) => + Container( + child: const Icon( + Icons.location_on, + size: 30.0, + color: Colors.red, + ), + ), + ) + ] + ) + ] + ) + ) + ); + } +} + + diff --git a/lib/Ui/screens/widgets/panaroma_image_view.dart b/lib/Ui/screens/widgets/panaroma_image_view.dart new file mode 100644 index 0000000..bf87008 --- /dev/null +++ b/lib/Ui/screens/widgets/panaroma_image_view.dart @@ -0,0 +1,38 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:panorama/panorama.dart'; + +import '../../../utils/Extensions/extensions.dart'; + +class PanaromaImageScreen extends StatelessWidget { + final String imageUrl; + final bool? isFileImage; + const PanaromaImageScreen( + {super.key, required this.imageUrl, this.isFileImage}); + + @override + Widget build(BuildContext context) { + return Scaffold( + extendBodyBehindAppBar: true, + appBar: AppBar( + elevation: 0, + backgroundColor: Colors.transparent, + iconTheme: IconThemeData(color: context.color.tertiaryColor), + ), + backgroundColor: Colors.transparent, + body: SafeArea( + child: Panorama( + sensitivity: 2, + sensorControl: SensorControl.None, + latitude: 4, + child: (isFileImage ?? false) + ? Image.file(File(imageUrl)) + : Image.network( + imageUrl, + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/widgets/promoted_widget.dart b/lib/Ui/screens/widgets/promoted_widget.dart new file mode 100644 index 0000000..5b09b56 --- /dev/null +++ b/lib/Ui/screens/widgets/promoted_widget.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; + +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/ui_utils.dart'; + +enum PromoteCardType { text, icon } + +class PromotedCard extends StatelessWidget { + final PromoteCardType type; + final Color? color; + const PromotedCard({super.key, required this.type, this.color}); + + @override + Widget build(BuildContext context) { + if (type == PromoteCardType.icon) { + return Container( + // width: 64, + // height: 24, + decoration: BoxDecoration( + color: color ?? context.color.tertiaryColor, + borderRadius: BorderRadius.circular(4)), + child: Padding( + padding: const EdgeInsets.all(3.0), + child: Center( + child: Text(UiUtils.translate(context, "featured")) + .color( + context.color.primaryColor, + ) + .bold() + .size(context.font.smaller), + ), + ), + ); + } + + return Container( + width: 64, + height: 24, + decoration: BoxDecoration( + color: context.color.tertiaryColor, + borderRadius: BorderRadius.circular(4)), + child: Center( + child: Text(UiUtils.translate(context, "featured")) + .color( + context.color.primaryColor, + ) + .bold() + .size(context.font.smaller), + ), + ); + } +} diff --git a/lib/Ui/screens/widgets/read_more_text.dart b/lib/Ui/screens/widgets/read_more_text.dart new file mode 100644 index 0000000..9e6b0df --- /dev/null +++ b/lib/Ui/screens/widgets/read_more_text.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; + +import '../../../utils/ui_utils.dart'; + +class ReadMoreText extends StatefulWidget { + final String text; + final int? maxVisibleCharectors; + final TextStyle? style; + final TextStyle? readMoreButtonStyle; + const ReadMoreText( + {super.key, + required this.text, + this.maxVisibleCharectors, + this.style, + this.readMoreButtonStyle}); + + @override + State createState() => _ReadMoreTextState(); +} + +class _ReadMoreTextState extends State { + bool showingFullText = false; + + buildReadMore(String text) { + final textPainter = TextPainter( + text: TextSpan(text: text, style: DefaultTextStyle.of(context).style), + maxLines: null, + textDirection: TextDirection.ltr, + )..layout(maxWidth: MediaQuery.of(context).size.width); + + final numLines = textPainter.computeLineMetrics().length; + + if (numLines > 5) { + return Wrap( + children: [ + Text( + showingFullText ? text : _truncateText(text), + style: widget.style, + ), + TextButton( + style: const ButtonStyle( + padding: MaterialStatePropertyAll(EdgeInsets.zero), + ), + onPressed: () { + setState(() { + showingFullText = !showingFullText; + }); + }, + child: Text( + showingFullText + ? UiUtils.translate(context, "readLessLbl") + : UiUtils.translate(context, "readMoreLbl"), + style: widget.readMoreButtonStyle, + ), + ), + ], + ); + } + + return Text(text); + } + + String _truncateText(String text) { + final textPainter = TextPainter( + text: TextSpan(text: text, style: DefaultTextStyle.of(context).style), + maxLines: 4, + textDirection: TextDirection.ltr, + )..layout(maxWidth: MediaQuery.of(context).size.width); + + final endIndex = textPainter + .getPositionForOffset( + Offset(MediaQuery.of(context).size.width, double.infinity)) + .offset; + + final truncatedText = text.substring(0, endIndex).trim(); + return truncatedText.length < text.length + ? "$truncatedText..." + : truncatedText; + } + + @override + Widget build(BuildContext context) { + return buildReadMore(widget.text); + } +} diff --git a/lib/Ui/screens/widgets/shimmerLoadingContainer.dart b/lib/Ui/screens/widgets/shimmerLoadingContainer.dart new file mode 100644 index 0000000..98f80f9 --- /dev/null +++ b/lib/Ui/screens/widgets/shimmerLoadingContainer.dart @@ -0,0 +1,32 @@ +// ignore_for_file: file_names + +import 'package:flutter/material.dart'; +import 'package:shimmer/shimmer.dart'; + +import '../../Theme/theme.dart'; + +class CustomShimmer extends StatelessWidget { + final double? height; + final double? width; + final double? borderRadius; + final EdgeInsetsGeometry? margin; + const CustomShimmer( + {Key? key, this.height, this.width, this.borderRadius, this.margin}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Shimmer.fromColors( + baseColor: Theme.of(context).colorScheme.shimmerBaseColor, + highlightColor: Theme.of(context).colorScheme.shimmerHighlightColor, + child: Container( + width: width, + margin: margin, + height: height ?? 10, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.shimmerContentColor, + borderRadius: BorderRadius.circular(borderRadius ?? 10)), + ), + ); + } +} diff --git a/lib/Ui/screens/widgets/sizedBoxes.dart b/lib/Ui/screens/widgets/sizedBoxes.dart new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/Ui/screens/widgets/sizedBoxes.dart @@ -0,0 +1 @@ + diff --git a/lib/Ui/screens/widgets/video_view_screen.dart b/lib/Ui/screens/widgets/video_view_screen.dart new file mode 100644 index 0000000..2ad3679 --- /dev/null +++ b/lib/Ui/screens/widgets/video_view_screen.dart @@ -0,0 +1,50 @@ +import 'package:flick_video_player/flick_video_player.dart'; +import 'package:flutter/material.dart'; + +import '../../../utils/Extensions/extensions.dart'; +import '../../../utils/helper_utils.dart'; +import 'youtube_player_widget.dart'; + +class VideoViewScreen extends StatelessWidget { + final String videoUrl; + final FlickManager? flickManager; + const VideoViewScreen({ + Key? key, + required this.videoUrl, + this.flickManager, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + extendBodyBehindAppBar: true, + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + automaticallyImplyLeading: true, + iconTheme: IconThemeData(color: context.color.tertiaryColor), + ), + backgroundColor: context.color.backgroundColor, + body: Center( + child: HelperUtils.checkVideoType( + videoUrl, + onYoutubeVideo: () { + return YoutubePlayerWidget( + videoUrl: videoUrl, + onLandscape: () {}, + onPortrate: () {}, + ); + }, + onOtherVideo: () { + if (flickManager != null) { + return FlickVideoPlayer(flickManager: flickManager!); + } + return Container(); + }, + ), + ), + ), + ); + } +} diff --git a/lib/Ui/screens/widgets/youtube_player_widget.dart b/lib/Ui/screens/widgets/youtube_player_widget.dart new file mode 100644 index 0000000..af67ecf --- /dev/null +++ b/lib/Ui/screens/widgets/youtube_player_widget.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:youtube_player_flutter/youtube_player_flutter.dart'; + +class YoutubePlayerWidget extends StatefulWidget { + final VoidCallback onLandscape; + final VoidCallback onPortrate; + + final String videoUrl; + const YoutubePlayerWidget( + {super.key, + required this.videoUrl, + required this.onLandscape, + required this.onPortrate}); + + @override + State createState() => _YoutubePlayerWidgetState(); +} + +class _YoutubePlayerWidgetState extends State { + late YoutubePlayerController controller; + + getVideoId() { + return YoutubePlayer.convertUrlToId(widget.videoUrl)!; + } + + @override + void initState() { + controller = YoutubePlayerController( + initialVideoId: getVideoId(), + flags: const YoutubePlayerFlags( + showLiveFullscreenButton: true, + autoPlay: false, + ), + ); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + child: YoutubePlayerBuilder( + onEnterFullScreen: () { + widget.onLandscape.call(); + }, + onExitFullScreen: () { + widget.onPortrate.call(); + }, + player: YoutubePlayer( + controller: controller, + ), + builder: (context, child) { + return child; + }, + )); + } +} diff --git a/lib/app/analytics_routes.dart b/lib/app/analytics_routes.dart new file mode 100644 index 0000000..bcf5d42 --- /dev/null +++ b/lib/app/analytics_routes.dart @@ -0,0 +1,18 @@ +//// This routes are not used for navigation in the app . instead these are used for track navigation in better naming. +//// Do not change these name , because this can couse analytics data corrupt +class AnalyticsRoutes { + static const String home = "Home Screen"; + static const String chatList = "Chat List Screen"; + static const String message = "Message Screen"; + static const String properties = "Properties Screen"; + static const String propertyDetailsScreen = "Property Details Screen"; + static const String searchScreen = "Search Screen"; + static const String propertyMapScreen = "Property Map Screen"; + static const String categoryPropertyList = "Category Property List"; + static const String editProfileScreen = "Edit Profile Screen"; + static const String articlesScreen = "Articles Screen"; + static const String favoriteScreen = "Favorite Screen"; + static const String subscriptionScreen = "Subscription Pacakge Screen"; + static const String myAdvertismentScreen = "My Advertisment Screen"; + static const String areaConverterScreen = "Area Converter Screen"; +} diff --git a/lib/app/app.dart b/lib/app/app.dart new file mode 100644 index 0000000..ce65665 --- /dev/null +++ b/lib/app/app.dart @@ -0,0 +1,224 @@ +import 'package:ebroker/Ui/screens/widgets/Erros/something_went_wrong.dart'; +import 'package:ebroker/data/cubits/project/fetch_projects.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; +import 'package:path_provider/path_provider.dart'; + +import '../Ui/screens/splash_screen.dart'; +import '../data/Repositories/personalized_feed_repository.dart'; +import '../data/cubits/Personalized/fetch_personalized_properties.dart'; +import '../data/model/Personalized/personalized_settings.dart'; +import '../data/model/app_settings_datamodel.dart'; +import '../firebase_options.dart'; +import '../main.dart'; +import '../utils/Network/apiCallTrigger.dart'; +import '../utils/api.dart'; +import '../utils/guestChecker.dart'; +import '../utils/ui_utils.dart'; +import 'default_app_setting.dart'; + +PersonalizedInterestSettings personalizedInterestSettings = + PersonalizedInterestSettings.empty(); +AppSettingsDataModel appSettings = fallbackSettingAppSettings; + +/// + +getAppSettings() async { + await LoadAppSettings().load(); +} + +Future getLanguage() async { + await getDefaultLanguage( + () {}, + ); +} + +void initApp() async { + ///Note: this file's code is very necessary and sensitive if you change it, this might affect whole app , So change it carefully. + ///This must be used do not remove this line + WidgetsFlutterBinding.ensureInitialized(); + MobileAds.instance.initialize(); + await HiveUtils.initBoxes(); + // await Isolate.spawn(getLanguage, languageSettingReceivePort.sendPort); + + Api.initInterceptors(); + + ///This is the widget to show uncaught runtime error in this custom widget so that user can know in that screen something is wrong instead of grey screen + SomethingWentWrong.asGlobalErrorBuilder(); + + if (Firebase.apps.isNotEmpty) { + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); + } else { + await Firebase.initializeApp(); + } + + FirebaseMessaging.onBackgroundMessage( + NotificationService.onBackgroundMessageHandler); + HydratedBloc.storage = await HydratedStorage.build( + storageDirectory: await getApplicationDocumentsDirectory(), + ); + + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]) + .then((_) async { + SystemChrome.setSystemUIOverlayStyle( + const SystemUiOverlayStyle(statusBarColor: Colors.transparent)); + + runApp(const EntryPoint()); + }); +} + +class App extends StatefulWidget { + const App({super.key}); + @override + State createState() => _AppState(); +} + +class _AppState extends State { + @override + void initState() { + ///Here Fetching property report reasons + context.read().fetch(); + context.read().loadCurrentLanguage(); + AppTheme currentTheme = HiveUtils.getCurrentTheme(); + + ///Initialized notification services + LocalAwsomeNotification().init(context); + /////////////////////////////////////// + NotificationService.init(context); + + /// Initialized dynamic links for share properties feature + context.read().changeTheme(currentTheme); + + APICallTrigger.onTrigger( + () { + //THIS WILL be CALLED WHEN USER WILL LOGIN FROM ANONYMOUS USER. + context.read().emptyCubit(); + context.read().fetch(); + + loadInitialData(context, loadWithoutDelay: true); + }, + ); + + UiUtils.setContext(context); + super.initState(); + } + + @override + Widget build(BuildContext context) { + //Continuously watching theme change + AppTheme currentTheme = context.watch().state.appTheme; + return BlocListener( + listener: (context, state) { + context.read().setAPIKeys(); + }, + child: BlocBuilder( + builder: (context, languageState) { + return MaterialApp( + initialRoute: Routes + .splash, // App will start from here splash screen is first screen, + navigatorKey: Constant + .navigatorKey, //This navigator key is used for Navigate users through notification + title: Constant.appName, + debugShowCheckedModeBanner: false, + onGenerateRoute: Routes.onGenerateRouted, + theme: appThemeData[currentTheme], + builder: (context, child) { + TextDirection direction; + //here we are languages direction locally + if (languageState is LanguageLoader) { + if (Constant.totalRtlLanguages + .contains((languageState).languageCode)) { + direction = TextDirection.rtl; + } else { + direction = TextDirection.ltr; + } + } else { + direction = TextDirection.ltr; + } + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaler: const TextScaler.linear(1.0), + // textScaleFactor: + // 1.0, //set text scale factor to 1 so that this will not resize app's text while user change their system settings text scale + ), + child: Directionality( + textDirection: + direction, //This will convert app direction according to language + child: child!, + ), + ); + }, + localizationsDelegates: const [ + AppLocalization.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + locale: loadLocalLanguageIfFail(languageState), + ); + }, + ), + ); + } + + dynamic loadLocalLanguageIfFail(LanguageState state) { + if ((state is LanguageLoader)) { + return Locale(state.languageCode); + } else if (state is LanguageLoadFail) { + return const Locale("en"); + } + } +} + +void loadInitialData(BuildContext context, + {bool? loadWithoutDelay, bool? forceRefresh}) { + context.read().fetchProjects(); + context.read().fetchSlider(context, + loadWithoutDelay: loadWithoutDelay, forceRefresh: forceRefresh); + context.read().fetchCategories( + loadWithoutDelay: loadWithoutDelay, forceRefresh: forceRefresh); + context + .read() + .fetch(loadWithoutDelay: loadWithoutDelay, forceRefresh: forceRefresh); + context + .read() + .fetch(loadWithoutDelay: loadWithoutDelay, forceRefresh: forceRefresh); + + context + .read() + .fetch(loadWithoutDelay: loadWithoutDelay, forceRefresh: forceRefresh); + context + .read() + .fetch(loadWithoutDelay: loadWithoutDelay, forceRefresh: forceRefresh); + context.read().fetchCityCategory( + loadWithoutDelay: loadWithoutDelay, forceRefresh: forceRefresh); + context + .read() + .fetch(loadWithoutDelay: loadWithoutDelay, forceRefresh: forceRefresh); + context + .read() + .fetch(loadWithoutDelay: loadWithoutDelay, forceRefresh: forceRefresh); + context.read().setContext(context); + context.read().fetch(); + + // if (widget.from != "login") { + PersonalizedFeedRepository().getUserPersonalizedSettings().then((value) { + personalizedInterestSettings = value; + }); + GuestChecker.listen().addListener(() { + if (GuestChecker.value == false) { + PersonalizedFeedRepository().getUserPersonalizedSettings().then((value) { + personalizedInterestSettings = value; + }); + } + }); + +// // } +} diff --git a/lib/app/app_localization.dart b/lib/app/app_localization.dart new file mode 100644 index 0000000..7e43760 --- /dev/null +++ b/lib/app/app_localization.dart @@ -0,0 +1,75 @@ +//For localization of app + +import 'dart:convert'; + +import 'package:ebroker/utils/hive_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class AppLocalization { + final Locale locale; + + //it will hold key of text and it's values in given language + late Map _localizedValues; + + AppLocalization(this.locale); + + //to access app-localization instance any where in app using context + static AppLocalization? of(BuildContext context) { + return Localizations.of(context, AppLocalization); + } + + //to load json(language) from assets + Future loadJson() async { + // String languageJsonName = locale.countryCode == null + // ? locale.languageCode + // : "${locale.languageCode}-${locale.countryCode}"; + String jsonStringValues = + await rootBundle.loadString('assets/languages/template.json'); + // value from root-bundle will be encoded string + Map mappedJson = {}; + + if (HiveUtils.getLanguage() == null || + HiveUtils.getLanguage()['data'] == null) { + mappedJson = json.decode(jsonStringValues); + } else { + mappedJson = Map.from(HiveUtils.getLanguage()['data']); + } + _localizedValues = + mappedJson.map((key, value) => MapEntry(key, value.toString())); + } + + //to get translated value of given title/key + String? getTranslatedValues(String? key) { + return _localizedValues[key!]; + } + + //need to declare custom delegate + static const LocalizationsDelegate delegate = + _AppLocalizationDelegate(); +} + +//Custom app delegate +class _AppLocalizationDelegate extends LocalizationsDelegate { + const _AppLocalizationDelegate(); + + //providing all supported languages + @override + bool isSupported(Locale locale) { + // + return true; + } + + //load languageCode.json files + @override + Future load(Locale locale) async { + AppLocalization localization = AppLocalization(locale); + await localization.loadJson(); + return localization; + } + + @override + bool shouldReload(LocalizationsDelegate old) { + return true; + } +} diff --git a/lib/app/app_theme.dart b/lib/app/app_theme.dart new file mode 100644 index 0000000..8f2e5c8 --- /dev/null +++ b/lib/app/app_theme.dart @@ -0,0 +1,49 @@ +// ignore_for_file: deprecated_member_use + +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; + +enum AppTheme { dark, light } + +final appThemeData = { + AppTheme.light: ThemeData( + useMaterial3: false, + // scaffoldBackgroundColor: pageBackgroundColor, + brightness: Brightness.light, + //textTheme + fontFamily: "Manrope", + textSelectionTheme: const TextSelectionThemeData( + selectionColor: Colors.green, + cursorColor: Colors.green, + selectionHandleColor: Colors.green, + ), + errorColor: errorMessageColor, + // textSelectionTheme: + // const TextSelectionThemeData(selectionHandleColor: teritoryColor_), + switchTheme: SwitchThemeData( + thumbColor: const MaterialStatePropertyAll(tertiaryColor_), + trackColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.selected)) { + return tertiaryColor_.withOpacity(0.3); + } + return primaryColorDark; + }), + ), + ), + AppTheme.dark: ThemeData( + brightness: Brightness.dark, + useMaterial3: false, + fontFamily: "Manrope", + errorColor: errorMessageColor.withOpacity(0.7), + textSelectionTheme: + const TextSelectionThemeData(selectionHandleColor: tertiaryColorDark), + switchTheme: SwitchThemeData( + thumbColor: const MaterialStatePropertyAll(tertiaryColor_), + trackColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.selected)) { + return tertiaryColor_.withOpacity(0.3); + } + return primaryColor_.withOpacity(0.2); + })), + ) +}; diff --git a/lib/app/default_app_setting.dart b/lib/app/default_app_setting.dart new file mode 100644 index 0000000..6c0e28d --- /dev/null +++ b/lib/app/default_app_setting.dart @@ -0,0 +1,102 @@ +import 'dart:ui'; + +import 'package:ebroker/data/model/app_settings_datamodel.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/AppIcon.dart'; +import 'package:ebroker/utils/hive_keys.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:hive/hive.dart'; + +import '../Ui/Theme/theme.dart'; +import '../utils/api.dart'; +import '../utils/network_to_localsvg.dart'; + +AppSettingsDataModel fallbackSettingAppSettings = AppSettingsDataModel( + appHomeScreen: AppIcons.fallbackHomeLogo, + splashLogo: AppIcons.fallbackSplashLogo, + placeholderLogo: AppIcons.fallbackPlaceholderLogo, + lightPrimary: primaryColor_, + lightSecondary: secondaryColor_, + lightTertiary: tertiaryColor_, + darkPrimary: primaryColorDark, + darkSecondary: secondaryColorDark, + darkTertiary: tertiaryColorDark, +); + +///DO not touch this +class LoadAppSettings { + Future load() async { + try { + try { + await HiveUtils.initBoxes(); + Map response = + await Api.get(url: Api.getAppSettings, queryParameters: { + if (HiveUtils.getUserId() != null) "user_id": HiveUtils.getUserId() + }); + + appSettings = AppSettingsDataModel.fromJson(response['data']); + HiveUtils.setAppThemeSetting(response['data']); + + ///Set other icons from here which will come from server + /// + appSettings.splashLogo = + await loadIconIfChange(appSettings.splashLogo!); + appSettings.appHomeScreen = + await loadIconIfChange(appSettings.appHomeScreen!); + appSettings.placeholderLogo = + await loadIconIfChange(appSettings.placeholderLogo!); + } catch (e) { + appSettings = + AppSettingsDataModel.fromJson(HiveUtils.getAppThemeSettings()); + appSettings.splashLogo = + await loadIconIfChange(appSettings.splashLogo!); + appSettings.appHomeScreen = + await loadIconIfChange(appSettings.appHomeScreen!); + appSettings.placeholderLogo = + await loadIconIfChange(appSettings.placeholderLogo!); + } + } catch (ee) { + print("Issue in load default setting $ee"); + } + } + + Future loadIconIfChange(String svgURL) async { + try { + Box box = Hive.box(HiveKeys.svgBox); + bool isAvailable = box.containsKey(svgURL); + if (isAvailable) { + return box.get(svgURL) as String; + } else { + String? localSVG = await NetworkToLocalSvg().convert(svgURL); + await box.put(svgURL, localSVG); + + return await Future.value(localSVG); + } + } catch (e) { + rethrow; + } + } + + SvgPicture svg( + String svg, { + Color? color, + double? width, + double? height, + }) { + if (svg.startsWith("assets/svg/")) { + return SvgPicture.asset( + svg, + color: color, + width: width, + height: height, + ); + } else { + return SvgPicture.string( + svg, + color: color, + width: width, + height: height, + ); + } + } +} diff --git a/lib/app/register_cubits.dart b/lib/app/register_cubits.dart new file mode 100644 index 0000000..462fac2 --- /dev/null +++ b/lib/app/register_cubits.dart @@ -0,0 +1,66 @@ +import 'package:ebroker/data/cubits/Personalized/add_update_personalized_interest.dart'; +import 'package:ebroker/data/cubits/Personalized/fetch_personalized_properties.dart'; +import 'package:ebroker/data/cubits/project/fetchMyProjectsListCubit.dart'; +import 'package:ebroker/data/cubits/project/fetch_projects.dart'; +import 'package:ebroker/data/cubits/property/fetch_city_property_list.dart'; +import 'package:nested/nested.dart'; + +import '../exports/main_export.dart'; + +class RegisterCubits { + List register() { + return [ + BlocProvider(create: (context) => AuthCubit()), + BlocProvider(create: (context) => FetchMyProjectsListCubit()), + BlocProvider(create: (context) => FetchProjectsCubit()), + BlocProvider(create: (context) => LoginCubit()), + BlocProvider(create: (context) => SliderCubit()), + BlocProvider(create: (context) => CompanyCubit()), + BlocProvider(create: (context) => PropertyCubit()), + BlocProvider(create: (context) => FetchCategoryCubit()), + BlocProvider(create: (context) => HouseTypeCubit()), + BlocProvider(create: (context) => SearchPropertyCubit()), + BlocProvider(create: (context) => DeleteAccountCubit()), + BlocProvider(create: (context) => TopViewedPropertyCubit()), + BlocProvider(create: (context) => ProfileSettingCubit()), + BlocProvider(create: (context) => NotificationCubit()), + BlocProvider(create: (context) => AppThemeCubit()), + BlocProvider(create: (context) => AuthenticationCubit()), + BlocProvider(create: (context) => FetchHomePropertiesCubit()), + BlocProvider(create: (context) => FetchTopRatedPropertiesCubit()), + BlocProvider(create: (context) => FetchMyPropertiesCubit()), + BlocProvider(create: (context) => FetchPropertyFromCategoryCubit()), + BlocProvider(create: (context) => FetchNotificationsCubit()), + BlocProvider(create: (context) => LanguageCubit()), + BlocProvider(create: (context) => GooglePlaceAutocompleteCubit()), + BlocProvider(create: (context) => FetchArticlesCubit()), + BlocProvider(create: (context) => FetchSystemSettingsCubit()), + BlocProvider(create: (context) => FavoriteIDsCubit()), + BlocProvider(create: (context) => FetchPromotedPropertiesCubit()), + BlocProvider(create: (context) => FetchMostViewedPropertiesCubit()), + BlocProvider(create: (context) => FetchFavoritesCubit()), + BlocProvider(create: (context) => CreatePropertyCubit()), + BlocProvider(create: (context) => UserDetailsCubit()), + BlocProvider(create: (context) => FetchLanguageCubit()), + BlocProvider(create: (context) => LikedPropertiesCubit()), + BlocProvider(create: (context) => EnquiryIdsLocalCubit()), + BlocProvider(create: (context) => AddToFavoriteCubitCubit()), + BlocProvider(create: (context) => FetchSubscriptionPackagesCubit()), + BlocProvider(create: (context) => RemoveFavoriteCubit()), + BlocProvider(create: (context) => GetApiKeysCubit()), + BlocProvider(create: (context) => FetchCityCategoryCubit()), + BlocProvider(create: (context) => SetPropertyViewCubit()), + BlocProvider(create: (context) => GetChatListCubit()), + BlocProvider(create: (context) => FetchPropertyReportReasonsListCubit()), + BlocProvider(create: (context) => FetchMostLikedPropertiesCubit()), + BlocProvider(create: (context) => FetchNearbyPropertiesCubit()), + BlocProvider(create: (context) => FetchOutdoorFacilityListCubit()), + BlocProvider(create: (context) => FetchRecentPropertiesCubit()), + BlocProvider(create: (context) => PropertyEditCubit()), + BlocProvider(create: (context) => FetchCityPropertyList()), + BlocProvider(create: (context) => FetchPersonalizedPropertyList()), + BlocProvider(create: (context) => AddUpdatePersonalizedInterest()), + BlocProvider(create: (context) => GetSubsctiptionPackageLimitsCubit()) + ]; + } +} diff --git a/lib/app/routes.dart b/lib/app/routes.dart new file mode 100644 index 0000000..4819c7e --- /dev/null +++ b/lib/app/routes.dart @@ -0,0 +1,251 @@ +import 'package:ebroker/Ui/screens/Dashboard/dashbord.dart'; +import 'package:ebroker/Ui/screens/Personalized/personalized_property_screen.dart'; +import 'package:ebroker/Ui/screens/home/view_most_liked_properties.dart'; +import 'package:ebroker/Ui/screens/home/view_nearby_properties.dart'; +import 'package:ebroker/Ui/screens/map/choose_location_map.dart'; +import 'package:ebroker/Ui/screens/map/property_map_screen.dart'; +import 'package:ebroker/Ui/screens/project/create/add_project_details.dart'; +import 'package:ebroker/Ui/screens/project/create/add_project_meta_details.dart'; +import 'package:ebroker/Ui/screens/project/create/manage_floor_plans.dart'; +import 'package:ebroker/Ui/screens/project/view/project_details_screen.dart'; +import 'package:ebroker/Ui/screens/project/view/project_list_screen.dart'; +import 'package:ebroker/Ui/screens/proprties/AddProperyScreens/select_outdoor_facility.dart'; +import 'package:ebroker/data/cubits/project/all_projects_screen.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import '../Ui/screens/Advertisement/create_advertisement_screen.dart'; +import '../Ui/screens/Advertisement/my_advertisment_screen.dart'; +import '../Ui/screens/Articles/article_details.dart'; +import '../Ui/screens/Articles/articles_screen.dart'; +import '../Ui/screens/Converter/area_converter.dart'; +import '../Ui/screens/auth/login_screen.dart'; +import '../Ui/screens/favorites_screen.dart'; +import '../Ui/screens/filter_screen.dart'; +import '../Ui/screens/home/category_list.dart'; +import '../Ui/screens/home/change_language_screen.dart'; +import '../Ui/screens/home/search_screen.dart'; +import '../Ui/screens/home/view_most_viewed_properties.dart'; +import '../Ui/screens/home/view_promoted_properties.dart'; +import '../Ui/screens/main_activity.dart'; +import '../Ui/screens/onboarding/onboarding_screen.dart'; +import '../Ui/screens/proprties/AddProperyScreens/add_property_details.dart'; +import '../Ui/screens/proprties/AddProperyScreens/select_type_of_property.dart'; +import '../Ui/screens/proprties/AddProperyScreens/set_property_parameters.dart'; +import '../Ui/screens/proprties/properties_list.dart'; +import '../Ui/screens/proprties/property_details.dart'; +import '../Ui/screens/settings/contact_us.dart'; +import '../Ui/screens/settings/notification_detail.dart'; +import '../Ui/screens/settings/notifications.dart'; +import '../Ui/screens/settings/profile_setting.dart'; +import '../Ui/screens/splash_screen.dart'; +import '../Ui/screens/subscription/packages_list.dart'; +import '../Ui/screens/subscription/subscribe_screen.dart'; +import '../Ui/screens/subscription/transaction_history_screen.dart'; +import '../Ui/screens/userprofile/edit_profile.dart'; +import '../Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import '../Ui/screens/widgets/maintenance_mode.dart'; +import '../sandBox/playground.dart'; +import '../utils/DeepLink/nativeDeepLinkManager.dart'; +import '../utils/ui_utils.dart'; + +class Routes { + //private constructor + Routes._(); + + static const splash = 'splash'; + static const onboarding = 'onboarding'; + static const login = 'login'; + static const completeProfile = 'complete_profile'; + static const main = 'main'; + static const home = 'Home'; + static const addProperty = 'addProperty'; + static const waitingScreen = 'waitingScreen'; + static const categories = 'Categories'; + static const addresses = 'address'; + static const chooseAdrs = 'chooseAddress'; + static const propertiesList = 'propertiesList'; + static const propertyDetails = 'PropertyDetails'; + static const contactUs = 'ContactUs'; + static const profileSettings = 'profileSettings'; + static const myEnquiry = 'MyEnquiry'; + static const filterScreen = 'filterScreen'; + static const notificationPage = 'notificationpage'; + static const notificationDetailPage = 'notificationdetailpage'; + static const addPropertyScreenRoute = 'addPropertyScreenRoute'; + static const articlesScreenRoute = 'articlesScreenRoute'; + static const subscriptionPackageListRoute = 'subscriptionPackageListRoute'; + static const subscriptionScreen = 'subscriptionScreen'; + static const maintenanceMode = '/maintenanceMode'; + static const favoritesScreen = '/favoritescreen'; + static const createAdvertismentScreenRoute = '/createAdvertisment'; + static const promotedPropertiesScreen = '/promotedPropertiesScreen'; + static const mostLikedPropertiesScreen = '/mostLikedPropertiesScreen'; + static const mostViewedPropertiesScreen = '/mostViewedPropertiesScreen'; + static const articleDetailsScreenRoute = '/articleDetailsScreenRoute'; + static const areaConvertorScreen = '/areaCalculatorScreen'; + static const languageListScreenRoute = '/languageListScreenRoute'; + static const searchScreenRoute = '/searchScreenRoute'; + static const chooseLocaitonMap = '/chooseLocationMap'; + static const propertyMapScreen = '/propertyMap'; + static const dashboard = '/dashboard'; + + static const myAdvertisment = '/myAdvertisment'; + static const transactionHistory = '/transactionHistory'; + static const nearbyAllProperties = '/nearbyAllProperties'; + static const personalizedPropertyScreen = '/personalizedPropertyScreen'; + static const allProjectsScreen = '/allProjectsScreen'; + + ///Project section routes + + static const String addProjectDetails = "/addProjectDetails"; + static const String projectMetaDataScreens = "/projectMetaDataScreens"; + static const String manageFloorPlansScreen = "/manageFloorPlansScreen"; + + ///Add property screens + static const selectPropertyTypeScreen = '/selectPropertyType'; + static const addPropertyDetailsScreen = '/addPropertyDetailsScreen'; + static const setPropertyParametersScreen = '/setPropertyParametersScreen'; + static const selectOutdoorFacility = '/selectOutdoorFacility'; + + ///View project + static const projectDetailsScreen = '/projectDetailsScreen'; + static const projectListScreen = '/projectListScreen'; + + //Sandbox[test] + static const playground = 'playground'; + + static String currentRoute = splash; + static String previousCustomerRoute = splash; + static Route? onGenerateRouted(RouteSettings routeSettings) { + previousCustomerRoute = currentRoute; + currentRoute = routeSettings.name ?? ""; + + ///This is to prevent infinity loading while login browser + if (routeSettings.name!.contains("/link?")) { + return null; + } + + switch (routeSettings.name) { + case "": + return null; + + case splash: + return BlurredRouter(builder: ((context) => const SplashScreen())); + case onboarding: + return CupertinoPageRoute( + builder: ((context) => const OnboardingScreen())); + case main: + return MainActivity.route(routeSettings); + case login: + return LoginScreen.route(routeSettings); + case completeProfile: + return UserProfileScreen.route(routeSettings); + + case categories: + return CategoryList.route(routeSettings); + case maintenanceMode: + return MaintenanceMode.route(routeSettings); + case languageListScreenRoute: + return LanguagesListScreen.route(routeSettings); + case propertiesList: + return PropertiesList.route(routeSettings); + case propertyDetails: + return PropertyDetails.route(routeSettings); + case contactUs: + return ContactUs.route(routeSettings); + case profileSettings: + return ProfileSettings.route(routeSettings); + + case filterScreen: + return FilterScreen.route(routeSettings); + case notificationPage: + return Notifications.route(routeSettings); + case notificationDetailPage: + return NotificationDetail.route(routeSettings); + case chooseLocaitonMap: + return ChooseLocationMap.route(routeSettings); + case articlesScreenRoute: + return ArticlesScreen.route(routeSettings); + case mostLikedPropertiesScreen: + return MostLikedPropertiesScreen.route(routeSettings); + case areaConvertorScreen: + return AreaCalculator.route(routeSettings); + + case articleDetailsScreenRoute: + return ArticleDetails.route(routeSettings); + case subscriptionPackageListRoute: + return SubscriptionPackageListScreen.route(routeSettings); + case subscriptionScreen: + return SubscriptionScreen.route(routeSettings); + case favoritesScreen: + return FavoritesScreen.route(routeSettings); + case createAdvertismentScreenRoute: + return CreateAdvertisementScreen.route(routeSettings); + case promotedPropertiesScreen: + return PromotedPropertiesScreen.route(routeSettings); + case mostViewedPropertiesScreen: + return MostViewedPropertiesScreen.route(routeSettings); + + case selectPropertyTypeScreen: + return SelectPropertyType.route(routeSettings); + + case transactionHistory: + return TransactionHistory.route(routeSettings); + + case myAdvertisment: + return MyAdvertismentScreen.route(routeSettings); + case personalizedPropertyScreen: + return PersonalizedPropertyScreen.route(routeSettings); + case dashboard: + return DashboardScreen.route(routeSettings); + case addPropertyDetailsScreen: + return AddPropertyDetails.route(routeSettings); + case setPropertyParametersScreen: + return SetProeprtyParametersScreen.route(routeSettings); + case searchScreenRoute: + return SearchScreen.route(routeSettings); + + case propertyMapScreen: + return PropertyMapScreen.route(routeSettings); + case nearbyAllProperties: + return NearbyAllPropertiesScreen.route(routeSettings); + case selectOutdoorFacility: + return SelectOutdoorFacility.route(routeSettings); + + case addProjectDetails: + return AddProjectDetails.route(routeSettings); + + case projectMetaDataScreens: + return ProjectMetaDetails.route(routeSettings); + + case projectDetailsScreen: + return ProjectDetailsScreen.route(routeSettings); + + case manageFloorPlansScreen: + return ManageFloorPlansScreen.route(routeSettings); + case projectListScreen: + return ProjectListScreen.route(routeSettings); + case allProjectsScreen: + return AllProjectsScreen.route(routeSettings); + + case playground: + return PlayGround.route(routeSettings); + + default: + if (routeSettings.name!.contains(AppSettings.shareNavigationWebUrl)) { + return NativeLinkWidget.render(routeSettings); + } + return BlurredRouter( + builder: ((context) => Scaffold( + body: Center( + child: Text( + UiUtils.translate(context, "pageNotFoundErrorMsg"), + ), + ), + )), + ); + } + } +} diff --git a/lib/data/.DS_Store b/lib/data/.DS_Store new file mode 100644 index 0000000..808dbcf Binary files /dev/null and b/lib/data/.DS_Store differ diff --git a/lib/data/Repositories/advertisement_repository.dart b/lib/data/Repositories/advertisement_repository.dart new file mode 100644 index 0000000..4c19874 --- /dev/null +++ b/lib/data/Repositories/advertisement_repository.dart @@ -0,0 +1,27 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; + +import '../../utils/api.dart'; + +class AdvertisementRepository { + Future> create({ + required String type, + required String propertyId, + File? image, + }) async { + Map parameters = { + Api.propertyId: propertyId, + Api.type: type + }; + if (image != null) { + parameters[Api.image] = await MultipartFile.fromFile(image.path); + } + + return await Api.post(url: Api.storeAdvertisement, parameter: parameters); + } + + Future deleteAdvertisment(dynamic id) async { + await Api.post(url: Api.deleteAdvertisement, parameter: {Api.id: id}); + } +} diff --git a/lib/data/Repositories/articles_repository.dart b/lib/data/Repositories/articles_repository.dart new file mode 100644 index 0000000..201be6d --- /dev/null +++ b/lib/data/Repositories/articles_repository.dart @@ -0,0 +1,36 @@ +import '../../utils/api.dart'; +import '../../utils/constant.dart'; +import '../model/article_model.dart'; +import '../model/data_output.dart'; + +class ArticlesRepository { + Future> fetchArticles({required int offset}) async { + Map parameters = { + Api.offset: offset, + Api.limit: Constant.loadLimit + }; + + Map result = + await Api.get(url: Api.getArticles, queryParameters: parameters); + + List modelList = (result['data'] as List) + .map((element) => ArticleModel.fromJson(element)) + .toList(); + + return DataOutput( + total: result['total'] ?? 0, modelList: modelList); + } + + Future fetchArticlesBySlugId(String slug) async { + Map parameters = {"slug_id": slug}; + + Map result = + await Api.get(url: Api.getArticles, queryParameters: parameters); + + List modelList = (result['data'] as List) + .map((element) => ArticleModel.fromJson(element)) + .toList(); + + return modelList.first; + } +} diff --git a/lib/data/Repositories/auth_repository.dart b/lib/data/Repositories/auth_repository.dart new file mode 100644 index 0000000..81b0955 --- /dev/null +++ b/lib/data/Repositories/auth_repository.dart @@ -0,0 +1,82 @@ +import 'dart:ffi'; + +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import '../../../settings.dart'; +import '../../utils/api.dart'; +import '../../utils/constant.dart'; + +Future> fetchUrl(String url) async { + try { + final response = await http.get(Uri.parse(url)); + if (response.statusCode == 200) { + // Successful response + Map data = json.decode(response.body); + // Process the data as needed + return data; + } else { + // Handle unsuccessful response + throw Exception('Failed to fetch data'); + } + } catch (e) { + // Handle any errors that occurred during the process + throw Exception('Failed to fetch data'); + } +} + + +class AuthRepository { + final FirebaseAuth _auth = FirebaseAuth.instance; + static int? forceResendingToken; + Future> loginWithApi( + {required String phone, required String uid}) async { + Map parameters = { + Api.mobile: phone.replaceAll(" ", "").replaceAll("+", ""), + Api.firebaseId: uid, + Api.type: Constant.logintypeMobile, + }; + + Map response = await Api.post( + url: Api.apiLogin, parameter: parameters, useAuthToken: false); + + return {"token": response['token'], "data": response['data']}; + } + + Future sendOTP( + {required String phoneNumber, + required Function(String verificationId) onCodeSent, + Function(dynamic e)? onError}) async { + await FirebaseAuth.instance.verifyPhoneNumber( + timeout: Duration( + seconds: Constant.otpTimeOutSecond, + ), + phoneNumber: phoneNumber, + verificationCompleted: (PhoneAuthCredential credential) {}, + verificationFailed: (FirebaseAuthException e) { + onError?.call(ApiException(e.code)); + }, + codeSent: (String verificationId, int? resendToken) { + forceResendingToken = resendToken; + onCodeSent.call(verificationId); + }, + codeAutoRetrievalTimeout: (String verificationId) {}, + forceResendingToken: forceResendingToken, + ); + } + + Future verifyOTP({ + required String otpVerificationId, + required String otp, + }) async { + String formattedOtp = "${otp.substring(0, 3)}-${otp.substring(3)}"; + Map dataS = + await fetchUrl("${AppSettings.apiUrl}token/cek/$formattedOtp"); + try { + UserCredential userCredential = await _auth.signInWithCustomToken(dataS['tokenCus']); + return userCredential; + } catch (e) { + throw Exception('error: $e'); + } + } +} diff --git a/lib/data/Repositories/category_repository.dart b/lib/data/Repositories/category_repository.dart new file mode 100644 index 0000000..7dac4e5 --- /dev/null +++ b/lib/data/Repositories/category_repository.dart @@ -0,0 +1,24 @@ +import '../../utils/api.dart'; +import '../../utils/constant.dart'; +import '../model/category.dart'; +import '../model/data_output.dart'; + +class CategoryRepository { + Future> fetchCategories( + {required int offset, int? id}) async { + Map parameters = { + if (id != null) "id": id, + Api.offset: offset, + Api.limit: Constant.loadLimit, + }; + Map response = + await Api.get(url: Api.apiGetCategories, queryParameters: parameters); + + List modelList = (response['data'] as List).map( + (e) { + return Category.fromJson(e); + }, + ).toList(); + return DataOutput(total: response['total'] ?? 0, modelList: modelList); + } +} diff --git a/lib/data/Repositories/chat_repository.dart b/lib/data/Repositories/chat_repository.dart new file mode 100644 index 0000000..024bcd8 --- /dev/null +++ b/lib/data/Repositories/chat_repository.dart @@ -0,0 +1,95 @@ +import 'package:dio/dio.dart'; +import 'package:ebroker/utils/logger.dart'; +import 'package:flutter/material.dart'; + +import '../../Ui/screens/ChatNew/MessageTypes/blueprint.dart'; +import '../../Ui/screens/ChatNew/MessageTypes/registerar.dart'; +import '../../Ui/screens/ChatNew/model.dart'; +import '../../utils/api.dart'; +import '../../utils/constant.dart'; +import '../../utils/hive_utils.dart'; +import '../model/chat/chated_user_model.dart'; +import '../model/data_output.dart'; + +class ChatRepostiory { + BuildContext? _setContext; + + void setContext(BuildContext context) { + _setContext = context; + } + + Future> fetchChatList(int pageNumber) async { + Map response = await Api.get( + url: Api.getChatList, + queryParameters: {"page": pageNumber, "per_page": Constant.loadLimit}); + + List modelList = (response['data'] as List).map( + (e) { + return ChatedUser.fromJson(e, context: _setContext); + }, + ).toList(); + + return DataOutput(total: response['total_page'] ?? 0, modelList: modelList); + } + + Future> getMessages( + {required int page, required int userId, required int propertyId}) async { + Map response = await Api.get( + url: Api.getMessages, + queryParameters: { + "user_id": userId, + "property_id": propertyId, + "page": page, + "per_page": Constant.minChatMessages + }, + ); + List modelList = (response['data']['data'] as List).map( + (result) { + //Creating model + ChatMessageModel chatMessageModel = ChatMessageModel.fromJson(result); + chatMessageModel.setIsSentByMe( + HiveUtils.getUserId() == chatMessageModel.senderId.toString()); + chatMessageModel.setIsSentNow(false); + chatMessageModel.date = result['created_at']; + //Creating message widget + Message message = filterMessageType(chatMessageModel); + message.isSentByMe = chatMessageModel.isSentByMe ?? false; + message.isSentNow = chatMessageModel.isSentNow ?? false; + message.message = chatMessageModel; + + + return message; + }, + ).toList(); + + return DataOutput(total: response['total_page'] ?? 0, modelList: modelList); + } + + Future> sendMessage( + {required String senderId, + required String recieverId, + required String? message, + required String proeprtyId, + MultipartFile? audio, + MultipartFile? attachment}) async { + Map parameters = { + "sender_id": senderId, + "receiver_id": recieverId, + "message": message, + "property_id": proeprtyId, + "file": attachment, + "audio": audio + }; + + if (attachment == null) { + parameters.remove("file"); + } + if (audio == null) { + parameters.remove("audio"); + } + Logger.error(parameters, name: "CHAT PARAMS"); + Map map = + await Api.post(url: Api.sendMessage, parameter: parameters); + return map; + } +} diff --git a/lib/data/Repositories/cities_repository.dart b/lib/data/Repositories/cities_repository.dart new file mode 100644 index 0000000..4fa3d47 --- /dev/null +++ b/lib/data/Repositories/cities_repository.dart @@ -0,0 +1,19 @@ +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/utils/api.dart'; + +import '../model/city_model.dart'; + +class CitiesRepository { + Future> fetchCitiesData() async { + try { + Map response = + await Api.get(url: Api.getCountByCitiesCategory, queryParameters: {}); + + List cities = response['city_data']; + List citiesList = cities.map((e) => City.fromMap(e)).toList(); + return DataOutput(total: citiesList.length, modelList: citiesList); + } catch (e, sr) { + throw sr; + } + } +} diff --git a/lib/data/Repositories/favourites_repository.dart b/lib/data/Repositories/favourites_repository.dart new file mode 100644 index 0000000..02d0883 --- /dev/null +++ b/lib/data/Repositories/favourites_repository.dart @@ -0,0 +1,39 @@ +import '../../utils/api.dart'; +import '../../utils/constant.dart'; +import '../model/data_output.dart'; +import '../model/property_model.dart'; + +class FavoriteRepository { + Future addToFavorite(int id, String type) async { + Map parameters = {Api.propertyId: id, Api.type: type}; + + await Api.post(url: Api.addFavourite, parameter: parameters); + } + + Future removeFavorite(int id) async { + Map parameters = { + Api.propertyId: id, + }; + + await Api.post(url: Api.removeFavorite, parameter: parameters); + } + + Future> fetchFavorites({required int offset}) async { + Map parameters = { + Api.offset: offset, + Api.limit: Constant.loadLimit + }; + + Map response = await Api.get( + url: Api.getFavoriteProperty, + queryParameters: parameters, + ); + + List modelList = (response['data'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(); + + return DataOutput( + total: response['total'] ?? 0, modelList: modelList); + } +} diff --git a/lib/data/Repositories/interest_repository.dart b/lib/data/Repositories/interest_repository.dart new file mode 100644 index 0000000..da1b195 --- /dev/null +++ b/lib/data/Repositories/interest_repository.dart @@ -0,0 +1,33 @@ +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/interested_user_model.dart'; + +import '../../utils/api.dart'; + +class InterestRepository { + ///this method will set if we are interested in any category when we click intereseted + Future setInterest( + {required String propertyId, required String interest}) async { + await Api.post(url: Api.interestedUsers, parameter: { + Api.type: interest, + Api.propertyId: propertyId, + }); + } + + Future> getInterestUser(String propertyId, + {required int offset}) async { + try { + Map response = + await Api.get(url: Api.getInterestedUsers, queryParameters: { + "property_id": propertyId, + }); + List interestedUserList = (response['data'] as List) + .map((e) => InterestedUserModel.fromJson(e)) + .toList(); + + return DataOutput( + total: response['total'] ?? 0, modelList: interestedUserList); + } catch (e) { + throw e; + } + } +} diff --git a/lib/data/Repositories/location_repository.dart b/lib/data/Repositories/location_repository.dart new file mode 100644 index 0000000..a6f84ae --- /dev/null +++ b/lib/data/Repositories/location_repository.dart @@ -0,0 +1,102 @@ +// ignore_for_file: file_names + +import 'package:dio/dio.dart'; +import 'package:ebroker/utils/constant.dart'; + +import '../../utils/api.dart'; +import '../model/google_place_model.dart'; + +class GooglePlaceRepository { + //This will search places from google place api + //We use this to search location while adding new property + Future> serchCities( + String text, + ) async { + try { + /// + ///************************ */ + Map queryParameters = { + Api.placeApiKey: Constant.googlePlaceAPIkey, + Api.input: text, + Api.type: "(cities)", + "language": "en" + }; + + ///************************ */ + + Map apiResponse = await Api.get( + url: Api.placeAPI, + useAuthToken: false, + useBaseUrl: false, + queryParameters: queryParameters, + ); + return _buildPlaceModelList(apiResponse); + } catch (e) { + if (e is DioError) {} + throw ApiException(e.toString()); + } + } + + ///this will convert normal response to List of models so we can use it easily in code + List _buildPlaceModelList( + Map apiResponse) { + ///loop throuh predictions list, + ///this will create List of GooglePlaceModel + try { + var filterdResult = (apiResponse["predictions"] as List).map((details) { + String name = details['description']; + String placeId = details['place_id']; + /////// + //// + String city = getLocationComponent(details, "locality"); + String country = getLocationComponent(details, "geocode"); + String state = getLocationComponent(details, "political"); + + /// + /// + GooglePlaceModel placeModel = GooglePlaceModel( + city: city, + description: name, + placeId: placeId, + state: state, + country: country, + latitude: '', + longitude: '', + ); + return placeModel; + }).toList(); + + return filterdResult; + } catch (e) { + rethrow; + } + } + + String getLocationComponent(Map details, String component) { + int index = (details['types'] as List) + .indexWhere((element) => element == component); + if ((details['terms'] as List).length > index) { + return (details['terms'] as List).elementAt(index)['value']; + } else { + return ""; + } + } + + ///Google Place Autocomplete api will give us Place Id. + ///We will use this place id to get Place Details + Future getPlaceDetailsFromPlaceId(String placeId) async { + Map queryParameters = { + Api.placeApiKey: Constant.googlePlaceAPIkey, + Api.placeid: placeId, + "language": "en" + }; + Map response = await Api.get( + url: Api.placeApiDetails, + queryParameters: queryParameters, + useBaseUrl: false, + useAuthToken: false, + ); + + return response['result']['geometry']['location']; + } +} diff --git a/lib/data/Repositories/map.dart b/lib/data/Repositories/map.dart new file mode 100644 index 0000000..16b7b5e --- /dev/null +++ b/lib/data/Repositories/map.dart @@ -0,0 +1,69 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:ebroker/utils/api.dart'; + +class GMap { + static Future> getNearByProperty( + String city, String state) async { + try { + Map response = await Api.get( + url: Api.getNearByProperties, + queryParameters: {"city": city, "state": state}, + useAuthToken: false); + response.mlog("City response"); + List points = (response['data'] as List).map((e) { + return MapPoint.fromMap(e); + }).toList(); + return points; + } catch (e) { + rethrow; + } + } +} + +class MapPoint { + final String price; + final String latitude; + final String longitude; + final int propertyId; + final String propertyType; + MapPoint({ + required this.price, + required this.latitude, + required this.longitude, + required this.propertyId, + required this.propertyType, + }); + + @override + String toString() { + return 'MapPoint(price: $price, latitude: $latitude, longitude: $longitude, propertyId: $propertyId, propertyType: $propertyType)'; + } + + Map toMap() { + return { + 'price': price, + 'latitude': latitude, + 'longitude': longitude, + 'id': propertyId, + 'property_type': propertyType, + }; + } + + factory MapPoint.fromMap(Map map) { + return MapPoint( + price: map['price'].toString(), + latitude: map['latitude'].toString(), + longitude: map['longitude'].toString(), + propertyId: map['id'] as int, + propertyType: map['property_type'].toString(), + ); + } + + String toJson() => json.encode(toMap()); + + factory MapPoint.fromJson(String source) => + MapPoint.fromMap(json.decode(source) as Map); +} diff --git a/lib/data/Repositories/notifications_repository_repository.dart b/lib/data/Repositories/notifications_repository_repository.dart new file mode 100644 index 0000000..089d457 --- /dev/null +++ b/lib/data/Repositories/notifications_repository_repository.dart @@ -0,0 +1,33 @@ +import '../../utils/api.dart'; +import '../../utils/constant.dart'; +import '../../utils/hive_utils.dart'; +import '../model/data_output.dart'; +import '../model/notification_data.dart'; + +class NotificationsRepository { + Future> fetchNotifications( + {required int offset}) async { + try { + Map parameters = { + Api.userid: HiveUtils.getUserId(), + Api.offset: offset, + Api.limit: Constant.loadLimit + }; + Map response = await Api.get( + url: Api.apiGetNotifications, + queryParameters: parameters, + ); + + List modelList = (response['data'] as List).map((e) { + return NotificationData.fromJson(e); + }).toList(); + + return DataOutput( + total: 0, + modelList: modelList, + ); + } catch (e) { + rethrow; + } + } +} diff --git a/lib/data/Repositories/outdoorfacility.dart b/lib/data/Repositories/outdoorfacility.dart new file mode 100644 index 0000000..d8f852a --- /dev/null +++ b/lib/data/Repositories/outdoorfacility.dart @@ -0,0 +1,16 @@ +import '../../utils/api.dart'; +import '../model/outdoor_facility.dart'; + +class OutdoorFacilityRepository { + Future> fetchOutdoorFacilityList() async { + Map result = + await Api.get(url: Api.getOutdoorFacilites, queryParameters: {}); + + List outdoorFacilities = + (result['data'] as List).map((element) { + return OutdoorFacility.fromJson(element); + }).toList(); + + return List.from(outdoorFacilities); + } +} diff --git a/lib/data/Repositories/personalized_feed_repository.dart b/lib/data/Repositories/personalized_feed_repository.dart new file mode 100644 index 0000000..1fd0ab2 --- /dev/null +++ b/lib/data/Repositories/personalized_feed_repository.dart @@ -0,0 +1,81 @@ +import 'package:ebroker/data/model/Personalized/personalized_settings.dart'; +import 'package:ebroker/utils/Extensions/lib/map.dart'; +import 'package:ebroker/utils/api.dart'; +import 'package:flutter/material.dart'; + +import '../../app/app.dart'; +import '../../utils/constant.dart'; +import '../model/data_output.dart'; +import '../model/property_model.dart'; + +enum PersonalizedFeedAction { add, edit, get } + +class PersonalizedFeedRepository { + Future addOrUpdate({ + required PersonalizedFeedAction action, + required List categoryIds, + List? outdoorFacilityList, + RangeValues? priceRange, + List? selectedPropertyType, + String? city, + }) async { + ////List to String + String categoryStringArray = categoryIds.join(","); + String outdoorFacilityStringArray = outdoorFacilityList?.join(",") ?? ""; + String priceRangeString = "${priceRange?.start},${priceRange?.end}"; + String propertyTypeString = selectedPropertyType?.join(",") ?? ""; + + Map parameters = { + "action": action.name, + "category_ids": categoryStringArray, + "outdoor_facilitiy_ids": outdoorFacilityStringArray, + "price_range": priceRangeString, + "property_type": propertyTypeString, + "city": city?.toLowerCase() + }; + parameters.removeEmptyKeys(); + + print("PARAMETER-- $parameters"); + Map result = + await Api.post(url: Api.addEditUserInterest, parameter: parameters); + + try { + personalizedInterestSettings = + PersonalizedInterestSettings.fromMap(result['data']); + } catch (e) {} + } + + Future getUserPersonalizedSettings() async { + try { + Map userPersonalization = await Api.post( + parameter: { + "action": "get", + }, + url: Api.addEditUserInterest, + ); + print("----------------- $userPersonalization"); + return PersonalizedInterestSettings.fromMap( + userPersonalization['data'], + ); + } catch (e) { + return PersonalizedInterestSettings.empty(); + } + } + + Future> getPersonalizedProeprties({ + required int offset, + }) async { + Map response = await Api.get( + url: Api.getUserRecommendation, + queryParameters: { + Api.offset: offset, + Api.limit: Constant.loadLimit, + }, + ); + + List modelList = (response['data'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(); + return DataOutput(total: response['total'] ?? 0, modelList: modelList); + } +} diff --git a/lib/data/Repositories/project_repository.dart b/lib/data/Repositories/project_repository.dart new file mode 100644 index 0000000..39d4637 --- /dev/null +++ b/lib/data/Repositories/project_repository.dart @@ -0,0 +1,99 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:ebroker/Ui/screens/widgets/adaptive_image_picker.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/api.dart'; + +import '../model/project_model.dart'; + +class ProjectRepository { + createProject(Map projectPayload) async { + try { + Map multipartedData = _multipartImages(projectPayload); + // multipartedData['image']=multipartedData['main_image']; + var images = projectPayload['gallery_images']; + multipartedData.remove("gallery_images"); + Map galleryImages = {}; + if (images != null) { + galleryImages = + ((images as MultiValue).value).fold({}, (previousValue, element) { + if (element.value is! String) { + previousValue.addAll({ + "gallery_images[${previousValue.length}]": + MultipartFile.fromFileSync((element.value as File).path) + }); + } + + return previousValue; + }); + } + + multipartedData.addAll(galleryImages); + print(galleryImages); + Map map = + await Api.post(url: Api.postProject, parameter: multipartedData); + print("PRINT MAP $map"); + return map; + } catch (e, st) { + print(":E $st"); + // throw e; + } + } + + Future> getMyProjects({required int offset}) async { + Map result = await Api.get( + url: Api.getProjects, + queryParameters: {"userid": HiveUtils.getUserId(), "offset": offset}); + List list = + (result['data'] as List).map((e) => ProjectModel.fromMap(e)).toList(); + + return DataOutput(total: result['total'] ?? 0, modelList: list); + } + + Future> getProjects({int? offset}) async { + Map result = await Api.get( + url: Api.getProjects, queryParameters: {"offset": offset}); + List list = + (result['data'] as List).map((e) => ProjectModel.fromMap(e)).toList(); + + return DataOutput(total: result['total'] ?? 0, modelList: list); + } + + Map _multipartImages(Map data) { + return data.map((key, value) { + if (value is FileValue) { + return MapEntry(key, MultipartFile.fromFileSync(value.value.path)); + } + if (value is MultiValue && key != "gallery_images") { + List images = value.value.map((image) { + if (image is FileValue) { + return MultipartFile.fromFileSync(image.value.path); + } + }).toList(); + return MapEntry(key, images); + } + if (value is List) { + List files = + value.map((e) => MultipartFile.fromFileSync(e.path)).toList(); + return MapEntry(key, files); + } + if (value is Map) { + var v = _multipartImages(value); + return MapEntry(key, v); + } + if (value is List) { + List list = value.map((e) { + if (e is Map) { + return _multipartImages(e); + } + return {}; + }).toList(); + return MapEntry(key, list); + } + + return MapEntry(key, value); + }); + } +} diff --git a/lib/data/Repositories/property_repository.dart b/lib/data/Repositories/property_repository.dart new file mode 100644 index 0000000..8f52866 --- /dev/null +++ b/lib/data/Repositories/property_repository.dart @@ -0,0 +1,366 @@ +import '../../utils/api.dart'; +import '../../utils/constant.dart'; +import '../../utils/hive_utils.dart'; +import '../model/data_output.dart'; +import '../model/property_model.dart'; + +class PropertyRepository { + ///This method will add property + Future createProperty({required Map parameters}) async { + var api = Api.apiPostProperty; + if (parameters['action_type'] == "0") { + api = Api.apiUpdateProperty; + + if (parameters.containsKey("gallery_images")) { + if ((parameters['gallery_images'] as List).isEmpty) { + parameters.remove("gallery_images"); + } + } + + if (parameters['title_image'] == null || + parameters['title_image'] == "") { + parameters.remove("title_image"); + } + if (parameters['meta_image'] == null || parameters['meta_image'] == "") { + parameters.remove("title_image"); + } + } + + return await Api.post(url: api, parameter: parameters); + } + + /// it will get all proerpties + Future> fetchProperty({required int offset}) async { + Map parameters = { + Api.offset: offset, + Api.limit: Constant.loadLimit, + "current_user": HiveUtils.getUserId() + }; + + Map response = + await Api.get(url: Api.apiGetProprty, queryParameters: parameters); + + List modelList = (response['data'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(); + + return DataOutput(total: response['total'] ?? 0, modelList: modelList); + } + + Future> fetchRecentProperties( + {required int offset}) async { + Map parameters = { + Api.offset: offset, + Api.limit: Constant.loadLimit, + "current_user": HiveUtils.getUserId() + }; + + Map response = + await Api.get(url: Api.apiGetProprty, queryParameters: parameters); + + List modelList = (response['data'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(); + + return DataOutput(total: response['total'] ?? 0, modelList: modelList); + } + + Future> fetchPropertyFromPropertyId( + dynamic id) async { + Map parameters = { + Api.id: id, + "current_user": HiveUtils.getUserId() + }; + + Map response = + await Api.get(url: Api.apiGetProprty, queryParameters: parameters); + + List modelList = (response['data'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(); + + return DataOutput(total: response['total'] ?? 0, modelList: modelList); + } + + Future deleteProperty(int id) async { + await Api.post( + url: Api.apiUpdateProperty, + parameter: {Api.id: id, Api.actionType: "1"}); + } + + Future> fetchTopRatedProperty() async { + Map parameters = { + Api.topRated: "1", + "current_user": HiveUtils.getUserId() + }; + + Map response = + await Api.get(url: Api.apiGetProprty, queryParameters: parameters); + + List modelList = (response['data'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(); + + return DataOutput(total: response['total'] ?? 0, modelList: modelList); + } + + ///fetch most viewed properties + Future> fetchMostViewedProperty( + {required int offset, required bool sendCityName}) async { + Map parameters = { + Api.topRated: "1", + Api.offset: offset, + Api.limit: Constant.loadLimit, + "current_user": HiveUtils.getUserId() + }; + try { + if (sendCityName) { + // if (HiveUtils.getCityName() != null) { + // if (!Constant.isDemoModeOn) { + // parameters['city'] = HiveUtils.getCityName(); + // } + // } + } + + Map response = await Api.get( + url: Api.apiGetProprty, + queryParameters: parameters, + ); + + List modelList = (response['data'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(); + return DataOutput(total: response['total'] ?? 0, modelList: modelList); + } catch (e) { + print("issue is $e"); + throw e; + } + } + + ///fetch advertised properties + Future> fetchPromotedProperty( + {required int offset, required bool sendCityName}) async { + /// + Map parameters = { + Api.promoted: true, + Api.offset: offset, + Api.limit: Constant.loadLimit, + "current_user": HiveUtils.getUserId() + }; + + Map response = await Api.get( + url: Api.apiGetProprty, + queryParameters: parameters, + ); + + List modelList = (response['data'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(); + + return DataOutput( + total: response['total'] ?? 0, + modelList: modelList, + ); + } + + Future> fetchNearByProperty( + {required int offset}) async { + try { + if (HiveUtils.getCityName() == null||HiveUtils.getCityName().toString().isEmpty) { + return Future.value(DataOutput( + total: 0, + modelList: [], + )); + } + Map result = await Api.get( + url: Api.apiGetProprty, + queryParameters: { + "city": HiveUtils.getCityName(), + Api.offset: offset, + "limit": Constant.loadLimit, + "current_user": HiveUtils.getUserId() + }, + ); + + List dataList = (result['data'] as List).map((e) { + return PropertyModel.fromMap(e); + }).toList(); + + return DataOutput( + total: result['total'] ?? 0, + modelList: dataList, + ); + } catch (e) { + rethrow; + } + } + + Future> fetchMostLikeProperty({ + required int offset, + required bool sendCityName, + }) async { + Map parameters = { + "most_liked": 1, + "limit": Constant.loadLimit, + "offset": offset, + "current_user": HiveUtils.getUserId() + }; + if (sendCityName) { + // if (HiveUtils.getCityName() != null) { + // if (!Constant.isDemoModeOn) { + // parameters['city'] = HiveUtils.getCityName(); + // } + // } + } + Map response = + await Api.get(url: Api.apiGetProprty, queryParameters: parameters); + + List modelList = (response['data'] as List).map((e) { + return PropertyModel.fromMap(e); + }).toList(); + return DataOutput(total: response['total'] ?? 0, modelList: modelList); + } + + Future> fetchMyPromotedProeprties( + {required int offset}) async { + Map parameters = { + "users_promoted": 1, + Api.offset: offset, + Api.limit: Constant.loadLimit, + "current_user": HiveUtils.getUserId() + }; + + Map response = + await Api.get(url: Api.apiGetProprty, queryParameters: parameters); + List modelList = (response['data'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(); + + return DataOutput(total: response['total'] ?? 0, modelList: modelList); + } + + ///Search proeprty + Future> searchProperty(String searchQuery, + {required int offset}) async { + Map parameters = { + Api.search: searchQuery, + Api.offset: offset, + Api.limit: Constant.loadLimit, + "current_user": HiveUtils.getUserId() + }; + + if (Constant.propertyFilter != null) { + parameters.addAll(Constant.propertyFilter!.toMap()); + } + + Map response = + await Api.get(url: Api.apiGetProprty, queryParameters: parameters); + + List modelList = (response['data'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(); + + return DataOutput(total: response['total'] ?? 0, modelList: modelList); + } + + ///to get my properties which i had added to sell or rent + Future> fetchMyProperties( + {required int offset, required String type}) async { + try { + String? propertyType = _findPropertyType(type.toLowerCase()); + + Map parameters = { + Api.offset: offset, + Api.limit: Constant.loadLimit, + Api.userid: HiveUtils.getUserId(), + Api.propertyType: propertyType, + "current_user": HiveUtils.getUserId() + }; + Map response = + await Api.get(url: Api.apiGetProprty, queryParameters: parameters); + List modelList = (response['data'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(); + + return DataOutput(total: response['total'] ?? 0, modelList: modelList); + } catch (e) { + throw e; + } + } + + String? _findPropertyType(String type) { + if (type == "sell") { + return "0"; + } else if (type == "rent") { + return "1"; + } + return null; + } + + Future> fetchProperyFromCategoryId( + {required int id, required int offset, bool? showPropertyType}) async { + Map parameters = { + Api.categoryId: id, + Api.offset: offset, + Api.limit: Constant.loadLimit, + "current_user": HiveUtils.getUserId() + }; + + if (Constant.propertyFilter != null) { + parameters.addAll(Constant.propertyFilter!.toMap()); + + if (Constant.propertyFilter?.categoryId == "") { + if (showPropertyType ?? true) { + parameters.remove(Api.categoryId); + } else { + parameters[Api.categoryId] = id; + } + } + } + + Map response = + await Api.get(url: Api.apiGetProprty, queryParameters: parameters); + + List modelList = (response['data'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(); + return DataOutput(total: response['total'] ?? 0, modelList: modelList); + } + + Future setProeprtyView(String propertyId) async { + await Api.post( + url: Api.setPropertyView, parameter: {Api.propertyId: propertyId}); + } + + Future updatePropertyStatus( + {required dynamic propertyId, required dynamic status}) async { + await Api.post( + url: Api.updatePropertyStatus, + parameter: {"status": status, "property_id": propertyId}); + } + + Future fetchBySlug(String slug) async { + Map result = await Api.get( + url: Api.apiGetProprty, queryParameters: {"slug_id": slug}); + + return PropertyModel.fromMap(result['data'][0]); + } + + Future> fetchPropertiesFromCityName( + String cityName, { + required int offset, + }) async { + Map response = + await Api.get(url: Api.apiGetProprty, queryParameters: { + "city": cityName, + Api.limit: Constant.loadLimit, + Api.offset: offset, + "current_user": HiveUtils.getUserId() + }); + + List modelList = (response['data'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(); + return DataOutput(total: response['total'] ?? 0, modelList: modelList); + } +} diff --git a/lib/data/Repositories/report_property_repository.dart b/lib/data/Repositories/report_property_repository.dart new file mode 100644 index 0000000..01fa815 --- /dev/null +++ b/lib/data/Repositories/report_property_repository.dart @@ -0,0 +1,30 @@ +import 'package:ebroker/data/model/ReportProperty/reason_model.dart'; +import 'package:ebroker/data/model/data_output.dart'; + +import '../../utils/api.dart'; + +class ReportPropertyRepository { + Future> fetchReportReasonsList() async { + try { + Map response = + await Api.get(url: Api.getReportReasons, queryParameters: {}); + + List list = (response['data'] as List).map((e) { + return ReportReason(id: e["id"], reason: e['reason']); + }).toList(); + + return DataOutput(total: response['total'], modelList: list); + } catch (e) { + rethrow; + } + } + + Future reportProperty( + {required int reasonId, required int propertyId, String? message}) async { + return await Api.post(url: Api.addReports, parameter: { + "reason_id": (reasonId == -10) ? 0 : reasonId, + "property_id": propertyId, + if (message != null) "other_message": message + }); + } +} diff --git a/lib/data/Repositories/subscription_repository.dart b/lib/data/Repositories/subscription_repository.dart new file mode 100644 index 0000000..bdb3cb3 --- /dev/null +++ b/lib/data/Repositories/subscription_repository.dart @@ -0,0 +1,75 @@ +import 'dart:io'; + +import '../../utils/api.dart'; +import '../../utils/hive_utils.dart'; +import '../model/data_output.dart'; +import '../model/subscription_pacakage_model.dart'; +import '../model/subscription_package_limit.dart'; + +enum SubscriptionLimitType { advertisement, property, isPremium } + +class SubscriptionRepository { + Future> getSubscriptionPackages({ + required int offset, + }) async { + Map response = + await Api.get(url: Api.getPackage, queryParameters: { + "platform": Platform.isIOS ? "ios" : "android", + "current_user": HiveUtils.getUserId() + }); + + List modelList = (response['data'] as List) + .map((element) => SubscriptionPackageModel.fromJson(element)) + .toList(); + + return DataOutput(total: modelList.length, modelList: modelList); + } + + Future getPackageLimit( + SubscriptionLimitType limitType) async { + Map response = await Api.get( + url: Api.getLimitsOfPackage, + queryParameters: {"package_type": limitType.name, "id" : 1}); + // sumber error data harusnya merespon bolean + return SubcriptionPackageLimit.fromMap(response); + } + + Future subscribeToPackage( + int packageId, bool isPackageAvailable) async { + try { + Map parameters = { + Api.packageId: packageId, + Api.userid: HiveUtils.getUserId(), + if (isPackageAvailable) 'flag': 1, + }; + + await Api.post( + url: Api.userPurchasePackage, + parameter: parameters, + ); + } catch (e) { + throw Exception(e.toString()); + } + } + + Future assignFreePackage(int packageId) async { + await Api.post( + url: Api.assignPackage, + parameter: {"package_id": packageId, "in_app": false}); + } + + Future assignPackage({ + required String packageId, + required String productId, + }) async { + try { + await Api.post(url: Api.assignPackage, parameter: { + "package_id": packageId, + "product_id": productId, + "in_app": true, + }); + } catch (e) { + throw "e:$e"; + } + } +} diff --git a/lib/data/Repositories/system_repository.dart b/lib/data/Repositories/system_repository.dart new file mode 100644 index 0000000..8b538e9 --- /dev/null +++ b/lib/data/Repositories/system_repository.dart @@ -0,0 +1,26 @@ +import '../../utils/api.dart'; +import '../../utils/hive_utils.dart'; + +class SystemRepository { + Future fetchSystemSettings({required bool isAnonymouse}) async { + Map parameters = {}; + + ///Passing user id here because we will hide sensitive details if there is no user id, + ///With user id we will get user subscription package details + if (!isAnonymouse) { + if (HiveUtils.getUserId() != null) { + print("UID IS ${HiveUtils.getUserId()}"); + parameters['user_id'] = HiveUtils.getUserId(); + } + } + // Stopwatch time = Stopwatch(); + // time.start(); + Map response = await Api.post( + url: Api.apiGetSystemSettings, + parameter: parameters, + useAuthToken: !isAnonymouse); + // time.stop(); + // print("SYSTEM SETTING TAKES ${time.elapsed.inSeconds}"); + return response; + } +} diff --git a/lib/data/Repositories/transaction.dart b/lib/data/Repositories/transaction.dart new file mode 100644 index 0000000..963d9d8 --- /dev/null +++ b/lib/data/Repositories/transaction.dart @@ -0,0 +1,19 @@ +import '../../utils/api.dart'; +import '../model/data_output.dart'; +import '../model/transaction_model.dart'; + +class TransactionRepository { + Future> fetchTransactions() async { + Map parameters = {}; + + Map response = + await Api.get(url: Api.getPaymentDetails, queryParameters: parameters); + + List transactionList = (response['data'] as List) + .map((e) => TransactionModel.fromMap(e)) + .toList(); + + return DataOutput( + total: transactionList.length, modelList: transactionList); + } +} diff --git a/lib/data/cubits/.DS_Store b/lib/data/cubits/.DS_Store new file mode 100644 index 0000000..6faae33 Binary files /dev/null and b/lib/data/cubits/.DS_Store differ diff --git a/lib/data/cubits/Interested/get_interested_user_cubit.dart b/lib/data/cubits/Interested/get_interested_user_cubit.dart new file mode 100644 index 0000000..13ffae3 --- /dev/null +++ b/lib/data/cubits/Interested/get_interested_user_cubit.dart @@ -0,0 +1,111 @@ +import 'package:ebroker/data/Repositories/interest_repository.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/interested_user_model.dart'; + +import '../../../exports/main_export.dart'; + +class GetInterestedUserState {} + +class GetInterestedUserInitial extends GetInterestedUserState {} + +class GetInterestedUserInProgress extends GetInterestedUserState {} + +class GetInterestedUserSuccess extends GetInterestedUserState { + final List list; + final int total; + final int offset; + final bool isLoadingMore; + final String propertyId; + final bool hasError; + + GetInterestedUserSuccess({ + required this.list, + required this.total, + required this.propertyId, + required this.offset, + required this.isLoadingMore, + required this.hasError, + }); + + GetInterestedUserSuccess copyWith( + {List? list, + int? total, + int? offset, + bool? isLoadingMore, + bool? hasError, + String? propertyId}) { + return GetInterestedUserSuccess( + list: list ?? this.list, + propertyId: propertyId ?? this.propertyId, + total: total ?? this.total, + offset: offset ?? this.offset, + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + hasError: hasError ?? this.hasError, + ); + } +} + +class GetInterestedUserFail extends GetInterestedUserState { + final dynamic error; + GetInterestedUserFail(this.error); +} + +class GetInterestedUserCubit extends Cubit { + GetInterestedUserCubit() : super(GetInterestedUserInitial()); + final InterestRepository _interestRepository = InterestRepository(); + void fetch(String propertyId) async { + try { + emit(GetInterestedUserInProgress()); + DataOutput result = + await _interestRepository.getInterestUser(propertyId, offset: 0); + + emit(GetInterestedUserSuccess( + hasError: false, + offset: 0, + total: result.total, + propertyId: propertyId, + isLoadingMore: false, + list: result.modelList)); + } catch (e) { + emit(GetInterestedUserFail(e)); + } + } + + fetchMore() async { + try { + if (state is GetInterestedUserSuccess) { + if ((state as GetInterestedUserSuccess).isLoadingMore) { + return; + } + emit((state as GetInterestedUserSuccess).copyWith(isLoadingMore: true)); + DataOutput result = + await _interestRepository.getInterestUser( + (state as GetInterestedUserSuccess).propertyId, + offset: (state as GetInterestedUserSuccess).list.length, + ); + + GetInterestedUserSuccess interestedUserList = + (state as GetInterestedUserSuccess); + interestedUserList.list.addAll(result.modelList); + emit(GetInterestedUserSuccess( + isLoadingMore: false, + propertyId: (state as GetInterestedUserSuccess).propertyId, + hasError: false, + list: interestedUserList.list, + offset: (state as GetInterestedUserSuccess).list.length, + total: result.total)); + } + } catch (e) { + emit((state as GetInterestedUserSuccess) + .copyWith(isLoadingMore: false, hasError: true)); + } + } + + bool hasMoreData() { + if (state is GetInterestedUserSuccess) { + return (state as GetInterestedUserSuccess).list.length < + (state as GetInterestedUserSuccess).total; + } + return false; + } +} diff --git a/lib/data/cubits/Personalized/add_update_personalized_interest.dart b/lib/data/cubits/Personalized/add_update_personalized_interest.dart new file mode 100644 index 0000000..b92d9a9 --- /dev/null +++ b/lib/data/cubits/Personalized/add_update_personalized_interest.dart @@ -0,0 +1,52 @@ +import 'package:ebroker/exports/main_export.dart'; +import 'package:flutter/material.dart'; + +import '../../Repositories/personalized_feed_repository.dart'; + +abstract class AddUpdatePersonalizedInterestState {} + +class AddUpdatePersonalizedInterestInitial + extends AddUpdatePersonalizedInterestState {} + +class AddUpdatePersonalizedInterestInProgress + extends AddUpdatePersonalizedInterestState {} + +class AddUpdatePersonalizedInterestSuccess + extends AddUpdatePersonalizedInterestState {} + +class AddUpdatePersonalizedInterestFail + extends AddUpdatePersonalizedInterestState { + final dynamic error; + + AddUpdatePersonalizedInterestFail(this.error); +} + +class AddUpdatePersonalizedInterest + extends Cubit { + AddUpdatePersonalizedInterest() + : super(AddUpdatePersonalizedInterestInitial()); + PersonalizedFeedRepository personalizedFeedRepository = + PersonalizedFeedRepository(); + void addOrUpdate({ + required PersonalizedFeedAction action, + required List categoryIds, + List? outdoorFacilityList, + RangeValues? priceRange, + List? selectedPropertyType, + String? city, + }) async { + try { + emit(AddUpdatePersonalizedInterestInProgress()); + await personalizedFeedRepository.addOrUpdate( + action: action, + categoryIds: categoryIds, + outdoorFacilityList: outdoorFacilityList, + priceRange: priceRange, + city: city, + selectedPropertyType: selectedPropertyType); + emit(AddUpdatePersonalizedInterestSuccess()); + } catch (e) { + emit(AddUpdatePersonalizedInterestFail(e)); + } + } +} diff --git a/lib/data/cubits/Personalized/fetch_personalized_properties.dart b/lib/data/cubits/Personalized/fetch_personalized_properties.dart new file mode 100644 index 0000000..f99972f --- /dev/null +++ b/lib/data/cubits/Personalized/fetch_personalized_properties.dart @@ -0,0 +1,240 @@ +import 'dart:developer'; + +import 'package:ebroker/data/Repositories/personalized_feed_repository.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../../Ui/screens/proprties/viewAll.dart'; +import '../../../settings.dart'; +import '../../../utils/Network/networkAvailability.dart'; +import '../../model/data_output.dart'; + +abstract class FetchPersonalizedPropertyListState {} + +class FetchPersonalizedPropertyInitial + extends FetchPersonalizedPropertyListState {} + +class FetchPersonalizedPropertyInProgress + extends FetchPersonalizedPropertyListState {} + +class FetchPersonalizedPropertySuccess + extends FetchPersonalizedPropertyListState + implements PropertySuccessStateWireframe { + @override + final bool isLoadingMore; + final bool loadingMoreError; + @override + final List properties; + final int offset; + final int total; + FetchPersonalizedPropertySuccess( + {required this.isLoadingMore, + required this.loadingMoreError, + required this.properties, + required this.offset, + required this.total}); + + @override + set isLoadingMore(bool _isLoadingMore) {} + + @override + set properties(List _properties) {} + + FetchPersonalizedPropertySuccess copyWith({ + bool? isLoadingMore, + bool? loadingMoreError, + List? properties, + int? offset, + int? total, + String? cityName, + }) { + return FetchPersonalizedPropertySuccess( + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + loadingMoreError: loadingMoreError ?? this.loadingMoreError, + properties: properties ?? this.properties, + offset: offset ?? this.offset, + total: total ?? this.total); + } + + Map toMap() { + return { + 'isLoadingMore': this.isLoadingMore, + 'loadingMoreError': this.loadingMoreError, + 'properties': properties.map((e) => e.toMap()).toList(), + 'offset': this.offset, + 'total': this.total, + }; + } + + factory FetchPersonalizedPropertySuccess.fromMap(Map map) { + return FetchPersonalizedPropertySuccess( + isLoadingMore: map['isLoadingMore'] as bool, + loadingMoreError: map['loadingMoreError'] as bool, + properties: (map['properties'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(), + offset: map['offset'] as int, + total: map['total'] as int, + ); + } +} + +class FetchPersonalizedPropertyFail extends FetchPersonalizedPropertyListState + implements PropertyErrorStateWireframe { + final dynamic error; + FetchPersonalizedPropertyFail(this.error); + + @override + set error(_error) {} +} + +class FetchPersonalizedPropertyList + extends Cubit + with HydratedMixin + implements PropertyCubitWireframe { + FetchPersonalizedPropertyList() : super(FetchPersonalizedPropertyInitial()); + final PersonalizedFeedRepository _personalizedFeedRepository = + PersonalizedFeedRepository(); + + @override + void fetch({bool? forceRefresh, bool? loadWithoutDelay}) async { + if (forceRefresh != true) { + if (state is FetchPersonalizedPropertySuccess) { + await Future.delayed(Duration( + seconds: loadWithoutDelay == true + ? 0 + : AppSettings.hiddenAPIProcessDelay)); + } else { + emit(FetchPersonalizedPropertyInProgress()); + } + } else { + emit(FetchPersonalizedPropertyInProgress()); + } + try { + if (forceRefresh == true) { + DataOutput result = await _personalizedFeedRepository + .getPersonalizedProeprties(offset: 0); + + emit(FetchPersonalizedPropertySuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total)); + } else { + if (state is! FetchPersonalizedPropertySuccess) { + DataOutput result = await _personalizedFeedRepository + .getPersonalizedProeprties(offset: 0); + + emit(FetchPersonalizedPropertySuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total)); + } else { + await CheckInternet.check( + onInternet: () async { + DataOutput result = + await _personalizedFeedRepository.getPersonalizedProeprties( + offset: 0); + + emit(FetchPersonalizedPropertySuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total)); + }, + onNoInternet: () { + emit( + FetchPersonalizedPropertySuccess( + total: (state as FetchPersonalizedPropertySuccess).total, + offset: (state as FetchPersonalizedPropertySuccess).offset, + isLoadingMore: (state as FetchPersonalizedPropertySuccess) + .isLoadingMore, + loadingMoreError: + (state as FetchPersonalizedPropertySuccess) + .loadingMoreError, + properties: + (state as FetchPersonalizedPropertySuccess).properties), + ); + }, + ); + } + } + } catch (e) { + emit(FetchPersonalizedPropertyFail(e as dynamic)); + } + } + + @override + void fetchMore() async { + try { + if (state is FetchPersonalizedPropertySuccess) { + if ((state as FetchPersonalizedPropertySuccess).isLoadingMore) { + return; + } + emit((state as FetchPersonalizedPropertySuccess) + .copyWith(isLoadingMore: true)); + DataOutput result = + await _personalizedFeedRepository.getPersonalizedProeprties( + offset: (state as FetchPersonalizedPropertySuccess).properties.length, + ); + + FetchPersonalizedPropertySuccess propertiesState = + (state as FetchPersonalizedPropertySuccess); + propertiesState.properties.addAll(result.modelList); + emit(FetchPersonalizedPropertySuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: propertiesState.properties, + offset: + (state as FetchPersonalizedPropertySuccess).properties.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchPersonalizedPropertySuccess) + .copyWith(isLoadingMore: false, loadingMoreError: true)); + } + } + + @override + bool hasMoreData() { + if (state is FetchPersonalizedPropertySuccess) { + return (state as FetchPersonalizedPropertySuccess).properties.length < + (state as FetchPersonalizedPropertySuccess).total; + } + return false; + } + + @override + FetchPersonalizedPropertyListState? fromJson(Map json) { + try { + if (json['cubit_state'] == "FetchPersonalizedPropertySuccess") { + FetchPersonalizedPropertySuccess fetchPersonalizedPropertySuccess = + FetchPersonalizedPropertySuccess.fromMap(json); + + return fetchPersonalizedPropertySuccess; + } + } catch (e, st) { + log("ERROR WHILE lOAD JSON TO MODEL $st"); + } + return null; + } + + @override + Map? toJson(FetchPersonalizedPropertyListState state) { + try { + if (state is FetchPersonalizedPropertySuccess) { + Map mapped = state.toMap(); + mapped['cubit_state'] = "FetchPersonalizedPropertySuccess"; + return mapped; + } + } catch (e) { + log("ISSUE ISSSS $e"); + } + + return null; + } +} diff --git a/lib/data/cubits/Report/fetch_property_report_reason_list.dart b/lib/data/cubits/Report/fetch_property_report_reason_list.dart new file mode 100644 index 0000000..9db6420 --- /dev/null +++ b/lib/data/cubits/Report/fetch_property_report_reason_list.dart @@ -0,0 +1,132 @@ +import 'dart:developer'; + +import 'package:ebroker/data/model/ReportProperty/reason_model.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../../settings.dart'; +import '../../Repositories/report_property_repository.dart'; + +abstract class FetchPropertyReportReasonsListState {} + +class FetchPropertyReportReasonsInitial + extends FetchPropertyReportReasonsListState {} + +class FetchPropertyReportReasonsInProgress + extends FetchPropertyReportReasonsListState {} + +class FetchPropertyReportReasonsSuccess + extends FetchPropertyReportReasonsListState { + final int total; + final List reasons; + + FetchPropertyReportReasonsSuccess( + {required this.total, required this.reasons}); + + Map toMap() { + return { + 'total': this.total, + 'reasons': this.reasons.map((e) => e.toMap()).toList(), + }; + } + + factory FetchPropertyReportReasonsSuccess.fromMap(Map map) { + return FetchPropertyReportReasonsSuccess( + total: map['total'] as int, + reasons: + (map['reasons'] as List).map((e) => ReportReason.fromMap(e)).toList(), + ); + } +} + +class FetchPropertyReportReasonsFailure + extends FetchPropertyReportReasonsListState { + final dynamic error; + + FetchPropertyReportReasonsFailure(this.error); +} + +class FetchPropertyReportReasonsListCubit + extends Cubit with HydratedMixin { + FetchPropertyReportReasonsListCubit() + : super(FetchPropertyReportReasonsInitial()); + ReportPropertyRepository _repository = ReportPropertyRepository(); + void fetch({bool? forceRefresh}) async { + try { + if (forceRefresh != true) { + if (state is FetchPropertyReportReasonsSuccess) { + // WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + await Future.delayed( + const Duration(seconds: AppSettings.hiddenAPIProcessDelay)); + // }); + } else { + emit(FetchPropertyReportReasonsInProgress()); + } + } else { + emit(FetchPropertyReportReasonsInProgress()); + } + + if (forceRefresh == true) { + DataOutput result = + await _repository.fetchReportReasonsList(); + + result.modelList.add(ReportReason(id: -10, reason: "Other")); + + emit(FetchPropertyReportReasonsSuccess( + reasons: result.modelList, + total: result.total, + )); + } else { + if (state is! FetchPropertyReportReasonsSuccess) { + DataOutput result = + await _repository.fetchReportReasonsList(); + + result.modelList.add(ReportReason(id: -10, reason: "Other")); + + emit(FetchPropertyReportReasonsSuccess( + reasons: result.modelList, + total: result.total, + )); + } + } + + // emit(FetchPropertyReportReasonsInProgress()); + } catch (e) { + log("REPORT REASON ERROR IS $e"); + emit(FetchPropertyReportReasonsFailure(e)); + } + } + + List? getList() { + if (state is FetchPropertyReportReasonsSuccess) { + return (state as FetchPropertyReportReasonsSuccess).reasons; + } + return null; + } + + @override + FetchPropertyReportReasonsListState? fromJson(Map json) { + try { + if (json['cubit_state'] == "FetchPropertyReportReasonsSuccess") { + FetchPropertyReportReasonsSuccess fetchPropertyReportReasonsSuccess = + FetchPropertyReportReasonsSuccess.fromMap(json); + + return fetchPropertyReportReasonsSuccess; + } + } catch (e) {} + return null; + } + + @override + Map? toJson(FetchPropertyReportReasonsListState state) { + try { + if (state is FetchPropertyReportReasonsSuccess) { + Map mapped = state.toMap(); + mapped['cubit_state'] = "FetchPropertyReportReasonsSuccess"; + return mapped; + } + } catch (e) {} + + return null; + } +} diff --git a/lib/data/cubits/Report/property_report_cubit.dart b/lib/data/cubits/Report/property_report_cubit.dart new file mode 100644 index 0000000..1736b7c --- /dev/null +++ b/lib/data/cubits/Report/property_report_cubit.dart @@ -0,0 +1,44 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../Repositories/report_property_repository.dart'; + +List reportedProperties = []; + +abstract class PropertyReportState {} + +class PropertyReportInitial extends PropertyReportState {} + +class PropertyReportInProgress extends PropertyReportState {} + +class PropertyReportInSuccess extends PropertyReportState { + final String responseMessage; + + PropertyReportInSuccess(this.responseMessage); +} + +class PropertyReportFaliure extends PropertyReportState { + final dynamic error; + + PropertyReportFaliure(this.error); +} + +class PropertyReportCubit extends Cubit { + PropertyReportCubit() : super(PropertyReportInitial()); + ReportPropertyRepository repository = ReportPropertyRepository(); + void report({ + required int property_id, + required int reason_id, + String? message, + }) async { + try { + emit(PropertyReportInProgress()); + Map result = await repository.reportProperty( + reasonId: reason_id, propertyId: property_id, message: message); + + reportedProperties.add(property_id); + emit(PropertyReportInSuccess(result['message'])); + } catch (e) { + emit(PropertyReportFaliure(e)); + } + } +} diff --git a/lib/data/cubits/Utility/dynamic_fields_cubit.dart b/lib/data/cubits/Utility/dynamic_fields_cubit.dart new file mode 100644 index 0000000..f218e50 --- /dev/null +++ b/lib/data/cubits/Utility/dynamic_fields_cubit.dart @@ -0,0 +1,9 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +class DynamicFieldsCubit extends Cubit { + DynamicFieldsCubit() : super(DynamicFieldsInitial()); +} + +class DynamicFieldsState {} + +class DynamicFieldsInitial extends DynamicFieldsState {} diff --git a/lib/data/cubits/Utility/fetch_transactions_cubit.dart b/lib/data/cubits/Utility/fetch_transactions_cubit.dart new file mode 100644 index 0000000..2a5c551 --- /dev/null +++ b/lib/data/cubits/Utility/fetch_transactions_cubit.dart @@ -0,0 +1,109 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../Repositories/transaction.dart'; +import '../../model/data_output.dart'; +import '../../model/transaction_model.dart'; + +abstract class FetchTransactionsState {} + +class FetchTransactionsInitial extends FetchTransactionsState {} + +class FetchTransactionsInProgress extends FetchTransactionsState {} + +class FetchTransactionsSuccess extends FetchTransactionsState { + final bool isLoadingMore; + final bool loadingMoreError; + final List transactionmodel; + final int offset; + final int total; + FetchTransactionsSuccess({ + required this.isLoadingMore, + required this.loadingMoreError, + required this.transactionmodel, + required this.offset, + required this.total, + }); + + FetchTransactionsSuccess copyWith({ + bool? isLoadingMore, + bool? loadingMoreError, + List? transactionmodel, + int? offset, + int? total, + }) { + return FetchTransactionsSuccess( + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + loadingMoreError: loadingMoreError ?? this.loadingMoreError, + transactionmodel: transactionmodel ?? this.transactionmodel, + offset: offset ?? this.offset, + total: total ?? this.total, + ); + } +} + +class FetchTransactionsFailure extends FetchTransactionsState { + final dynamic errorMessage; + FetchTransactionsFailure(this.errorMessage); +} + +class FetchTransactionsCubit extends Cubit { + FetchTransactionsCubit() : super(FetchTransactionsInitial()); + + final TransactionRepository _transactionRepository = TransactionRepository(); + + Future fetchTransactions() async { + try { + emit(FetchTransactionsInProgress()); + + DataOutput result = + await _transactionRepository.fetchTransactions(); + + emit(FetchTransactionsSuccess( + isLoadingMore: false, + loadingMoreError: false, + transactionmodel: result.modelList, + offset: 0, + total: result.total)); + } catch (e) { + if (!isClosed) { + emit(FetchTransactionsFailure(e)); + } + } + } + + Future fetchTransactionsMore() async { + try { + if (state is FetchTransactionsSuccess) { + if ((state as FetchTransactionsSuccess).isLoadingMore) { + return; + } + emit((state as FetchTransactionsSuccess).copyWith(isLoadingMore: true)); + DataOutput result = + await _transactionRepository.fetchTransactions( + // offset: (state as FetchTransactionsSuccess).offset.LIST.length, + ); + + FetchTransactionsSuccess transactionmodelState = + (state as FetchTransactionsSuccess); + transactionmodelState.transactionmodel.addAll(result.modelList); + emit(FetchTransactionsSuccess( + isLoadingMore: false, + loadingMoreError: false, + transactionmodel: transactionmodelState.transactionmodel, + offset: 0, //(state as FetchTransactionsSuccess).offset.LIST.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchTransactionsSuccess) + .copyWith(isLoadingMore: false, loadingMoreError: true)); + } + } + + bool hasMoreData() { + if (state is FetchTransactionsSuccess) { + // return (state as FetchTransactionsSuccess).offset.LIST.length < + // (state as FetchTransactionsSuccess).total; + } + return false; + } +} diff --git a/lib/data/cubits/Utility/google_place_autocomplate_cubit.dart b/lib/data/cubits/Utility/google_place_autocomplate_cubit.dart new file mode 100644 index 0000000..8667be6 --- /dev/null +++ b/lib/data/cubits/Utility/google_place_autocomplate_cubit.dart @@ -0,0 +1,53 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../Repositories/location_repository.dart'; +import '../../model/google_place_model.dart'; + +abstract class GooglePlaceAutocompleteState {} + +class GooglePlaceAutocompleteInitial extends GooglePlaceAutocompleteState {} + +class GooglePlaceAutocompleteInProgress extends GooglePlaceAutocompleteState {} + +class GooglePlaceAutocompleteSuccess extends GooglePlaceAutocompleteState { + List autocompleteResult; + + GooglePlaceAutocompleteSuccess(this.autocompleteResult); +} + +class GooglePlaceAutocompleteFail extends GooglePlaceAutocompleteState { + dynamic error; + + GooglePlaceAutocompleteFail(this.error); +} + +class GooglePlaceAutocompleteCubit extends Cubit { + GooglePlaceAutocompleteCubit() : super(GooglePlaceAutocompleteInitial()); + final GooglePlaceRepository _googlePlaceAutocomplete = + GooglePlaceRepository(); + + ///This method will search location from text, + ///We use it for search location + Future getLocationFromText({required String text}) async { + try { + emit(GooglePlaceAutocompleteInProgress()); + List googlePlaceAutocompleteResponse = + await _googlePlaceAutocomplete.serchCities(text); + emit(GooglePlaceAutocompleteSuccess(googlePlaceAutocompleteResponse)); + } catch (e) { + emit(GooglePlaceAutocompleteFail(e)); + rethrow; + } + } + + ///this will clear all data and set it to its initial state so, + ///When we don't need these all data we clear it. + void clearCubit() { + emit(GooglePlaceAutocompleteSuccess([])); + Future.delayed(const Duration(microseconds: 300), () { + emit(GooglePlaceAutocompleteInitial()); + }); + } +} diff --git a/lib/data/cubits/Utility/house_type_cubit.dart b/lib/data/cubits/Utility/house_type_cubit.dart new file mode 100644 index 0000000..44f683f --- /dev/null +++ b/lib/data/cubits/Utility/house_type_cubit.dart @@ -0,0 +1,75 @@ +import 'dart:convert'; + +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../utils/api.dart'; +import '../../helper/custom_exception.dart'; +import '../../../utils/helper_utils.dart'; +import '../../model/house_type.dart'; + +abstract class HouseTypeState {} + +class HouseTypeInitial extends HouseTypeState {} + +class HouseTypeFetchProgress extends HouseTypeState {} + +class HouseTypeFetchSuccess extends HouseTypeState { + List houseTypelist = []; + + HouseTypeFetchSuccess(this.houseTypelist); +} + +class ChangeSelectedHouseType extends HouseTypeState { + HouseType selectedHouseType; + + ChangeSelectedHouseType(this.selectedHouseType); +} + +class HouseTypeFetchFailure extends HouseTypeState { + final String errmsg; + HouseTypeFetchFailure(this.errmsg); +} + +class HouseTypeCubit extends Cubit { + HouseTypeCubit() : super(HouseTypeInitial()); + + void fetchHouseType(BuildContext context) { + emit(HouseTypeFetchProgress()); + fetchHouseTypeFromDb(context) + .then((value) => emit(HouseTypeFetchSuccess(value))) + .catchError((e) => emit(HouseTypeFetchFailure(e.toString()))); + } + + void changeSelectedHouseType(HouseType houseType) { + emit(ChangeSelectedHouseType(houseType)); + } + + Future> fetchHouseTypeFromDb(BuildContext context) async { + List housetypelist = []; + Map body = {}; + + var response = await HelperUtils.sendApiRequest( + Api.apiGetHouseType, body, true, context); + var getdata = json.decode(response); + if (getdata != null) { + if (!getdata[Api.error]) { + List list = getdata['data']; + + housetypelist = list.map((model) => HouseType.fromJson(model)).toList(); + } else { + throw CustomException(getdata[Api.message]); + } + } else { + Future.delayed( + Duration.zero, + () { + throw CustomException("nodatafound".translate(context)); + }, + ); + } + + return housetypelist; + } +} diff --git a/lib/data/cubits/Utility/like_properties.dart b/lib/data/cubits/Utility/like_properties.dart new file mode 100644 index 0000000..31629a5 --- /dev/null +++ b/lib/data/cubits/Utility/like_properties.dart @@ -0,0 +1,86 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +import 'package:flutter_bloc/flutter_bloc.dart'; + +class LikedPropertiesState { + final Set liked; + Set? removedLikes; + LikedPropertiesState({ + required this.liked, + this.removedLikes, + }); + + LikedPropertiesState copyWith({ + Set? liked, + }) { + return LikedPropertiesState( + liked: liked ?? this.liked, + ); + } + + Map toMap() { + return { + 'liked': liked.toList(), + }; + } + + factory LikedPropertiesState.fromMap(Map map) { + return LikedPropertiesState( + liked: Set.from( + (map['liked'] as Set), + )); + } + + String toJson() => json.encode(toMap()); + + factory LikedPropertiesState.fromJson(String source) => + LikedPropertiesState.fromMap(json.decode(source) as Map); + + @override + String toString() => 'LikedPropertiesState(liked: $liked)'; +} + +class LikedPropertiesCubit extends Cubit { + LikedPropertiesCubit() + : super(LikedPropertiesState(liked: {}, removedLikes: {})); + + void changeLike(dynamic id) { + bool isAvailable = state.liked.contains(id); + + if (isAvailable) { + state.liked.remove(id); + state.removedLikes?.add(id); + } else { + state.liked.add(id); + } + + emit(LikedPropertiesState( + liked: state.liked, removedLikes: state.removedLikes)); + } + + void add(id) { + state.liked.add(id); + emit(LikedPropertiesState( + liked: state.liked, removedLikes: state.removedLikes)); + } + + + void emptyCubit(){ + emit(LikedPropertiesState( + liked: {}, + removedLikes: {}, + )); + } + + void clear() { + state.liked.clear(); + state.removedLikes?.clear(); + emit(LikedPropertiesState(liked: {}, removedLikes: {})); + } + +//for locally , + Set? getRemovedLikes() { + return state.removedLikes; + } +} diff --git a/lib/data/cubits/Utility/proeprty_edit_global.dart b/lib/data/cubits/Utility/proeprty_edit_global.dart new file mode 100644 index 0000000..719f6e0 --- /dev/null +++ b/lib/data/cubits/Utility/proeprty_edit_global.dart @@ -0,0 +1,30 @@ +import 'package:ebroker/data/model/property_model.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class PropertyEditGlobal { + final List list; + + PropertyEditGlobal(this.list); +} + +class PropertyEditCubit extends Cubit { + PropertyEditCubit() : super(PropertyEditGlobal([])); + + void add(PropertyModel model) { + var list = state.list; + int indexOfElemeent = list.indexWhere((element) => element.id == model.id); + if (indexOfElemeent != -1) list.removeAt(indexOfElemeent); + + list.add(model); + emit(PropertyEditGlobal(list)); + } + + PropertyModel get(PropertyModel model) { + return state.list.firstWhere((element) => element.id == model.id, + orElse: () { + return model; + }); + } + + void remove() {} +} diff --git a/lib/data/cubits/auth/auth_cubit.dart b/lib/data/cubits/auth/auth_cubit.dart new file mode 100644 index 0000000..ce18af9 --- /dev/null +++ b/lib/data/cubits/auth/auth_cubit.dart @@ -0,0 +1,147 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../utils/api.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/hive_utils.dart'; +import '../../helper/custom_exception.dart'; + +abstract class AuthState {} + +class AuthInitial extends AuthState {} + +class AuthProgress extends AuthState {} + +class Unauthenticated extends AuthState {} + +class Authenticated extends AuthState { + bool isAuthenticated = false; + + Authenticated(this.isAuthenticated); +} + +class AuthFailure extends AuthState { + final String errorMessage; + + AuthFailure(this.errorMessage); +} + +class AuthCubit extends Cubit { + //late String name, email, profile, address; + AuthCubit() : super(AuthInitial()) { + // checkIsAuthenticated(); + } + void checkIsAuthenticated() { + if (HiveUtils.isUserAuthenticated()) { + //setUserData(); + emit(Authenticated(true)); + } else { + emit(Unauthenticated()); + } + } + + Future updateFCM(BuildContext context) async { + try { + String? token = await FirebaseMessaging.instance.getToken(); + await Api.post( + url: Api.apiUpdateProfile, + parameter: { + Api.userid: HiveUtils.getUserId(), + "fcm_id": token, + }, + ); + } catch (e) {} + } + + Future> updateUserData(BuildContext context, + {String? name, + String? email, + String? address, + File? fileUserimg, + String? fcmToken, + String? notification, + double? latitude, + double? longitude, + String? city, + String? state, + String? country}) async { + Map parameters = { + Api.name: name ?? '', + Api.email: email ?? '', + Api.address: address ?? '', + Api.fcmId: fcmToken ?? '', + Api.userid: HiveUtils.getUserId(), + Api.notification: notification, + "city": city ?? HiveUtils.getCityName(), + "state": state ?? HiveUtils.getStateName(), + "country": country ?? HiveUtils.getCountryName(), + }; + if (fileUserimg != null) { + parameters['profile'] = await MultipartFile.fromFile(fileUserimg.path); + } + + if (latitude != null && longitude != null) { + parameters.addAll({"latitude": latitude, "longitude": longitude}); + } + print("I AM DATA ${parameters}"); + + var response = + await Api.post(url: Api.apiUpdateProfile, parameter: parameters); + + if (!response[Api.error]) { + HiveUtils.setUserData(response['data']); + checkIsAuthenticated(); + } else { + throw CustomException(response[Api.message]); + } + return response; + } + + void getUserById( + BuildContext context, + ) async { + Map body = {Api.userid: HiveUtils.getUserId().toString()}; + + var response = await HelperUtils.sendApiRequest( + Api.apigetUserbyId, body, false, context); + + Future.delayed( + Duration.zero, + () async { + response = await HelperUtils.sendApiRequest( + Api.apiUpdateProfile, body, true, context); + }, + ); + + var getdata = json.decode(response); + + if (getdata != null) { + if (!getdata[Api.error]) { + // Constant.session.setUserData(getdata['data'], ""); + checkIsAuthenticated(); + } else { + throw CustomException(getdata[Api.message]); + } + } else { + Future.delayed( + Duration.zero, + () { + throw CustomException("nodatafound".translate(context)); + }, + ); + } + } + + void signOut(BuildContext context) async { + if ((state as Authenticated).isAuthenticated) { + HiveUtils.logoutUser(context, onLogout: () {}); + emit(Unauthenticated()); + } + } +} diff --git a/lib/data/cubits/auth/auth_state_cubit.dart b/lib/data/cubits/auth/auth_state_cubit.dart new file mode 100644 index 0000000..396f9f8 --- /dev/null +++ b/lib/data/cubits/auth/auth_state_cubit.dart @@ -0,0 +1,30 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../utils/hive_utils.dart'; + +enum AuthenticationState { initial, authenticated, unAuthenticated, firstTime } + +class AuthenticationCubit extends Cubit { + AuthenticationCubit() : super(AuthenticationState.initial) { + _checkIfAuthenticated(); + } + + void _checkIfAuthenticated() { + bool userAuthenticated = HiveUtils.isUserAuthenticated(); + + if (userAuthenticated) { + emit(AuthenticationState.authenticated); + } else { + //When user installs app for first time then this firstTime state will be emmited. + if (HiveUtils.isUserFirstTime()) { + emit(AuthenticationState.firstTime); + } else { + emit(AuthenticationState.unAuthenticated); + } + } + } + + void setUnAuthenticated() { + emit(AuthenticationState.unAuthenticated); + } +} diff --git a/lib/data/cubits/auth/login_cubit.dart b/lib/data/cubits/auth/login_cubit.dart new file mode 100644 index 0000000..618f110 --- /dev/null +++ b/lib/data/cubits/auth/login_cubit.dart @@ -0,0 +1,63 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'package:ebroker/data/Repositories/auth_repository.dart'; +import 'package:ebroker/utils/hive_utils.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class LoginState {} + +class LoginInitial extends LoginState {} + +class LoginInProgress extends LoginState {} + +class LoginSuccess extends LoginState { + final bool isProfileCompleted; + LoginSuccess({ + required this.isProfileCompleted, + }); +} + +class LoginFailure extends LoginState { + final String errorMessage; + LoginFailure(this.errorMessage); +} + +class LoginCubit extends Cubit { + LoginCubit() : super(LoginInitial()); + + final AuthRepository _authRepository = AuthRepository(); + bool isProfileIsCompleted = false; + + void login( + {required String phoneNumber, + required String fireabseUserId, + required countryCode}) async { + try { + emit(LoginInProgress()); + Map result = await _authRepository.loginWithApi( + phone: phoneNumber, + uid: fireabseUserId, + ); + + ///Storing data to local database {HIVE} + HiveUtils.setJWT(result['token']); + + if (result['data']['name'] == "" && result['data']['email'] == "") { + HiveUtils.setProfileNotCompleted(); + isProfileIsCompleted = false; + var data = result['data']; + data['countryCode'] = countryCode; + HiveUtils.setUserData(data); + } else { + isProfileIsCompleted = true; + var data = result['data']; + data['countryCode'] = countryCode; + HiveUtils.setUserData(data); + } + + emit(LoginSuccess(isProfileCompleted: isProfileIsCompleted)); + } catch (e) { + emit(LoginFailure(e.toString())); + } + } +} diff --git a/lib/data/cubits/auth/send_otp_cubit.dart b/lib/data/cubits/auth/send_otp_cubit.dart new file mode 100644 index 0000000..30e0445 --- /dev/null +++ b/lib/data/cubits/auth/send_otp_cubit.dart @@ -0,0 +1,85 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'package:ebroker/Ui/screens/home/home_screen.dart'; +import 'package:ebroker/data/Repositories/auth_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import '../../../settings.dart'; + +Future> fetchUrl(String url) async { + try { + final response = await http.get(Uri.parse(url)); + if (response.statusCode == 200) { + // Successful response + Map data = json.decode(response.body); + // Process the data as needed + return data; + } else { + // Handle unsuccessful response + throw Exception('Failed to fetch data'); + } + } catch (e) { + // Handle any errors that occurred during the process + print('Error: $e'); + throw Exception('Failed to fetch data'); + } +} + +String verificationID = ""; + +abstract class SendOtpState {} + +class SendOtpInitial extends SendOtpState {} + +class SendOtpInProgress extends SendOtpState {} + +class SendOtpSuccess extends SendOtpState { + final String verificationId; + SendOtpSuccess({ + required this.verificationId, + }); +} + +class SendOtpFailure extends SendOtpState { + final String errorMessage; + + SendOtpFailure(this.errorMessage); +} + +class SendOtpCubit extends Cubit { + SendOtpCubit() : super(SendOtpInitial()); + + final AuthRepository _authRepository = AuthRepository(); + void sendOTP({required String phoneNumber}) async { + + try { + AppSettings.appNumber = phoneNumber.replaceAll('+', ''); + Map dataS = await fetchUrl("${AppSettings.apiUrl}token/requesttiga/${phoneNumber.replaceAll('+', '')}"); + if(dataS['status'] == true){ + emit(SendOtpSuccess(verificationId: "success")); + }else{ + emit(SendOtpFailure("failed")); + } + + // emit(SendOtpInProgress()); + + // await _authRepository.sendOTP( + // phoneNumber: phoneNumber, + // onCodeSent: (verificationId) { + // verificationID = verificationId; + // emit(SendOtpSuccess(verificationId: verificationId)); + // }, + // onError: (e) { + // emit(SendOtpFailure(e.toString())); + // }, + // ); + } catch (e) { + emit(SendOtpFailure(e.toString())); + } + } + + void setToInitial() { + emit(SendOtpInitial()); + } +} diff --git a/lib/data/cubits/auth/verify_otp_cubit.dart b/lib/data/cubits/auth/verify_otp_cubit.dart new file mode 100644 index 0000000..6b222b7 --- /dev/null +++ b/lib/data/cubits/auth/verify_otp_cubit.dart @@ -0,0 +1,71 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'dart:developer'; + +import 'package:ebroker/data/Repositories/auth_repository.dart'; +import 'package:ebroker/utils/errorFilter.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + + +abstract class VerifyOtpState {} + + +class VerifyOtpInitial extends VerifyOtpState {} + +class VerifyOtpInProgress extends VerifyOtpState {} + +class VerifyOtpSuccess extends VerifyOtpState { + final UserCredential credential; + VerifyOtpSuccess({ + required this.credential + }); +} + +class VerifyOtpFailure extends VerifyOtpState { + final String errorMessage; + + VerifyOtpFailure(this.errorMessage); +} + +class VerifyOtpCubit extends Cubit { + final AuthRepository _authRepository = AuthRepository(); + + VerifyOtpCubit() : super(VerifyOtpInitial()); + + Future verifyOTP( + {required String verificationId, required String otp}) async { + try { + emit(VerifyOtpInProgress()); + UserCredential userCredential = await _authRepository.verifyOTP( + otpVerificationId: verificationId, otp: otp); + emit(VerifyOtpSuccess(credential: userCredential)); + } on FirebaseAuthException catch (e) { + emit(VerifyOtpFailure(ErrorFilter.check(e.code).error)); + } catch (e, st) { + log("ISSUEEE $e $st"); + emit(VerifyOtpFailure(e.toString())); + } + } + /* + Future verifyOTP( + {required String verificationId, required String otp}) async { + try { + emit(VerifyOtpInProgress()); + + UserCredential userCredential = await _authRepository.verifyOTP( + otpVerificationId: verificationId, otp: otp); + + emit(VerifyOtpSuccess(credential: userCredential)); + } on FirebaseAuthException catch (e) { + emit(VerifyOtpFailure(ErrorFilter.check(e.code).error)); + } catch (e, st) { + log("ISSUEEE $e $st"); + emit(VerifyOtpFailure(e.toString())); + } + } */ + + void setInitialState() { + emit(VerifyOtpInitial()); + } +} diff --git a/lib/data/cubits/category/fetch_category_cubit.dart b/lib/data/cubits/category/fetch_category_cubit.dart new file mode 100644 index 0000000..871b409 --- /dev/null +++ b/lib/data/cubits/category/fetch_category_cubit.dart @@ -0,0 +1,258 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'dart:convert'; +import 'dart:developer'; + +import 'package:ebroker/data/Repositories/category_repository.dart'; +import 'package:ebroker/data/model/category.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/utils/helper_utils.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../../settings.dart'; +import '../../../utils/Network/networkAvailability.dart'; + +abstract class FetchCategoryState {} + +class FetchCategoryInitial extends FetchCategoryState {} + +class FetchCategoryInProgress extends FetchCategoryState {} + +class FetchCategorySuccess extends FetchCategoryState { + final int total; + final int offset; + final bool isLoadingMore; + final bool hasError; + final List categories; + FetchCategorySuccess({ + required this.total, + required this.offset, + required this.isLoadingMore, + required this.hasError, + required this.categories, + }); + + FetchCategorySuccess copyWith({ + int? total, + int? offset, + bool? isLoadingMore, + bool? hasError, + List? categories, + }) { + return FetchCategorySuccess( + total: total ?? this.total, + offset: offset ?? this.offset, + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + hasError: hasError ?? this.hasError, + categories: categories ?? this.categories, + ); + } + + Map toMap() { + return { + 'total': total, + 'offset': offset, + 'isLoadingMore': isLoadingMore, + 'hasError': hasError, + 'categories': categories.map((x) => x.toMap()).toList(), + }; + } + + factory FetchCategorySuccess.fromMap(Map map) { + return FetchCategorySuccess( + total: map['total'] as int, + offset: map['offset'] as int, + isLoadingMore: map['isLoadingMore'] as bool, + hasError: map['hasError'] as bool, + categories: List.from( + (map['categories']).map( + (x) => Category.fromMap(x as Map), + ), + ), + ); + } + + String toJson() => json.encode(toMap()); + + factory FetchCategorySuccess.fromJson(String source) => + FetchCategorySuccess.fromMap(json.decode(source) as Map); + + @override + String toString() { + return 'FetchCategorySuccess(total: $total, offset: $offset, isLoadingMore: $isLoadingMore, hasError: $hasError, categories: $categories)'; + } +} + +class FetchCategoryFailure extends FetchCategoryState { + final String errorMessage; + + FetchCategoryFailure(this.errorMessage); +} + +class FetchCategoryCubit extends Cubit with HydratedMixin { + FetchCategoryCubit() : super(FetchCategoryInitial()); + + final CategoryRepository _categoryRepository = CategoryRepository(); + + Future fetchCategories( + {bool? forceRefresh, bool? loadWithoutDelay}) async { + try { + if (forceRefresh != true) { + if (state is FetchCategorySuccess) { + await Future.delayed(Duration( + seconds: loadWithoutDelay == true + ? 0 + : AppSettings.hiddenAPIProcessDelay)); + } else { + emit(FetchCategoryInProgress()); + } + } else { + emit(FetchCategoryInProgress()); + } + + if (forceRefresh == true) { + DataOutput categories = + await _categoryRepository.fetchCategories(offset: 0); + + List list = + categories.modelList.map((element) => element.image!).toList(); + await HelperUtils.precacheSVG(list); + + log("CATEGORIES P${categories.modelList}"); + emit(FetchCategorySuccess( + total: categories.total, + categories: categories.modelList, + offset: 0, + hasError: false, + isLoadingMore: false)); + } else { + if (state is! FetchCategorySuccess) { + DataOutput categories = + await _categoryRepository.fetchCategories(offset: 0); + + List list = + categories.modelList.map((element) => element.image!).toList(); + await HelperUtils.precacheSVG(list); + + emit(FetchCategorySuccess( + total: categories.total, + categories: categories.modelList, + offset: 0, + hasError: false, + isLoadingMore: false)); + } else { + await CheckInternet.check( + onInternet: () async { + DataOutput categories = + await _categoryRepository.fetchCategories(offset: 0); + + List list = categories.modelList + .map((element) => element.image!) + .toList(); + await HelperUtils.precacheSVG(list); + + emit(FetchCategorySuccess( + total: categories.total, + categories: categories.modelList, + offset: 0, + hasError: false, + isLoadingMore: false)); + }, + onNoInternet: () { + emit(FetchCategorySuccess( + total: (state as FetchCategorySuccess).total, + offset: (state as FetchCategorySuccess).offset, + isLoadingMore: (state as FetchCategorySuccess).isLoadingMore, + hasError: (state as FetchCategorySuccess).hasError, + categories: (state as FetchCategorySuccess).categories)); + }, + ); + } + } + } catch (e) { + emit(FetchCategoryFailure(e.toString())); + } + } + + Future get(int id) async { + try { + DataOutput dataOutput = + await _categoryRepository.fetchCategories(offset: 0, id: id); + return dataOutput.modelList.first; + } catch (e) { + throw e; + } + } + + List getCategories() { + if (state is FetchCategorySuccess) { + return (state as FetchCategorySuccess).categories; + } + + return []; + } + + Future fetchCategoriesMore() async { + try { + if (state is FetchCategorySuccess) { + if ((state as FetchCategorySuccess).isLoadingMore) { + return; + } + emit((state as FetchCategorySuccess).copyWith(isLoadingMore: true)); + DataOutput result = await _categoryRepository.fetchCategories( + offset: (state as FetchCategorySuccess).categories.length, + ); + + FetchCategorySuccess categoryState = (state as FetchCategorySuccess); + categoryState.categories.addAll(result.modelList); + + List list = + categoryState.categories.map((e) => e.image!).toList(); + await HelperUtils.precacheSVG(list); + + emit(FetchCategorySuccess( + isLoadingMore: false, + hasError: false, + categories: categoryState.categories, + offset: (state as FetchCategorySuccess).categories.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchCategorySuccess) + .copyWith(isLoadingMore: false, hasError: true)); + } + } + + bool hasMoreData() { + if (state is FetchCategorySuccess) { + return (state as FetchCategorySuccess).categories.length < + (state as FetchCategorySuccess).total; + } + return false; + } + + @override + FetchCategoryState? fromJson(Map json) { + try { + var state = json['cubit_state']; + + if (state == "FetchCategorySuccess") { + return FetchCategorySuccess.fromMap(json); + } + } catch (e) {} + return null; + } + + @override + Map? toJson(FetchCategoryState state) { + if (state is FetchCategorySuccess) { + Map mapped = state.toMap(); + + mapped['cubit_state'] = "FetchCategorySuccess"; + + return mapped; + } + + return null; + } +} diff --git a/lib/data/cubits/category/fetch_cities_category.dart b/lib/data/cubits/category/fetch_cities_category.dart new file mode 100644 index 0000000..4efe35f --- /dev/null +++ b/lib/data/cubits/category/fetch_cities_category.dart @@ -0,0 +1,141 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:ebroker/data/Repositories/cities_repository.dart'; +import 'package:ebroker/data/model/city_model.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/utils/Extensions/lib/list.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../../settings.dart'; +import '../../../utils/Network/networkAvailability.dart'; + +abstract class FetchCityCategoryState {} + +class FetchCityCategoryInitial extends FetchCityCategoryState {} + +class FetchCityCategoryInProgress extends FetchCityCategoryState {} + +class FetchCityCategorySuccess extends FetchCityCategoryState { + final List cities; + final int total; + FetchCityCategorySuccess({ + required this.cities, + required this.total, + }); + + Map toMap() { + return { + 'cities': cities.map((e) => e.toMap()).toList(), + 'total': total, + }; + } + + factory FetchCityCategorySuccess.fromMap(Map map) { + return FetchCityCategorySuccess( + cities: (map['cities'] as List) + .map( + (e) => City.fromMap(e), + ) + .toList(), + total: map['total'] as int, + ); + } +} + +class FetchCityCategoryFail extends FetchCityCategoryState { + final dynamic error; + + FetchCityCategoryFail(this.error); +} + +class FetchCityCategoryCubit extends Cubit + with HydratedMixin { + FetchCityCategoryCubit() : super(FetchCityCategoryInitial()); + final CitiesRepository _citiesRepository = CitiesRepository(); + void fetchCityCategory({bool? forceRefresh, bool? loadWithoutDelay}) async { + try { + if (forceRefresh != true) { + if (state is FetchCityCategorySuccess) { + // WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + await Future.delayed(Duration( + seconds: loadWithoutDelay == true + ? 0 + : AppSettings.hiddenAPIProcessDelay)); + // }); + } else { + emit(FetchCityCategoryInProgress()); + } + } else { + emit(FetchCityCategoryInProgress()); + } + + // emit(FetchCityCategoryInProgress()); + + if (forceRefresh == true) { + DataOutput result = await _citiesRepository.fetchCitiesData(); + + emit(FetchCityCategorySuccess( + cities: result.modelList, total: result.total)); + } else { + if (state is! FetchCityCategorySuccess) { + DataOutput result = await _citiesRepository.fetchCitiesData(); + + emit(FetchCityCategorySuccess( + cities: result.modelList, total: result.total)); + } else { + await CheckInternet.check( + onInternet: () async { + DataOutput result = + await _citiesRepository.fetchCitiesData(); + + emit(FetchCityCategorySuccess( + cities: result.modelList, total: result.total)); + }, + onNoInternet: () { + emit(FetchCityCategorySuccess( + cities: (state as FetchCityCategorySuccess).cities, + total: (state as FetchCityCategorySuccess).total)); + }, + ); + } + } + } catch (error) { + emit(FetchCityCategoryFail(error)); + } + } + + dynamic getCount() { + if (state is FetchCityCategorySuccess) { + return (state as FetchCityCategorySuccess).cities.sum((e) => e.count); + } else { + return "--"; + } + } + + @override + FetchCityCategoryState? fromJson(Map json) { + try { + if (json['cubit_state'] == "FetchCityCategorySuccess") { + FetchCityCategorySuccess fetchCityCategory = + FetchCityCategorySuccess.fromMap(json); + + return fetchCityCategory; + } + } catch (e) { + // Optionally handle the exception + } + return null; // Ensure a value is always returned, even if it's null + } + + @override + Map? toJson(FetchCityCategoryState state) { + try { + if (state is FetchCityCategorySuccess) { + Map mapped = state.toMap(); + mapped['cubit_state'] = "FetchCityCategorySuccess"; + return mapped; + } + } catch (e) {} + + return null; + } +} diff --git a/lib/data/cubits/chatCubits/delete_message_cubit.dart b/lib/data/cubits/chatCubits/delete_message_cubit.dart new file mode 100644 index 0000000..4fc9f9e --- /dev/null +++ b/lib/data/cubits/chatCubits/delete_message_cubit.dart @@ -0,0 +1,42 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../utils/api.dart'; + +class DeleteMessageState {} + +class DeleteMessageInitial extends DeleteMessageState {} + +class DeleteMessageInProgress extends DeleteMessageState {} + +class DeleteMessageSuccess extends DeleteMessageState { + final int id; + DeleteMessageSuccess({ + required this.id, + }); +} + +class DeleteMessageFail extends DeleteMessageState { + dynamic error; + DeleteMessageFail({ + required this.error, + }); +} + +class DeleteMessageCubit extends Cubit { + DeleteMessageCubit() : super(DeleteMessageInitial()); + + void delete(int id, {required int receiverId}) async { + try { + emit(DeleteMessageInProgress()); + await Api.post(url: Api.deleteChatMessage, parameter: { + "message_id": id, + "receiver_id": receiverId, + }); + + emit(DeleteMessageSuccess(id: id)); + } catch (e) { + emit(DeleteMessageFail(error: e.toString())); + } + } +} diff --git a/lib/data/cubits/chatCubits/get_chat_users.dart b/lib/data/cubits/chatCubits/get_chat_users.dart new file mode 100644 index 0000000..f185ac0 --- /dev/null +++ b/lib/data/cubits/chatCubits/get_chat_users.dart @@ -0,0 +1,172 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'package:ebroker/data/Repositories/chat_repository.dart'; +import 'package:ebroker/data/model/chat/chated_user_model.dart'; +import 'package:flutter/material.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../model/data_output.dart'; + +abstract class GetChatListState {} + +class GetChatListInitial extends GetChatListState {} + +class GetChatListInProgress extends GetChatListState {} + +class GetChatListInternalProcess extends GetChatListState {} + +class GetChatListSuccess extends GetChatListState { + final int total; + final int currentPage; + final bool isLoadingMore; + final bool hasError; + final List chatedUserList; + GetChatListSuccess({ + required this.total, + required this.currentPage, + required this.isLoadingMore, + required this.hasError, + required this.chatedUserList, + }); + + GetChatListSuccess copyWith({ + int? total, + int? currentPage, + bool? isLoadingMore, + bool? hasError, + List? chatedUserList, + }) { + return GetChatListSuccess( + total: total ?? this.total, + currentPage: currentPage ?? this.currentPage, + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + hasError: hasError ?? this.hasError, + chatedUserList: chatedUserList ?? this.chatedUserList, + ); + } + + Map toMap() { + return { + 'total': total, + 'currentPage': currentPage, + 'isLoadingMore': isLoadingMore, + 'hasError': hasError, + 'chatedUserList': chatedUserList.map((x) => x.toJson()).toList(), + }; + } + + factory GetChatListSuccess.fromMap(Map map) { + return GetChatListSuccess( + total: map['total'] as int, + currentPage: map['currentPage'] as int, + isLoadingMore: map['isLoadingMore'] as bool, + hasError: map['hasError'] as bool, + chatedUserList: List.from( + (map['chatedUserList'] as List).map( + (x) => ChatedUser.fromJson(x as Map), + ), + ), + ); + } +} + +class GetChatListFailed extends GetChatListState { + final dynamic error; + + GetChatListFailed(this.error); +} + +class GetChatListCubit extends Cubit with HydratedMixin { + GetChatListCubit() : super(GetChatListInitial()); + final ChatRepostiory _chatRepostiory = ChatRepostiory(); + + ///Setting build context for later use + void setContext(BuildContext context) { + _chatRepostiory.setContext(context); + } + + void fetch() async { + try { + emit(GetChatListInProgress()); + + DataOutput result = await _chatRepostiory.fetchChatList(1); + + emit( + GetChatListSuccess( + isLoadingMore: false, + hasError: false, + chatedUserList: result.modelList, + currentPage: 1, + total: result.total, + ), + ); + } catch (e) { + emit(GetChatListFailed(e)); + } + } + + void addNewChat(ChatedUser user) { + //this will create new chat in chat list if there is no already + if (state is GetChatListSuccess) { + List chatedUserList = + (state as GetChatListSuccess).chatedUserList; + bool contains = chatedUserList.any( + (element) => element.userId == user.userId, + ); + if (contains == false) { + chatedUserList.insert(0, user); + emit((state as GetChatListSuccess) + .copyWith(chatedUserList: chatedUserList)); + } + } + } + + Future loadMore() async { + try { + if (state is GetChatListSuccess) { + if ((state as GetChatListSuccess).isLoadingMore) { + return; + } + emit((state as GetChatListSuccess).copyWith(isLoadingMore: true)); + + DataOutput result = await _chatRepostiory.fetchChatList( + (state as GetChatListSuccess).currentPage + 1, + ); + + GetChatListSuccess messagesSuccessState = (state as GetChatListSuccess); + + // messagesSuccessState.await.insertAll(0, result.modelList); + messagesSuccessState.chatedUserList.addAll(result.modelList); + emit(GetChatListSuccess( + chatedUserList: messagesSuccessState.chatedUserList, + currentPage: (state as GetChatListSuccess).currentPage + 1, + hasError: false, + isLoadingMore: false, + total: result.total, + )); + } + } catch (e) { + emit((state as GetChatListSuccess) + .copyWith(isLoadingMore: false, hasError: true)); + } + } + + bool hasMoreData() { + if (state is GetChatListSuccess) { + return (state as GetChatListSuccess).currentPage < + (state as GetChatListSuccess).total; + } + + return false; + } + + @override + GetChatListState? fromJson(Map json) { + return null; + } + + @override + Map? toJson(GetChatListState state) { + return null; + } +} diff --git a/lib/data/cubits/chatCubits/load_chat_messages.dart b/lib/data/cubits/chatCubits/load_chat_messages.dart new file mode 100644 index 0000000..b3534d1 --- /dev/null +++ b/lib/data/cubits/chatCubits/load_chat_messages.dart @@ -0,0 +1,125 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'package:ebroker/Ui/screens/ChatNew/MessageTypes/blueprint.dart'; +import 'package:ebroker/data/Repositories/chat_repository.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class LoadChatMessagesState {} + +class LoadChatMessagesInitial extends LoadChatMessagesState {} + +class LoadChatMessagesInProgress extends LoadChatMessagesState {} + +class LoadChatMessagesSuccess extends LoadChatMessagesState { + List messages; + int currentPage; + int userId; + int propertyId; + int totalPage; + bool isLoadingMore; + LoadChatMessagesSuccess({ + required this.messages, + required this.currentPage, + required this.userId, + required this.propertyId, + required this.totalPage, + required this.isLoadingMore, + }); + + LoadChatMessagesSuccess copyWith({ + List? messages, + int? currentPage, + int? userId, + int? propertyId, + int? totalPage, + bool? isLoadingMore, + }) { + return LoadChatMessagesSuccess( + messages: messages ?? this.messages, + currentPage: currentPage ?? this.currentPage, + userId: userId ?? this.userId, + propertyId: propertyId ?? this.propertyId, + totalPage: totalPage ?? this.totalPage, + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + ); + } + + @override + String toString() { + return 'LoadChatMessagesSuccess(messages: $messages, currentPage: $currentPage, userId: $userId, propertyId: $propertyId, totalPage: $totalPage, isLoadingMore: $isLoadingMore)'; + } +} + +class LoadChatMessagesFailed extends LoadChatMessagesState { + final dynamic error; + LoadChatMessagesFailed({ + required this.error, + }); +} + +class LoadChatMessagesCubit extends Cubit { + LoadChatMessagesCubit() : super(LoadChatMessagesInitial()); + final ChatRepostiory _chatRepostiory = ChatRepostiory(); + + Future load({required int userId, required int propertyId}) async { + try { + emit(LoadChatMessagesInProgress()); + DataOutput result = await _chatRepostiory.getMessages( + page: 1, + userId: userId, + propertyId: propertyId, + ); + emit( + LoadChatMessagesSuccess( + messages: result.modelList, + currentPage: 1, + propertyId: propertyId, + isLoadingMore: false, + totalPage: result.total, + userId: userId), + ); + } catch (e) { + emit(LoadChatMessagesFailed(error: e.toString())); + } + } + + Future loadMore() async { + try { + if (state is LoadChatMessagesSuccess) { + if ((state as LoadChatMessagesSuccess).isLoadingMore) { + return; + } + emit((state as LoadChatMessagesSuccess).copyWith(isLoadingMore: true)); + + DataOutput result = await _chatRepostiory.getMessages( + page: (state as LoadChatMessagesSuccess).currentPage + 1, + userId: (state as LoadChatMessagesSuccess).userId, + propertyId: (state as LoadChatMessagesSuccess).propertyId); + + LoadChatMessagesSuccess messagesSuccessState = + (state as LoadChatMessagesSuccess); + + messagesSuccessState.messages.addAll(result.modelList); + + emit(LoadChatMessagesSuccess( + messages: messagesSuccessState.messages, + currentPage: (state as LoadChatMessagesSuccess).currentPage + 1, + propertyId: (state as LoadChatMessagesSuccess).propertyId, + isLoadingMore: false, + totalPage: result.total, + userId: (state as LoadChatMessagesSuccess).userId)); + } + } catch (e) { + emit((state as LoadChatMessagesSuccess).copyWith(isLoadingMore: false)); + } + } + + bool hasMoreChat() { + if (state is LoadChatMessagesSuccess) { + return (state as LoadChatMessagesSuccess).currentPage < + (state as LoadChatMessagesSuccess).totalPage; + } + return false; + } +} diff --git a/lib/data/cubits/chatCubits/send_message.dart b/lib/data/cubits/chatCubits/send_message.dart new file mode 100644 index 0000000..92fd678 --- /dev/null +++ b/lib/data/cubits/chatCubits/send_message.dart @@ -0,0 +1,81 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +// import 'dart:developer'; + +import 'package:dio/dio.dart'; +import 'package:ebroker/data/Repositories/chat_repository.dart'; +import 'package:ebroker/utils/logger.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class SendMessageState {} + +class SendMessageInitial extends SendMessageState {} + +class SendMessageInProgress extends SendMessageState {} + +class SendMessageSuccess extends SendMessageState { + final int messageId; + SendMessageSuccess({ + required this.messageId, + }); +} + +class SendMessageFailed extends SendMessageState { + final dynamic error; + SendMessageFailed( + this.error, + ); +} + +class SendMessageCubit extends Cubit { + SendMessageCubit() : super(SendMessageInitial()); + final ChatRepostiory _chatRepostiory = ChatRepostiory(); + void send( + {required String senderId, + required String recieverId, + required String message, + required String proeprtyId, + dynamic audio, + dynamic attachment}) async { + try { + emit(SendMessageInProgress()); + MultipartFile? audioFile; + MultipartFile? attachmentFile; + + if (audio != null) { + audioFile = await MultipartFile.fromFile(audio); + } + if (attachment != null) { + attachmentFile = await MultipartFile.fromFile(attachment); + } + + ///If use is not uploading any text so we will upload [File]. + var message0 = message; + if (attachment != null && message == "") { + message0 = ""; + } + + var result = await _chatRepostiory.sendMessage( + senderId: senderId, + recieverId: recieverId, + message: message0, + proeprtyId: proeprtyId, + attachment: attachmentFile, + audio: audioFile); + + emit(SendMessageSuccess(messageId: result['id'])); + } catch (e) { + Logger.error(e.toString()); + emit(SendMessageFailed(e.toString())); + } + } + +//This will check if given file like audio recording or attachment is local or it is coming from remote server + bool isRemoteFile(dynamic file) { + if (file is String) { + return true; + } else { + return false; + } + } +} diff --git a/lib/data/cubits/company_cubit.dart b/lib/data/cubits/company_cubit.dart new file mode 100644 index 0000000..34eebb5 --- /dev/null +++ b/lib/data/cubits/company_cubit.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../utils/api.dart'; +import '../helper/custom_exception.dart'; +import '../model/company.dart'; + +abstract class CompanyState {} + +class CompanyInitial extends CompanyState {} + +class CompanyFetchProgress extends CompanyState {} + +class CompanyFetchSuccess extends CompanyState { + Company companyData; + + CompanyFetchSuccess(this.companyData); +} + +class CompanyFetchFailure extends CompanyState { + final String errmsg; + CompanyFetchFailure(this.errmsg); +} + +class CompanyCubit extends Cubit { + CompanyCubit() : super(CompanyInitial()); + + void fetchCompany(BuildContext context) { + emit(CompanyFetchProgress()); + fetchCompanyFromDb(context) + .then((value) => emit(CompanyFetchSuccess(value))) + .catchError((e) => emit(CompanyFetchFailure(e.toString()))); + } + + Future fetchCompanyFromDb(BuildContext context) async { + try { + Company companyData = Company(); + + Map body = { + Api.type: Api.company, + }; + + // var response = await HelperUtils.sendApiRequest( + // Api.apiGetSystemSettings, body, true, context, + // passUserid: false); + + var response = + await Api.post(url: Api.apiGetSystemSettings, parameter: body); + + // var getdata = json.decode(response); + + if (!response[Api.error]) { + Map list = response['data']; + // companyData = list.map((model) => Company.fromJson(model)).toList(); + + companyData = Company.fromJson(Map.from(list)); + + //set company mobile/contact number for Call @ Property details + // Constant.session + // .setData(Session.keyCompMobNo, contactNumber.data.toString()); + } else { + throw CustomException(response[Api.message]); + } + + return companyData; + } catch (e) { + throw e; + } + } +} diff --git a/lib/data/cubits/delete_advertisment_cubit.dart b/lib/data/cubits/delete_advertisment_cubit.dart new file mode 100644 index 0000000..79da32b --- /dev/null +++ b/lib/data/cubits/delete_advertisment_cubit.dart @@ -0,0 +1,34 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../Repositories/advertisement_repository.dart'; + +abstract class DeleteAdvertismentState {} + +class DeleteAdvertismentInitial extends DeleteAdvertismentState {} + +class DeleteAdvertismentInProgress extends DeleteAdvertismentState {} + +class DeleteAdvertismentSuccess extends DeleteAdvertismentState {} + +class DeleteAdvertismentFailure extends DeleteAdvertismentState { + final String errorMessage; + + DeleteAdvertismentFailure(this.errorMessage); +} + +class DeleteAdvertismentCubit extends Cubit { + final AdvertisementRepository _advertisementRepository; + + DeleteAdvertismentCubit(this._advertisementRepository) + : super(DeleteAdvertismentInitial()); + + void delete(dynamic id) async { + try { + emit(DeleteAdvertismentInProgress()); + await _advertisementRepository.deleteAdvertisment(id); + emit(DeleteAdvertismentSuccess()); + } catch (e) { + emit(DeleteAdvertismentFailure(e.toString())); + } + } +} diff --git a/lib/data/cubits/enquiry/enquiry_cubit.dart b/lib/data/cubits/enquiry/enquiry_cubit.dart new file mode 100644 index 0000000..9ac48e0 --- /dev/null +++ b/lib/data/cubits/enquiry/enquiry_cubit.dart @@ -0,0 +1,73 @@ +import 'dart:convert'; + +import 'package:ebroker/utils/Extensions/extensions.dart'; + +import '../../helper/custom_exception.dart'; +import '../../../utils/helper_utils.dart'; +import '../../../utils/hive_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../utils/api.dart'; + +abstract class EnquiryState {} + +class EnquiryInitial extends EnquiryState {} + +class EnquirySetProgress extends EnquiryState {} + +class EnquirySetSuccess extends EnquiryState { + String msg = ''; + EnquirySetSuccess(this.msg); +} + +class EnquirySetFailure extends EnquiryState { + final String errmsg; + EnquirySetFailure(this.errmsg); +} + +class EnquiryCubit extends Cubit { + EnquiryCubit() : super(EnquiryInitial()); + +void setEnquiry(BuildContext context, + {String? actionType, String? propertyId, String? status}) { + emit(EnquirySetProgress()); + setEnquiryFromDb(context, actionType!, propertyId!, status!) + .then((value) => emit(EnquirySetSuccess(value))) + .catchError((e) => emit(EnquirySetFailure(e.toString()))); + } + + Future setEnquiryFromDb( + BuildContext context, + String actionType, + String propertyId, + String status, + ) async { + if (actionType == '0') { + } else { + // ApiParams.id: '', + // ApiParams.enqStatus: '' + } + Map body = { + //Add + Api.actionType: actionType, + Api.propertyId: propertyId, + Api.customerId: HiveUtils.getUserId().toString(), + }; + + var response = await HelperUtils.sendApiRequest( + Api.apiSetPropertyEnquiry, body, true, context, + passUserid: false); + var getdata = json.decode(response); + if (getdata != null) { + } else { + Future.delayed( + Duration.zero, + () { + throw CustomException("nodatafound".translate(context)); + }, + ); + } + return getdata[Api.message]; + } +} diff --git a/lib/data/cubits/enquiry/store_enqury_id.dart b/lib/data/cubits/enquiry/store_enqury_id.dart new file mode 100644 index 0000000..d07d727 --- /dev/null +++ b/lib/data/cubits/enquiry/store_enqury_id.dart @@ -0,0 +1,34 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:flutter_bloc/flutter_bloc.dart'; + +class EnquiryIdsLocalCubit extends Cubit { + EnquiryIdsLocalCubit() : super(EnquiryIdsLocalState(ids: [])); + + void add(id) { + List? ids = state.ids; + ids?.add( + id, + ); + emit( + EnquiryIdsLocalState(ids: ids), + ); + } +} + +class EnquiryIdsLocalState { + List? ids; + EnquiryIdsLocalState({ + this.ids, + }); + + EnquiryIdsLocalState copyWith({ + List? ids, + }) { + return EnquiryIdsLocalState( + ids: ids ?? this.ids, + ); + } + + @override + String toString() => 'EnquiryIdsLocalState(ids: $ids)'; +} diff --git a/lib/data/cubits/favorite/add_to_favorite_cubit.dart b/lib/data/cubits/favorite/add_to_favorite_cubit.dart new file mode 100644 index 0000000..cd7e308 --- /dev/null +++ b/lib/data/cubits/favorite/add_to_favorite_cubit.dart @@ -0,0 +1,60 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:ebroker/data/Repositories/favourites_repository.dart'; + +import '../../../utils/constant.dart'; + +enum FavoriteType { + add("1"), + remove("0"); + + final String value; + + const FavoriteType(this.value); +} + +abstract class AddToFavoriteCubitState {} + +class AddToFavoriteCubitInitial extends AddToFavoriteCubitState {} + +class AddToFavoriteCubitInProgress extends AddToFavoriteCubitState {} + +class AddToFavoriteCubitSuccess extends AddToFavoriteCubitState { + final int id; + final FavoriteType favorite; + AddToFavoriteCubitSuccess({ + required this.favorite, + required this.id, + }); +} + +class AddToFavoriteCubitFailure extends AddToFavoriteCubitState { + final String errorMessage; + + AddToFavoriteCubitFailure(this.errorMessage); +} + +class AddToFavoriteCubitCubit extends Cubit { + AddToFavoriteCubitCubit() : super(AddToFavoriteCubitInitial()); + + final FavoriteRepository _favouriteRepository = FavoriteRepository(); + + Future setFavroite({ + required int propertyId, + required FavoriteType type, + }) async { + try { + emit(AddToFavoriteCubitInProgress()); + await _favouriteRepository.addToFavorite(propertyId, type.value); + if (type == FavoriteType.add) { + Constant.favoritePropertyList.add((propertyId)); + } else { + Constant.favoritePropertyList.remove((propertyId)); + } + emit(AddToFavoriteCubitSuccess(id: propertyId, favorite: type)); + } catch (e) { + emit(AddToFavoriteCubitFailure(e.toString())); + } + } +} diff --git a/lib/data/cubits/favorite/fetch_favorites_cubit.dart b/lib/data/cubits/favorite/fetch_favorites_cubit.dart new file mode 100644 index 0000000..6deb0dd --- /dev/null +++ b/lib/data/cubits/favorite/fetch_favorites_cubit.dart @@ -0,0 +1,132 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../Repositories/favourites_repository.dart'; +import '../../model/data_output.dart'; +import '../../model/property_model.dart'; + +abstract class FetchFavoritesState {} + +class FetchFavoritesInitial extends FetchFavoritesState {} + +class FetchFavoritesInProgress extends FetchFavoritesState {} + +class FetchFavoritesSuccess extends FetchFavoritesState { + final bool isLoadingMore; + final bool loadingMoreError; + final List propertymodel; + final int offset; + final int total; + FetchFavoritesSuccess({ + required this.isLoadingMore, + required this.loadingMoreError, + required this.propertymodel, + required this.offset, + required this.total, + }); + + FetchFavoritesSuccess copyWith({ + bool? isLoadingMore, + bool? loadingMoreError, + List? propertymodel, + int? offset, + int? total, + }) { + return FetchFavoritesSuccess( + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + loadingMoreError: loadingMoreError ?? this.loadingMoreError, + propertymodel: propertymodel ?? this.propertymodel, + offset: offset ?? this.offset, + total: total ?? this.total, + ); + } +} + +class FetchFavoritesFailure extends FetchFavoritesState { + final dynamic errorMessage; + FetchFavoritesFailure(this.errorMessage); +} + +class FetchFavoritesCubit extends Cubit { + FetchFavoritesCubit() : super(FetchFavoritesInitial()); + + final FavoriteRepository _favoritesRepository = FavoriteRepository(); + + Future fetchFavorites() async { + try { + emit(FetchFavoritesInProgress()); + + DataOutput result = + await _favoritesRepository.fetchFavorites(offset: 0); + + emit(FetchFavoritesSuccess( + isLoadingMore: false, + loadingMoreError: false, + propertymodel: result.modelList, + offset: 0, + total: result.total)); + } catch (error) { + emit(FetchFavoritesFailure(error)); + } + } + + void remove(id) { + if (state is FetchFavoritesSuccess) { + List propertymodel = + (state as FetchFavoritesSuccess).propertymodel; + + propertymodel.removeWhere((element) => element.id == id); + + emit((state as FetchFavoritesSuccess) + .copyWith(propertymodel: propertymodel)); + } + } + + void add(PropertyModel model) { + if (state is FetchFavoritesSuccess) { + List propertymodel = + (state as FetchFavoritesSuccess).propertymodel; + + propertymodel.insert(0, model); + // propertymodel.removeWhere((element) => element.id == id); + + emit((state as FetchFavoritesSuccess) + .copyWith(propertymodel: propertymodel)); + } + } + + Future fetchFavoritesMore() async { + try { + if (state is FetchFavoritesSuccess) { + if ((state as FetchFavoritesSuccess).isLoadingMore) { + return; + } + emit((state as FetchFavoritesSuccess).copyWith(isLoadingMore: true)); + DataOutput result = + await _favoritesRepository.fetchFavorites( + offset: (state as FetchFavoritesSuccess).propertymodel.length, + ); + + FetchFavoritesSuccess propertymodelState = + (state as FetchFavoritesSuccess); + propertymodelState.propertymodel.addAll(result.modelList); + emit(FetchFavoritesSuccess( + isLoadingMore: false, + loadingMoreError: false, + propertymodel: propertymodelState.propertymodel, + offset: (state as FetchFavoritesSuccess).propertymodel.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchFavoritesSuccess) + .copyWith(isLoadingMore: false, loadingMoreError: true)); + } + } + + bool hasMoreData() { + if (state is FetchFavoritesSuccess) { + return (state as FetchFavoritesSuccess).propertymodel.length < + (state as FetchFavoritesSuccess).total; + } + return false; + } +} diff --git a/lib/data/cubits/favorite/remove_favoriteubit.dart b/lib/data/cubits/favorite/remove_favoriteubit.dart new file mode 100644 index 0000000..339ba34 --- /dev/null +++ b/lib/data/cubits/favorite/remove_favoriteubit.dart @@ -0,0 +1,39 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../Repositories/favourites_repository.dart'; + +abstract class RemoveFavoriteState {} + +class RemoveFavoriteInitial extends RemoveFavoriteState {} + +class RemoveFavoriteInProgress extends RemoveFavoriteState {} + +class RemoveFavoriteSuccess extends RemoveFavoriteState { + final int id; + RemoveFavoriteSuccess({ + required this.id, + }); +} + +class RemoveFavoriteFailure extends RemoveFavoriteState { + final String errorMessage; + + RemoveFavoriteFailure(this.errorMessage); +} + +class RemoveFavoriteCubit extends Cubit { + final FavoriteRepository _favoriteRepository = FavoriteRepository(); + + RemoveFavoriteCubit() : super(RemoveFavoriteInitial()); + + void remove(int propertyID) async { + try { + emit(RemoveFavoriteInProgress()); + await _favoriteRepository.removeFavorite(propertyID); + emit(RemoveFavoriteSuccess(id: propertyID)); + } catch (e) { + emit(RemoveFavoriteFailure(e.toString())); + } + } +} diff --git a/lib/data/cubits/fetch_articles_cubit.dart b/lib/data/cubits/fetch_articles_cubit.dart new file mode 100644 index 0000000..8d76f7d --- /dev/null +++ b/lib/data/cubits/fetch_articles_cubit.dart @@ -0,0 +1,111 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../Repositories/articles_repository.dart'; +import '../model/article_model.dart'; +import '../model/data_output.dart'; + +abstract class FetchArticlesState {} + +class FetchArticlesInitial extends FetchArticlesState {} + +class FetchArticlesInProgress extends FetchArticlesState {} + +class FetchArticlesSuccess extends FetchArticlesState { + final bool isLoadingMore; + final bool loadingMoreError; + final List articlemodel; + final int offset; + final int total; + FetchArticlesSuccess({ + required this.isLoadingMore, + required this.loadingMoreError, + required this.articlemodel, + required this.offset, + required this.total, + }); + + FetchArticlesSuccess copyWith({ + bool? isLoadingMore, + bool? loadingMoreError, + List? articlemodel, + int? offset, + int? total, + }) { + return FetchArticlesSuccess( + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + loadingMoreError: loadingMoreError ?? this.loadingMoreError, + articlemodel: articlemodel ?? this.articlemodel, + offset: offset ?? this.offset, + total: total ?? this.total, + ); + } +} + +class FetchArticlesFailure extends FetchArticlesState { + final dynamic errorMessage; + FetchArticlesFailure(this.errorMessage); +} + +class FetchArticlesCubit extends Cubit { + FetchArticlesCubit() : super(FetchArticlesInitial()); + + final ArticlesRepository _articleRepository = ArticlesRepository(); + + Future fetchArticles() async { + try { + emit(FetchArticlesInProgress()); + + DataOutput result = + await _articleRepository.fetchArticles(offset: 0); + + emit( + FetchArticlesSuccess( + isLoadingMore: false, + loadingMoreError: false, + articlemodel: result.modelList, + offset: 0, + total: result.total), + ); + } catch (e) { + emit(FetchArticlesFailure(e)); + } + } + + Future fetchArticlesMore() async { + try { + if (state is FetchArticlesSuccess) { + if ((state as FetchArticlesSuccess).isLoadingMore) { + return; + } + + emit((state as FetchArticlesSuccess).copyWith(isLoadingMore: true)); + + DataOutput result = + await _articleRepository.fetchArticles( + offset: (state as FetchArticlesSuccess).articlemodel.length, + ); + + FetchArticlesSuccess articlemodelState = + (state as FetchArticlesSuccess); + articlemodelState.articlemodel.addAll(result.modelList); + emit(FetchArticlesSuccess( + isLoadingMore: false, + loadingMoreError: false, + articlemodel: articlemodelState.articlemodel, + offset: (state as FetchArticlesSuccess).articlemodel.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchArticlesSuccess) + .copyWith(isLoadingMore: false, loadingMoreError: true)); + } + } + + bool hasMoreData() { + if (state is FetchArticlesSuccess) { + return (state as FetchArticlesSuccess).articlemodel.length < + (state as FetchArticlesSuccess).total; + } + return false; + } +} diff --git a/lib/data/cubits/fetch_notifications_cubit.dart b/lib/data/cubits/fetch_notifications_cubit.dart new file mode 100644 index 0000000..718b9bc --- /dev/null +++ b/lib/data/cubits/fetch_notifications_cubit.dart @@ -0,0 +1,110 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../Repositories/notifications_repository_repository.dart'; +import '../model/data_output.dart'; +import '../model/notification_data.dart'; + +abstract class FetchNotificationsState {} + +class FetchNotificationsInitial extends FetchNotificationsState {} + +class FetchNotificationsInProgress extends FetchNotificationsState {} + +class FetchNotificationsSuccess extends FetchNotificationsState { + final bool isLoadingMore; + final bool loadingMoreError; + final List notificationdata; + final int offset; + final int total; + FetchNotificationsSuccess({ + required this.isLoadingMore, + required this.loadingMoreError, + required this.notificationdata, + required this.offset, + required this.total, + }); + + FetchNotificationsSuccess copyWith({ + bool? isLoadingMore, + bool? loadingMoreError, + List? notificationdata, + int? offset, + int? total, + }) { + return FetchNotificationsSuccess( + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + loadingMoreError: loadingMoreError ?? this.loadingMoreError, + notificationdata: notificationdata ?? this.notificationdata, + offset: offset ?? this.offset, + total: total ?? this.total, + ); + } +} + +class FetchNotificationsFailure extends FetchNotificationsState { + final dynamic errorMessage; + FetchNotificationsFailure(this.errorMessage); +} + +class FetchNotificationsCubit extends Cubit { + FetchNotificationsCubit() : super(FetchNotificationsInitial()); + + final NotificationsRepository _notificationsRepository = + NotificationsRepository(); + + Future fetchNotifications() async { + try { + emit(FetchNotificationsInProgress()); + + DataOutput result = + await _notificationsRepository.fetchNotifications(offset: 0); + + emit(FetchNotificationsSuccess( + isLoadingMore: false, + loadingMoreError: false, + notificationdata: result.modelList, + offset: 0, + total: result.total)); + } catch (e) { + emit(FetchNotificationsFailure(e)); + } + } + + Future fetchNotificationsMore() async { + try { + if (state is FetchNotificationsSuccess) { + if ((state as FetchNotificationsSuccess).isLoadingMore) { + return; + } + emit( + (state as FetchNotificationsSuccess).copyWith(isLoadingMore: true)); + DataOutput result = + await _notificationsRepository.fetchNotifications( + offset: (state as FetchNotificationsSuccess).notificationdata.length, + ); + + FetchNotificationsSuccess notificationdataState = + (state as FetchNotificationsSuccess); + notificationdataState.notificationdata.addAll(result.modelList); + emit(FetchNotificationsSuccess( + isLoadingMore: false, + loadingMoreError: false, + notificationdata: notificationdataState.notificationdata, + offset: + (state as FetchNotificationsSuccess).notificationdata.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchNotificationsSuccess) + .copyWith(isLoadingMore: false, loadingMoreError: true)); + } + } + + bool hasMoreData() { + if (state is FetchNotificationsSuccess) { + return (state as FetchNotificationsSuccess).notificationdata.length < + (state as FetchNotificationsSuccess).total; + } + return false; + } +} diff --git a/lib/data/cubits/outdoorfacility/fetch_outdoor_facility_list.dart b/lib/data/cubits/outdoorfacility/fetch_outdoor_facility_list.dart new file mode 100644 index 0000000..bc7931f --- /dev/null +++ b/lib/data/cubits/outdoorfacility/fetch_outdoor_facility_list.dart @@ -0,0 +1,85 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'package:ebroker/data/Repositories/outdoorfacility.dart'; +import 'package:ebroker/data/model/outdoor_facility.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../model/property_model.dart'; + +class FetchOutdoorFacilityListState {} + +class FetchOutdoorFacilityListInitial extends FetchOutdoorFacilityListState {} + +class FetchOutdoorFacilityListInProgress + extends FetchOutdoorFacilityListState {} + +class FetchOutdoorFacilityListSucess extends FetchOutdoorFacilityListState { + final List outdoorFacilityList; + FetchOutdoorFacilityListSucess({ + required this.outdoorFacilityList, + }); + + FetchOutdoorFacilityListSucess copyWith({ + List? outdoorFacilityList, + }) { + return FetchOutdoorFacilityListSucess( + outdoorFacilityList: outdoorFacilityList ?? this.outdoorFacilityList, + ); + } +} + +class FetchOutdoorFacilityListFailure extends FetchOutdoorFacilityListState { + final dynamic error; + FetchOutdoorFacilityListFailure({ + required this.error, + }); +} + +class FetchOutdoorFacilityListCubit + extends Cubit { + FetchOutdoorFacilityListCubit() : super(FetchOutdoorFacilityListInitial()); + final OutdoorFacilityRepository _facilityRepository = + OutdoorFacilityRepository(); + void fetch() async { + try { + emit(FetchOutdoorFacilityListInProgress()); + + List facilityList = + await _facilityRepository.fetchOutdoorFacilityList(); + emit(FetchOutdoorFacilityListSucess(outdoorFacilityList: facilityList)); + } catch (error) { + emit(FetchOutdoorFacilityListFailure(error: error)); + } + } + + List getList() { + if (state is FetchOutdoorFacilityListSucess) { + return (state as FetchOutdoorFacilityListSucess).outdoorFacilityList; + } + return []; + } + + void fetchIfFailed() { + if (state is FetchOutdoorFacilityListFailure || + state is FetchOutdoorFacilityListInitial) { + fetch(); + } + } + + void fillData(List facilities) { + if (state is FetchOutdoorFacilityListSucess) { + List newFacility = []; + + for (var i = 0; i < facilities.length; i++) { + newFacility.add(OutdoorFacility( + name: facilities[i].name, + id: facilities[i].id, + distance: facilities[i].distance.toString(), + image: facilities[i].image)); + } + + emit((state as FetchOutdoorFacilityListSucess) + .copyWith(outdoorFacilityList: newFacility)); + } + } +} diff --git a/lib/data/cubits/profile_setting_cubit.dart b/lib/data/cubits/profile_setting_cubit.dart new file mode 100644 index 0000000..11c42c7 --- /dev/null +++ b/lib/data/cubits/profile_setting_cubit.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../settings.dart'; +import '../../utils/api.dart'; +import '../../utils/constant.dart'; +import '../helper/custom_exception.dart'; + +abstract class ProfileSettingState {} + +//String? profileSettingData = ''; + +class ProfileSettingInitial extends ProfileSettingState {} + +class ProfileSettingFetchProgress extends ProfileSettingState {} + +class ProfileSettingFetchSuccess extends ProfileSettingState { + String data; + ProfileSettingFetchSuccess({required this.data}); + + Map toMap() { + return { + 'data': data, + }; + } + + factory ProfileSettingFetchSuccess.fromMap(Map map) { + return ProfileSettingFetchSuccess( + data: map['data'] as String, + ); + } +} + +class ProfileSettingFetchFailure extends ProfileSettingState { + final String errmsg; + ProfileSettingFetchFailure(this.errmsg); +} + +class ProfileSettingCubit extends Cubit + with HydratedMixin { + ProfileSettingCubit() : super(ProfileSettingInitial()); + + void fetchProfileSetting(BuildContext context, String title, + {bool? forceRefresh}) async { + if (forceRefresh != true) { + if (state is ProfileSettingFetchSuccess) { + await Future.delayed( + const Duration(seconds: AppSettings.hiddenAPIProcessDelay)); + } else { + emit(ProfileSettingFetchProgress()); + } + } else { + emit(ProfileSettingFetchProgress()); + } + + if (forceRefresh == true) { + fetchProfileSettingFromDb(context, title).then((value) { + emit(ProfileSettingFetchSuccess(data: value ?? "")); + }).catchError((e, stack) { + emit(ProfileSettingFetchFailure(stack.toString())); + }); + } else { + if (state is! ProfileSettingFetchSuccess) { + fetchProfileSettingFromDb(context, title).then((value) { + emit(ProfileSettingFetchSuccess(data: value ?? "")); + }).catchError((e, stack) { + emit(ProfileSettingFetchFailure(stack.toString())); + }); + } else { + emit( + ProfileSettingFetchSuccess( + data: (state as ProfileSettingFetchSuccess).data), + ); + } + } + } + + Future fetchProfileSettingFromDb( + BuildContext context, String title) async { + try { + String? profileSettingData; + Map body = { + Api.type: title, + }; + + var response = await Api.post( + url: Api.apiGetSystemSettings, parameter: body, useAuthToken: false); + + if (!response[Api.error]) { + if (title == Api.currencySymbol) { + // Constant.currencySymbol = getdata['data'].toString(); + } else if (title == Api.maintenanceMode) { + Constant.maintenanceMode = response['data'].toString(); + } else { + Map data = (response['data']); + + if (title == Api.termsAndConditions) { + profileSettingData = data['terms_conditions']; + // .where((element) => element['type'] == "terms_conditions") + // .first['data']; + } + + if (title == Api.privacyPolicy) { + profileSettingData = data['privacy_policy']; + // .where((element) => element['type'] == "privacy_policy") + // .first['data']; + } + + if (title == Api.aboutApp) { + profileSettingData = data['about_us']; + // .where((element) => element['type'] == "about_us") + // .first['data']; + } + } + } else { + throw CustomException(response[Api.message]); + } + + return profileSettingData; + } catch (e) { + rethrow; + } + } + + @override + ProfileSettingState? fromJson(Map json) { + try { + if (json['cubit_state'] == "ProfileSettingFetchSuccess") { + ProfileSettingFetchSuccess profileSettingFetchSuccess = + ProfileSettingFetchSuccess.fromMap(json); + + return profileSettingFetchSuccess; + } + } catch (e) {} + return null; + } + + @override + Map? toJson(ProfileSettingState state) { + try { + if (state is ProfileSettingFetchSuccess) { + Map mapped = state.toMap(); + mapped['cubit_state'] = "ProfileSettingFetchSuccess"; + return mapped; + } + } catch (e) {} + + return null; + } +} diff --git a/lib/data/cubits/project/all_projects_screen.dart b/lib/data/cubits/project/all_projects_screen.dart new file mode 100644 index 0000000..b78585d --- /dev/null +++ b/lib/data/cubits/project/all_projects_screen.dart @@ -0,0 +1,115 @@ +import 'package:ebroker/Ui/screens/project/view/project_list_screen.dart'; +import 'package:ebroker/data/cubits/project/fetch_projects.dart'; +import 'package:ebroker/data/model/project_model.dart'; +import 'package:ebroker/utils/guestChecker.dart'; +import 'package:flutter/material.dart'; + +import '../../../Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import '../../../Ui/screens/widgets/blurred_dialoge_box.dart'; +import '../../../exports/main_export.dart'; +import '../../../utils/ui_utils.dart'; + +class AllProjectsScreen extends StatefulWidget { + const AllProjectsScreen({super.key}); + + static Route route(RouteSettings settings) { + return BlurredRouter( + builder: (context) { + return const AllProjectsScreen(); + }, + ); + } + + @override + State createState() => _AllProjectsScreenState(); +} + +class _AllProjectsScreenState extends State { + final ScrollController _controller = ScrollController(); + @override + void initState() { + _controller.addListener(() { + if (_controller.isEndReached()) { + if (context.read().hasMore()) { + context.read().fetchMoreProjects(); + } + } + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: UiUtils.buildAppBar(context, + showBackButton: true, title: UiUtils.translate(context, "projects")), + body: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: BlocBuilder( + builder: (context, state) { + if (state is FetchProjectsSuccess) { + return Column( + mainAxisSize: MainAxisSize.max, + children: [ + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.all(14), + itemCount: state.projects.length, + itemBuilder: (context, index) { + ProjectModel project = state.projects[index]; + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: GestureDetector( + onTap: () { + GuestChecker.check( + onNotGuest: () { + if (context + .read() + .getRawSettings()['is_premium'] ?? + false) { + Navigator.pushNamed( + context, Routes.projectDetailsScreen, + arguments: {"project": project}); + } else { + UiUtils.showBlurredDialoge(context, + dialoge: BlurredDialogBox( + title: "Subscription needed", + isAcceptContainesPush: true, + onAccept: () async { + Navigator.popAndPushNamed( + context, + Routes + .subscriptionPackageListRoute, + arguments: {"from": "home"}); + }, + content: const Text( + "Subscribe to package if you want to use this feature"))); + } + }, + ); + }, + child: ProjectCard( + categoryName: project.category?.category ?? "", + url: project.image ?? "", + title: project.title ?? "", + description: project.description ?? "", + categoryIcon: project.category?.image ?? "", + status: project.type ?? ""), + ), + ); + }, + ), + if (state.isLoadingMore) UiUtils.progress() + ], + ); + // return ProjectCard(categoryName: categoryName, url: url, title: title, description: description, categoryIcon: categoryIcon, status: status); + } + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/data/cubits/project/delete_project_cubit.dart b/lib/data/cubits/project/delete_project_cubit.dart new file mode 100644 index 0000000..6aa909a --- /dev/null +++ b/lib/data/cubits/project/delete_project_cubit.dart @@ -0,0 +1,32 @@ +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/api.dart'; + +abstract class DeleteProjectState {} + +class DeleteProjectInitial extends DeleteProjectState {} + +class DeleteProjectInProgress extends DeleteProjectState {} + +class DeleteProjectSuccess extends DeleteProjectState { + final int id; + DeleteProjectSuccess(this.id); +} + +class DeleteProjectFail extends DeleteProjectState { + final dynamic error; + DeleteProjectFail(this.error); +} + +class DeleteProjectCubit extends Cubit { + DeleteProjectCubit() : super(DeleteProjectInitial()); + + delete(int id) async { + try { + emit(DeleteProjectInProgress()); + await Api.post(url: Api.deleteProject, parameter: {"id": id}); + emit(DeleteProjectSuccess(id)); + } catch (e) { + emit(DeleteProjectFail(e)); + } + } +} diff --git a/lib/data/cubits/project/fetchMyProjectsListCubit.dart b/lib/data/cubits/project/fetchMyProjectsListCubit.dart new file mode 100644 index 0000000..888d888 --- /dev/null +++ b/lib/data/cubits/project/fetchMyProjectsListCubit.dart @@ -0,0 +1,133 @@ +import 'package:ebroker/data/Repositories/project_repository.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/project_model.dart'; +import 'package:ebroker/exports/main_export.dart'; + +abstract class FetchMyProjectsListState {} + +class FetchMyProjectsListInitial extends FetchMyProjectsListState {} + +class FetchMyProjectsListInProgress extends FetchMyProjectsListState {} + +class FetchMyProjectsListSuccess extends FetchMyProjectsListState { + final bool isLoadingMore; + final bool hasError; + final int total; + final List projects; + final int offset; + + FetchMyProjectsListSuccess({ + required this.isLoadingMore, + required this.hasError, + required this.total, + required this.projects, + required this.offset, + }); + + FetchMyProjectsListSuccess copyWith({ + bool? isLoadingMore, + bool? hasError, + int? total, + List? projects, + int? offset, + }) { + return FetchMyProjectsListSuccess( + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + hasError: hasError ?? this.hasError, + total: total ?? this.total, + projects: projects ?? this.projects, + offset: offset ?? this.offset, + ); + } +} + +class FetchMyProjectsListFail extends FetchMyProjectsListState { + final dynamic error; + FetchMyProjectsListFail(this.error); +} + +class FetchMyProjectsListCubit extends Cubit { + FetchMyProjectsListCubit() : super(FetchMyProjectsListInitial()); + final ProjectRepository _projectRepository = ProjectRepository(); + + void fetch() async { + try { + emit(FetchMyProjectsListInProgress()); + DataOutput dataOutput = + await _projectRepository.getMyProjects(offset: 0); + + emit(FetchMyProjectsListSuccess( + hasError: false, + isLoadingMore: false, + offset: 0, + total: dataOutput.total, + projects: dataOutput.modelList)); + } catch (e) { + emit(FetchMyProjectsListFail(e)); + } + } + + delete(int id) { + if (state is FetchMyProjectsListSuccess) { + int indexWhere = (state as FetchMyProjectsListSuccess) + .projects + .indexWhere((element) => element.id == id); + (state as FetchMyProjectsListSuccess).projects.removeAt(indexWhere); + emit((state as FetchMyProjectsListSuccess) + .copyWith(projects: (state as FetchMyProjectsListSuccess).projects)); + } + } + + bool hasMore() { + if (state is FetchMyProjectsListSuccess) { + return (state as FetchMyProjectsListSuccess).projects.length < + (state as FetchMyProjectsListSuccess).total; + } + return false; + } + + void update(ProjectModel model) { + if (state is FetchMyProjectsListSuccess) { + int indexWhere = (state as FetchMyProjectsListSuccess) + .projects + .indexWhere((element) => element.id == model.id); + if (indexWhere.isNegative) { + (state as FetchMyProjectsListSuccess).projects.add(model); + } else { + (state as FetchMyProjectsListSuccess).projects[indexWhere] = model; + } + emit((state as FetchMyProjectsListSuccess) + .copyWith(projects: (state as FetchMyProjectsListSuccess).projects)); + } + } + + void fetchMore() async { + if (state is FetchMyProjectsListInProgress) { + return; + } + try { + if (state is FetchMyProjectsListSuccess) { + emit((state as FetchMyProjectsListSuccess) + .copyWith(isLoadingMore: true)); + DataOutput result = + await _projectRepository.getMyProjects( + offset: (state as FetchMyProjectsListSuccess).projects.length, + ); + + List projects = + (state as FetchMyProjectsListSuccess).projects; + projects.addAll(result.modelList); + + emit(FetchMyProjectsListSuccess( + projects: projects, + isLoadingMore: false, + hasError: false, + offset: projects.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchMyProjectsListSuccess) + .copyWith(isLoadingMore: false, hasError: true)); + } + } +} diff --git a/lib/data/cubits/project/fetch_projects.dart b/lib/data/cubits/project/fetch_projects.dart new file mode 100644 index 0000000..f0fab44 --- /dev/null +++ b/lib/data/cubits/project/fetch_projects.dart @@ -0,0 +1,111 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:ebroker/data/Repositories/project_repository.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/project_model.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class FetchProjectsState {} + +class FetchProjectsInitial extends FetchProjectsState {} + +class FetchProjectsInProgress extends FetchProjectsState {} + +class FetchProjectsSuccess extends FetchProjectsState { + final List projects; + final bool isLoadingMore; + final bool hasError; + final int offset; + final int total; + + FetchProjectsSuccess({ + required this.projects, + required this.isLoadingMore, + required this.hasError, + required this.offset, + required this.total, + }); + + FetchProjectsSuccess copyWith({ + List? projects, + bool? isLoadingMore, + bool? hasError, + int? offset, + int? total, + }) { + return FetchProjectsSuccess( + projects: projects ?? this.projects, + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + hasError: hasError ?? this.hasError, + offset: offset ?? this.offset, + total: total ?? this.total, + ); + } +} + +class FetchProjectsFailure extends FetchProjectsState { + final dynamic errorMessage; + + FetchProjectsFailure(this.errorMessage); +} + +class FetchProjectsCubit extends Cubit { + FetchProjectsCubit() : super(FetchProjectsInitial()); + final ProjectRepository _projectRepository = ProjectRepository(); + Future fetchProjects() async { + try { + emit(FetchProjectsInProgress()); + DataOutput result = + await _projectRepository.getProjects(offset: 0); + emit(FetchProjectsSuccess( + projects: result.modelList, + offset: 0, + isLoadingMore: false, + total: result.total, + hasError: false)); + } catch (e) { + emit(FetchProjectsFailure(e)); + } + } + + bool hasMore() { + if (state is FetchProjectsSuccess) { + return (state as FetchProjectsSuccess).projects.length < + (state as FetchProjectsSuccess).total; + } + return false; + } + + isProjectEmpty() { + if (state is FetchProjectsSuccess) { + return (state as FetchProjectsSuccess).projects.isEmpty; + } + return true; + } + + fetchMoreProjects() async { + if (state is FetchProjectsInProgress) { + return; + } + try { + if (state is FetchProjectsSuccess) { + emit((state as FetchProjectsSuccess).copyWith(isLoadingMore: true)); + DataOutput result = await _projectRepository.getProjects( + offset: (state as FetchProjectsSuccess).projects.length, + ); + + List projects = (state as FetchProjectsSuccess).projects; + projects.addAll(result.modelList); + + emit(FetchProjectsSuccess( + projects: projects, + isLoadingMore: false, + hasError: false, + offset: projects.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchProjectsSuccess) + .copyWith(isLoadingMore: false, hasError: true)); + } + } +} diff --git a/lib/data/cubits/project/manage_project_cubit.dart b/lib/data/cubits/project/manage_project_cubit.dart new file mode 100644 index 0000000..8b170a1 --- /dev/null +++ b/lib/data/cubits/project/manage_project_cubit.dart @@ -0,0 +1,41 @@ +import 'package:ebroker/data/Repositories/project_repository.dart'; +import 'package:ebroker/data/model/project_model.dart'; + +import '../../../exports/main_export.dart'; + +abstract class ManageProjectState {} + +class ManageProjectIntial extends ManageProjectState {} + +class ManageProjectInProgress extends ManageProjectState {} + +class ManageProjectInSuccess extends ManageProjectState { + final ProjectModel project; + ManageProjectInSuccess(this.project); +} + +class ManageProjectInFail extends ManageProjectState { + final dynamic error; + ManageProjectInFail(this.error); +} + +enum ManageProjectType { create, update } + +class ManageProjectCubit extends Cubit { + ManageProjectCubit() : super(ManageProjectIntial()); + final ProjectRepository _projectRepository = ProjectRepository(); + void manage( + {required ManageProjectType type, + required Map data}) async { + try { + emit(ManageProjectInProgress()); + var reposnse = await _projectRepository.createProject(data); + print("RESP DTA ${reposnse}"); + emit(ManageProjectInSuccess(ProjectModel.fromMap(reposnse['data'][0]))); + } catch (e, st) { + emit(ManageProjectInFail( + st, + )); + } + } +} diff --git a/lib/data/cubits/property/Interest/change_interest_in_property_cubit.dart b/lib/data/cubits/property/Interest/change_interest_in_property_cubit.dart new file mode 100644 index 0000000..a911c52 --- /dev/null +++ b/lib/data/cubits/property/Interest/change_interest_in_property_cubit.dart @@ -0,0 +1,61 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:ebroker/data/Repositories/interest_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:ebroker/utils/constant.dart'; + +// import '../../../Repositories/property_repository.dart'; + +enum PropertyInterest { + interested("1"), + notInterested("0"); + + final String value; + const PropertyInterest(this.value); +} + +abstract class ChangeInterestInPropertyState {} + +class ChangeInterestInPropertyInitial extends ChangeInterestInPropertyState {} + +class ChangeInterestInPropertyInProgress + extends ChangeInterestInPropertyState {} + +class ChangeInterestInPropertySuccess extends ChangeInterestInPropertyState { + PropertyInterest interest; + ChangeInterestInPropertySuccess({ + required this.interest, + }); +} + +class ChangeInterestInPropertyFailure extends ChangeInterestInPropertyState { + final String errorMessage; + + ChangeInterestInPropertyFailure(this.errorMessage); +} + +class ChangeInterestInPropertyCubit + extends Cubit { + InterestRepository _interestRepository=InterestRepository(); + ChangeInterestInPropertyCubit() : super(ChangeInterestInPropertyInitial()); + + Future changeInterest({ + required String propertyId, + required PropertyInterest interest, + }) async { + try { + emit(ChangeInterestInPropertyInProgress()); + await _interestRepository.setInterest( + interest: interest.value, propertyId: propertyId); + if (interest == PropertyInterest.interested) { + Constant.interestedPropertyIds.add(int.parse(propertyId)); + } else { + Constant.interestedPropertyIds.remove(int.parse(propertyId)); + } + + emit(ChangeInterestInPropertySuccess(interest: interest)); + } catch (e) { + emit(ChangeInterestInPropertyFailure(e.toString())); + } + } +} diff --git a/lib/data/cubits/property/create_advertisement_cubit.dart b/lib/data/cubits/property/create_advertisement_cubit.dart new file mode 100644 index 0000000..859b9db --- /dev/null +++ b/lib/data/cubits/property/create_advertisement_cubit.dart @@ -0,0 +1,59 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:io'; + +import 'package:ebroker/data/model/property_model.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../Repositories/advertisement_repository.dart'; + +abstract class CreateAdvertisementState {} + +class CreateAdvertisementInitial extends CreateAdvertisementState {} + +class CreateAdvertisementInProgress extends CreateAdvertisementState {} + +class CreateAdvertisementSuccess extends CreateAdvertisementState { + final dynamic proeprtyId; + final PropertyModel property; + CreateAdvertisementSuccess({ + required this.property, + required this.proeprtyId, + }); +} + +class CreateAdvertisementFailure extends CreateAdvertisementState { + final String errorMessage; + CreateAdvertisementFailure( + this.errorMessage, + ); +} + +class CreateAdvertisementCubit extends Cubit { + final AdvertisementRepository _advertisementRepository = + AdvertisementRepository(); + + CreateAdvertisementCubit() + : super( + CreateAdvertisementInitial(), + ); + + Future create({ + required String type, + required String propertyId, + File? image, + }) async { + try { + emit(CreateAdvertisementInProgress()); + Map result = await _advertisementRepository.create( + propertyId: propertyId, + type: type, + image: image, + ); + emit(CreateAdvertisementSuccess( + proeprtyId: propertyId, + property: PropertyModel.fromMap(result['data'][0]))); + } catch (e) { + emit(CreateAdvertisementFailure(e.toString())); + } + } +} diff --git a/lib/data/cubits/property/create_property_cubit.dart b/lib/data/cubits/property/create_property_cubit.dart new file mode 100644 index 0000000..4751e31 --- /dev/null +++ b/lib/data/cubits/property/create_property_cubit.dart @@ -0,0 +1,52 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'package:ebroker/data/model/property_model.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../Repositories/property_repository.dart'; + +abstract class CreatePropertyState {} + +class CreatePropertyInitial extends CreatePropertyState {} + +class CreatePropertyInProgress extends CreatePropertyState {} + +class CreatePropertySuccess extends CreatePropertyState { + final PropertyModel? propertyModel; + CreatePropertySuccess({ + this.propertyModel, + }); +} + +class CreatePropertyFailure extends CreatePropertyState { + final String errorMessage; + + CreatePropertyFailure(this.errorMessage); +} + +class CreatePropertyCubit extends Cubit { + final PropertyRepository _propertyRepository = PropertyRepository(); + + CreatePropertyCubit() : super(CreatePropertyInitial()); + + Future create({required Map parameters}) async { + try { + emit(CreatePropertyInProgress()); + var result = + await _propertyRepository.createProperty(parameters: parameters); + + if (result['data'] != null) { + emit(CreatePropertySuccess( + propertyModel: PropertyModel.fromMap(result['data'][0]))); + } else { + if (result is Map) { + emit(CreatePropertyFailure(result['message'].toString())); + } else { + emit(CreatePropertyFailure("Something went wrong".toString())); + } + } + } catch (e) { + emit(CreatePropertyFailure(e.toString())); + } + } +} diff --git a/lib/data/cubits/property/delete_property_cubit.dart b/lib/data/cubits/property/delete_property_cubit.dart new file mode 100644 index 0000000..a1be088 --- /dev/null +++ b/lib/data/cubits/property/delete_property_cubit.dart @@ -0,0 +1,32 @@ +import '../../Repositories/property_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class DeletePropertyState {} + +class DeletePropertyInitial extends DeletePropertyState {} + +class DeletePropertyInProgress extends DeletePropertyState {} + +class DeletePropertySuccess extends DeletePropertyState {} + +class DeletePropertyFailure extends DeletePropertyState { + final String errorMessage; + + DeletePropertyFailure(this.errorMessage); +} + +class DeletePropertyCubit extends Cubit { + final PropertyRepository _propertyRepository = PropertyRepository(); + DeletePropertyCubit() : super(DeletePropertyInitial()); + + Future delete(int id) async { + try { + emit(DeletePropertyInProgress()); + + await _propertyRepository.deleteProperty(id); + emit(DeletePropertySuccess()); + } catch (e) { + emit(DeletePropertyFailure(e.toString())); + } + } +} diff --git a/lib/data/cubits/property/favorite_id_properties.dart b/lib/data/cubits/property/favorite_id_properties.dart new file mode 100644 index 0000000..9e4d515 --- /dev/null +++ b/lib/data/cubits/property/favorite_id_properties.dart @@ -0,0 +1,28 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:flutter_bloc/flutter_bloc.dart'; + +class FavoriteIDsCubit extends Cubit { + FavoriteIDsCubit() : super(FavoriteIDsState(list: {})); + + void addToFavoriteLocal(int id) { + state.list.add(id); + emit(FavoriteIDsState(list: state.list)); + } + + void removeFromFavourite(int id) { + state.list.remove(id); + emit(FavoriteIDsState(list: state.list)); + } + + bool isFavourite(int id) { + return state.list.contains(id); + } +} + +class FavoriteIDsState { + Set list; + + FavoriteIDsState({ + required this.list, + }); +} diff --git a/lib/data/cubits/property/fetch_city_property_list.dart b/lib/data/cubits/property/fetch_city_property_list.dart new file mode 100644 index 0000000..93a5b78 --- /dev/null +++ b/lib/data/cubits/property/fetch_city_property_list.dart @@ -0,0 +1,145 @@ +import 'package:ebroker/data/model/property_model.dart'; + +import '../../../Ui/screens/proprties/viewAll.dart'; +import '../../../exports/main_export.dart'; +import '../../Repositories/property_repository.dart'; +import '../../model/data_output.dart'; + +abstract class FetchCityPropertyListState {} + +class FetchCityPropertyInitial extends FetchCityPropertyListState {} + +class FetchCityPropertyInProgress extends FetchCityPropertyListState {} + +class FetchCityPropertySuccess extends FetchCityPropertyListState + implements PropertySuccessStateWireframe { + @override + final bool isLoadingMore; + final bool loadingMoreError; + @override + final List properties; + final int offset; + final int total; + final String cityName; + FetchCityPropertySuccess( + {required this.isLoadingMore, + required this.loadingMoreError, + required this.properties, + required this.cityName, + required this.offset, + required this.total}); + + @override + set isLoadingMore(bool _isLoadingMore) { + // TODO: implement isLoadingMore + } + + @override + set properties(List _properties) { + // TODO: implement properties + } + + FetchCityPropertySuccess copyWith( + {bool? isLoadingMore, + bool? loadingMoreError, + List? properties, + int? offset, + int? total, + String? cityName}) { + return FetchCityPropertySuccess( + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + loadingMoreError: loadingMoreError ?? this.loadingMoreError, + properties: properties ?? this.properties, + offset: offset ?? this.offset, + total: total ?? this.total, + cityName: cityName ?? this.cityName, + ); + } +} + +class FetchCityPropertyFail extends FetchCityPropertyListState + implements PropertyErrorStateWireframe { + final dynamic error; + FetchCityPropertyFail(this.error); + + @override + set error(_error) { + // TODO: implement error + } +} + +class FetchCityPropertyList extends Cubit + implements PropertyCubitWireframe { + FetchCityPropertyList() : super(FetchCityPropertyInitial()); + final PropertyRepository _propertyRepository = PropertyRepository(); + + @override + void fetch({bool? forceRefresh, String? cityName}) async { + if (forceRefresh != true) { + if (state is FetchCityPropertySuccess) { + // WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + await Future.delayed( + const Duration(seconds: AppSettings.hiddenAPIProcessDelay)); + // }); + } else { + emit(FetchCityPropertyInProgress()); + } + } else { + emit(FetchCityPropertyInProgress()); + } + try { + DataOutput result = await _propertyRepository + .fetchPropertiesFromCityName(cityName!, offset: 0); + // log("###THESE ARE ${result.modelList}"); + emit(FetchCityPropertySuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + cityName: cityName, + total: result.total)); + } catch (e) { + emit(FetchCityPropertyFail(e as dynamic)); + } + } + + @override + void fetchMore() async { + try { + if (state is FetchCityPropertySuccess) { + if ((state as FetchCityPropertySuccess).isLoadingMore) { + return; + } + emit((state as FetchCityPropertySuccess).copyWith(isLoadingMore: true)); + DataOutput result = + await _propertyRepository.fetchPropertiesFromCityName( + (state as FetchCityPropertySuccess).cityName, + offset: (state as FetchCityPropertySuccess).properties.length, + ); + + FetchCityPropertySuccess propertiesState = + (state as FetchCityPropertySuccess); + propertiesState.properties.addAll(result.modelList); + emit(FetchCityPropertySuccess( + cityName: (state as FetchCityPropertySuccess).cityName, + isLoadingMore: false, + loadingMoreError: false, + properties: propertiesState.properties, + offset: (state as FetchCityPropertySuccess).properties.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchCityPropertySuccess) + .copyWith(isLoadingMore: false, loadingMoreError: true)); + } + } + + @override + bool hasMoreData() { + if (state is FetchCityPropertySuccess) { + return (state as FetchCityPropertySuccess).properties.length < + (state as FetchCityPropertySuccess).total; + } + return false; + } +} diff --git a/lib/data/cubits/property/fetch_home_properties_cubit.dart b/lib/data/cubits/property/fetch_home_properties_cubit.dart new file mode 100644 index 0000000..cb5b23e --- /dev/null +++ b/lib/data/cubits/property/fetch_home_properties_cubit.dart @@ -0,0 +1,117 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'package:ebroker/data/Repositories/property_repository.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class FetchHomePropertiesState {} + +class FetchHomePropertiesInitial extends FetchHomePropertiesState {} + +class FetchHomePropertiesInProgress extends FetchHomePropertiesState {} + +class FetchHomePropertiesSuccess extends FetchHomePropertiesState { + final int total; + final int offset; + final bool isLoadingMore; + final bool hasError; + final List property; + + FetchHomePropertiesSuccess({ + required this.total, + required this.offset, + required this.isLoadingMore, + required this.hasError, + required this.property, + }); + + FetchHomePropertiesSuccess copyWith({ + int? total, + int? offset, + bool? isLoadingMore, + bool? hasMoreData, + List? property, + }) { + return FetchHomePropertiesSuccess( + total: total ?? this.total, + offset: offset ?? this.offset, + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + hasError: hasMoreData ?? hasError, + property: property ?? this.property, + ); + } +} + +class FetchHomePropertiesFailure extends FetchHomePropertiesState { + final String errorMessage; + + FetchHomePropertiesFailure(this.errorMessage); +} + +class FetchHomePropertiesCubit extends Cubit { + final PropertyRepository _propertyRepository = PropertyRepository(); + FetchHomePropertiesCubit() : super(FetchHomePropertiesInitial()); + + void fetchProperty() async { + try { + emit(FetchHomePropertiesInProgress()); + DataOutput result = + await _propertyRepository.fetchProperty(offset: 0); + emit(FetchHomePropertiesSuccess( + hasError: false, + isLoadingMore: false, + offset: 0, + property: result.modelList, + total: result.total, + )); + } catch (e) { + emit(FetchHomePropertiesFailure(e.toString())); + } + } + + Future fetchMoreProperty() async { + try { + if (state is FetchHomePropertiesSuccess) { + if ((state as FetchHomePropertiesSuccess).isLoadingMore) { + return; + } + emit((state as FetchHomePropertiesSuccess) + .copyWith(isLoadingMore: true)); + + DataOutput result = + await _propertyRepository.fetchProperty( + offset: (state as FetchHomePropertiesSuccess).property.length, + ); + + FetchHomePropertiesSuccess bookingsState = + (state as FetchHomePropertiesSuccess); + bookingsState.property.addAll(result.modelList); + emit( + FetchHomePropertiesSuccess( + isLoadingMore: false, + hasError: false, + property: bookingsState.property, + offset: (state as FetchHomePropertiesSuccess).property.length, + total: result.total, + ), + ); + } + } catch (e) { + emit( + (state as FetchHomePropertiesSuccess).copyWith( + isLoadingMore: false, + hasMoreData: true, + ), + ); + } + } + + bool hasMoreData() { + if (state is FetchHomePropertiesSuccess) { + return (state as FetchHomePropertiesSuccess).property.length < + (state as FetchHomePropertiesSuccess).total; + } + return false; + } +} diff --git a/lib/data/cubits/property/fetch_most_liked_properties.dart b/lib/data/cubits/property/fetch_most_liked_properties.dart new file mode 100644 index 0000000..bc9a5e0 --- /dev/null +++ b/lib/data/cubits/property/fetch_most_liked_properties.dart @@ -0,0 +1,273 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +import 'package:ebroker/Ui/screens/proprties/viewAll.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../../settings.dart'; +import '../../../utils/Network/networkAvailability.dart'; +import '../../Repositories/property_repository.dart'; +import '../../model/data_output.dart'; +import '../../model/property_model.dart'; + +abstract class FetchMostLikedPropertiesState {} + +class FetchMostLikedPropertiesInitial extends FetchMostLikedPropertiesState {} + +class FetchMostLikedPropertiesInProgress + extends FetchMostLikedPropertiesState {} + +class FetchMostLikedPropertiesSuccess extends FetchMostLikedPropertiesState + implements PropertySuccessStateWireframe { + @override + final bool isLoadingMore; + final bool loadingMoreError; + @override + final List properties; + final int offset; + final int total; + FetchMostLikedPropertiesSuccess({ + required this.isLoadingMore, + required this.loadingMoreError, + required this.properties, + required this.offset, + required this.total, + }); + + FetchMostLikedPropertiesSuccess copyWith({ + bool? isLoadingMore, + bool? loadingMoreError, + List? properties, + int? offset, + int? total, + }) { + return FetchMostLikedPropertiesSuccess( + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + loadingMoreError: loadingMoreError ?? this.loadingMoreError, + properties: properties ?? this.properties, + offset: offset ?? this.offset, + total: total ?? this.total, + ); + } + + Map toMap() { + return { + 'isLoadingMore': isLoadingMore, + 'loadingMoreError': loadingMoreError, + 'properties': properties.map((x) => x.toMap()).toList(), + 'offset': offset, + 'total': total, + }; + } + + factory FetchMostLikedPropertiesSuccess.fromMap(Map map) { + return FetchMostLikedPropertiesSuccess( + isLoadingMore: map['isLoadingMore'] as bool, + loadingMoreError: map['loadingMoreError'] as bool, + properties: List.from( + (map['properties']).map( + (x) => PropertyModel.fromMap(x as Map), + ), + ), + offset: map['offset'] as int, + total: map['total'] as int, + ); + } + + String toJson() => json.encode(toMap()); + + factory FetchMostLikedPropertiesSuccess.fromJson(String source) => + FetchMostLikedPropertiesSuccess.fromMap( + json.decode(source) as Map); + + @override + set isLoadingMore(bool _isLoadingMore) {} + + @override + set properties(List _properties) {} +} + +class FetchMostLikedPropertiesFailure extends FetchMostLikedPropertiesState + implements PropertyErrorStateWireframe { + final dynamic error; + FetchMostLikedPropertiesFailure(this.error); + + @override + set error(_error) { + // TODO: implement error + } +} + +class FetchMostLikedPropertiesCubit extends Cubit + with HydratedMixin + implements PropertyCubitWireframe { + FetchMostLikedPropertiesCubit() : super(FetchMostLikedPropertiesInitial()); + + final PropertyRepository _propertyRepository = PropertyRepository(); + + @override + Future fetch({bool? forceRefresh, bool? loadWithoutDelay}) async { + // if (state is FetchMostLikedPropertiesSuccess) { + // return; + // } + if (forceRefresh != true) { + if (state is FetchMostLikedPropertiesSuccess) { + // WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + await Future.delayed(Duration( + seconds: loadWithoutDelay == true + ? 0 + : AppSettings.hiddenAPIProcessDelay)); + // }); + } else { + emit(FetchMostLikedPropertiesInProgress()); + } + } else { + emit(FetchMostLikedPropertiesInProgress()); + } + try { + if (forceRefresh == true) { + DataOutput result = await _propertyRepository + .fetchMostLikeProperty(offset: 0, sendCityName: true); + + emit(FetchMostLikedPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total)); + } else { + if (state is! FetchMostLikedPropertiesSuccess) { + DataOutput result = + await _propertyRepository.fetchMostLikeProperty( + offset: 0, + sendCityName: true, + ); + + emit( + FetchMostLikedPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total), + ); + } else { + await CheckInternet.check( + onInternet: () async { + DataOutput result = + await _propertyRepository.fetchMostLikeProperty( + offset: 0, + sendCityName: true, + ); + + emit( + FetchMostLikedPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total), + ); + }, + onNoInternet: () { + emit( + FetchMostLikedPropertiesSuccess( + total: (state as FetchMostLikedPropertiesSuccess).total, + offset: (state as FetchMostLikedPropertiesSuccess).offset, + isLoadingMore: (state as FetchMostLikedPropertiesSuccess) + .isLoadingMore, + loadingMoreError: (state as FetchMostLikedPropertiesSuccess) + .loadingMoreError, + properties: + (state as FetchMostLikedPropertiesSuccess).properties), + ); + }, + ); + } + } + } catch (e) { + emit(FetchMostLikedPropertiesFailure(e as dynamic)); + } + } + + void update(PropertyModel model) { + if (state is FetchMostLikedPropertiesSuccess) { + List properties = + (state as FetchMostLikedPropertiesSuccess).properties; + + var index = properties.indexWhere((element) => element.id == model.id); + + if (index != -1) { + properties[index] = model; + } + + emit((state as FetchMostLikedPropertiesSuccess) + .copyWith(properties: properties)); + } + } + + @override + Future fetchMore() async { + try { + if (state is FetchMostLikedPropertiesSuccess) { + if ((state as FetchMostLikedPropertiesSuccess).isLoadingMore) { + return; + } + emit((state as FetchMostLikedPropertiesSuccess) + .copyWith(isLoadingMore: true)); + DataOutput result = + await _propertyRepository.fetchMostLikeProperty( + offset: (state as FetchMostLikedPropertiesSuccess) + .properties + .length, + sendCityName: true); + + FetchMostLikedPropertiesSuccess propertiesState = + (state as FetchMostLikedPropertiesSuccess); + propertiesState.properties.addAll(result.modelList); + emit(FetchMostLikedPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: propertiesState.properties, + offset: + (state as FetchMostLikedPropertiesSuccess).properties.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchMostLikedPropertiesSuccess) + .copyWith(isLoadingMore: false, loadingMoreError: true)); + } + } + + @override + bool hasMoreData() { + if (state is FetchMostLikedPropertiesSuccess) { + return (state as FetchMostLikedPropertiesSuccess).properties.length < + (state as FetchMostLikedPropertiesSuccess).total; + } + return false; + } + + @override + FetchMostLikedPropertiesState? fromJson(Map json) { + try { + var state = json['cubit_state']; + + if (state == "FetchMostLikedPropertiesSuccess") { + return FetchMostLikedPropertiesSuccess.fromMap(json); + } + } catch (e) {} + + return null; + } + + @override + Map? toJson(FetchMostLikedPropertiesState state) { + if (state is FetchMostLikedPropertiesSuccess) { + Map map = state.toMap(); + map['cubit_state'] = "FetchMostLikedPropertiesSuccess"; + return map; + } + return null; + } +} diff --git a/lib/data/cubits/property/fetch_most_viewed_properties_cubit.dart b/lib/data/cubits/property/fetch_most_viewed_properties_cubit.dart new file mode 100644 index 0000000..c1a7d43 --- /dev/null +++ b/lib/data/cubits/property/fetch_most_viewed_properties_cubit.dart @@ -0,0 +1,267 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +import 'package:ebroker/Ui/screens/proprties/viewAll.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../../settings.dart'; +import '../../../utils/Network/networkAvailability.dart'; +import '../../Repositories/property_repository.dart'; +import '../../model/data_output.dart'; +import '../../model/property_model.dart'; + +abstract class FetchMostViewedPropertiesState {} + +class FetchMostViewedPropertiesInitial extends FetchMostViewedPropertiesState {} + +class FetchMostViewedPropertiesInProgress + extends FetchMostViewedPropertiesState {} + +class FetchMostViewedPropertiesSuccess extends FetchMostViewedPropertiesState + implements PropertySuccessStateWireframe { + @override + final bool isLoadingMore; + final bool loadingMoreError; + @override + final List properties; + final int offset; + final int total; + FetchMostViewedPropertiesSuccess({ + required this.isLoadingMore, + required this.loadingMoreError, + required this.properties, + required this.offset, + required this.total, + }); + + FetchMostViewedPropertiesSuccess copyWith({ + bool? isLoadingMore, + bool? loadingMoreError, + List? properties, + int? offset, + int? total, + }) { + return FetchMostViewedPropertiesSuccess( + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + loadingMoreError: loadingMoreError ?? this.loadingMoreError, + properties: properties ?? this.properties, + offset: offset ?? this.offset, + total: total ?? this.total, + ); + } + + Map toMap() { + return { + 'isLoadingMore': isLoadingMore, + 'loadingMoreError': loadingMoreError, + 'properties': properties.map((x) => x.toMap()).toList(), + 'offset': offset, + 'total': total, + }; + } + + factory FetchMostViewedPropertiesSuccess.fromMap(Map map) { + return FetchMostViewedPropertiesSuccess( + isLoadingMore: map['isLoadingMore'] as bool, + loadingMoreError: map['loadingMoreError'] as bool, + properties: List.from( + (map['properties']).map( + (x) => PropertyModel.fromMap(x as Map), + ), + ), + offset: map['offset'] as int, + total: map['total'] as int, + ); + } + + String toJson() => json.encode(toMap()); + + factory FetchMostViewedPropertiesSuccess.fromJson(String source) => + FetchMostViewedPropertiesSuccess.fromMap( + json.decode(source) as Map); + + @override + set properties(List _properties) { + // TODO: implement properties + } + + @override + set isLoadingMore(bool _isLoadingMore) { + // TODO: implement isLoadingMore + } +} + +class FetchMostViewedPropertiesFailure extends FetchMostViewedPropertiesState + implements PropertyErrorStateWireframe { + final dynamic error; + FetchMostViewedPropertiesFailure(this.error); + + @override + set error(_error) { + // TODO: implement error + } +} + +class FetchMostViewedPropertiesCubit + extends Cubit + with HydratedMixin + implements PropertyCubitWireframe { + FetchMostViewedPropertiesCubit() : super(FetchMostViewedPropertiesInitial()); + + final PropertyRepository _propertyRepository = PropertyRepository(); + + fetch({bool? forceRefresh, bool? loadWithoutDelay}) async { + // if (state is FetchMostViewedPropertiesSuccess) { + // return; + // } + if (forceRefresh != true) { + if (state is FetchMostViewedPropertiesSuccess) { + // WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + await Future.delayed(Duration( + seconds: loadWithoutDelay == true + ? 0 + : AppSettings.hiddenAPIProcessDelay)); + // }); + } else { + emit(FetchMostViewedPropertiesInProgress()); + } + } else { + emit(FetchMostViewedPropertiesInProgress()); + } + try { + if (forceRefresh == true) { + DataOutput result = await _propertyRepository + .fetchMostViewedProperty(offset: 0, sendCityName: true); + + emit(FetchMostViewedPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total)); + } else { + if (state is! FetchMostViewedPropertiesSuccess) { + DataOutput result = await _propertyRepository + .fetchMostViewedProperty(offset: 0, sendCityName: true); + + emit(FetchMostViewedPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total)); + } else { + await CheckInternet.check( + onInternet: () async { + DataOutput result = await _propertyRepository + .fetchMostViewedProperty(offset: 0, sendCityName: true); + + emit(FetchMostViewedPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total)); + }, + onNoInternet: () { + emit( + FetchMostViewedPropertiesSuccess( + total: (state as FetchMostViewedPropertiesSuccess).total, + offset: (state as FetchMostViewedPropertiesSuccess).offset, + isLoadingMore: (state as FetchMostViewedPropertiesSuccess) + .isLoadingMore, + loadingMoreError: + (state as FetchMostViewedPropertiesSuccess) + .loadingMoreError, + properties: + (state as FetchMostViewedPropertiesSuccess).properties), + ); + }, + ); + } + } + } catch (e, st) { + print(st); + emit(FetchMostViewedPropertiesFailure(e as dynamic)); + } + } + + void update(PropertyModel model) { + if (state is FetchMostViewedPropertiesSuccess) { + List properties = + (state as FetchMostViewedPropertiesSuccess).properties; + + var index = properties.indexWhere((element) => element.id == model.id); + + if (index != -1) { + properties[index] = model; + } + + emit((state as FetchMostViewedPropertiesSuccess) + .copyWith(properties: properties)); + } + } + + Future fetchMore() async { + try { + if (state is FetchMostViewedPropertiesSuccess) { + if ((state as FetchMostViewedPropertiesSuccess).isLoadingMore) { + return; + } + emit((state as FetchMostViewedPropertiesSuccess) + .copyWith(isLoadingMore: true)); + DataOutput result = + await _propertyRepository.fetchMostViewedProperty( + offset: (state as FetchMostViewedPropertiesSuccess) + .properties + .length, + sendCityName: true); + + FetchMostViewedPropertiesSuccess propertiesState = + (state as FetchMostViewedPropertiesSuccess); + propertiesState.properties.addAll(result.modelList); + emit(FetchMostViewedPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: propertiesState.properties, + offset: + (state as FetchMostViewedPropertiesSuccess).properties.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchMostViewedPropertiesSuccess) + .copyWith(isLoadingMore: false, loadingMoreError: true)); + } + } + + bool hasMoreData() { + if (state is FetchMostViewedPropertiesSuccess) { + return (state as FetchMostViewedPropertiesSuccess).properties.length < + (state as FetchMostViewedPropertiesSuccess).total; + } + return false; + } + + @override + FetchMostViewedPropertiesState? fromJson(Map json) { + try { + var state = json['cubit_state']; + + if (state == "FetchMostViewedPropertiesSuccess") { + return FetchMostViewedPropertiesSuccess.fromMap(json); + } + } catch (e) {} + + return null; + } + + @override + Map? toJson(FetchMostViewedPropertiesState state) { + if (state is FetchMostViewedPropertiesSuccess) { + Map map = state.toMap(); + map['cubit_state'] = "FetchMostViewedPropertiesSuccess"; + return map; + } + return null; + } +} diff --git a/lib/data/cubits/property/fetch_my_promoted_propertys_cubit.dart b/lib/data/cubits/property/fetch_my_promoted_propertys_cubit.dart new file mode 100644 index 0000000..e1c651b --- /dev/null +++ b/lib/data/cubits/property/fetch_my_promoted_propertys_cubit.dart @@ -0,0 +1,140 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../Repositories/property_repository.dart'; +import '../../model/data_output.dart'; +import '../../model/property_model.dart'; + +abstract class FetchMyPromotedPropertysState {} + +class FetchMyPromotedPropertysInitial extends FetchMyPromotedPropertysState {} + +class FetchMyPromotedPropertysInProgress + extends FetchMyPromotedPropertysState {} + +class FetchMyPromotedPropertysSuccess extends FetchMyPromotedPropertysState { + final bool isLoadingMore; + final bool loadingMoreError; + final List propertymodel; + final int offset; + final int total; + FetchMyPromotedPropertysSuccess({ + required this.isLoadingMore, + required this.loadingMoreError, + required this.propertymodel, + required this.offset, + required this.total, + }); + + FetchMyPromotedPropertysSuccess copyWith({ + bool? isLoadingMore, + bool? loadingMoreError, + List? propertymodel, + int? offset, + int? total, + }) { + return FetchMyPromotedPropertysSuccess( + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + loadingMoreError: loadingMoreError ?? this.loadingMoreError, + propertymodel: propertymodel ?? this.propertymodel, + offset: offset ?? this.offset, + total: total ?? this.total, + ); + } +} + +class FetchMyPromotedPropertysFailure extends FetchMyPromotedPropertysState { + final dynamic errorMessage; + FetchMyPromotedPropertysFailure(this.errorMessage); +} + +class FetchMyPromotedPropertysCubit + extends Cubit { + FetchMyPromotedPropertysCubit() : super(FetchMyPromotedPropertysInitial()); + + final PropertyRepository _propertyRepository = PropertyRepository(); + + Future fetchMyPromotedPropertys() async { + try { + emit(FetchMyPromotedPropertysInProgress()); + + DataOutput result = + await _propertyRepository.fetchMyPromotedProeprties(offset: 0); + + emit( + FetchMyPromotedPropertysSuccess( + isLoadingMore: false, + loadingMoreError: false, + propertymodel: result.modelList, + offset: 0, + total: result.total, + ), + ); + } catch (e) { + emit(FetchMyPromotedPropertysFailure(e)); + } + } + + void delete(dynamic id) { + if (state is FetchMyPromotedPropertysSuccess) { + List propertymodel = + (state as FetchMyPromotedPropertysSuccess).propertymodel; + propertymodel.removeWhere((element) => element.id == id); + + emit((state as FetchMyPromotedPropertysSuccess) + .copyWith(propertymodel: propertymodel)); + } + } + + Future fetchMyPromotedPropertysMore() async { + try { + if (state is FetchMyPromotedPropertysSuccess) { + if ((state as FetchMyPromotedPropertysSuccess).isLoadingMore) { + return; + } + emit((state as FetchMyPromotedPropertysSuccess) + .copyWith(isLoadingMore: true)); + DataOutput result = + await _propertyRepository.fetchMyPromotedProeprties( + offset: + (state as FetchMyPromotedPropertysSuccess).propertymodel.length, + ); + + FetchMyPromotedPropertysSuccess propertymodelState = + (state as FetchMyPromotedPropertysSuccess); + propertymodelState.propertymodel.addAll(result.modelList); + emit(FetchMyPromotedPropertysSuccess( + isLoadingMore: false, + loadingMoreError: false, + propertymodel: propertymodelState.propertymodel, + offset: + (state as FetchMyPromotedPropertysSuccess).propertymodel.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchMyPromotedPropertysSuccess) + .copyWith(isLoadingMore: false, loadingMoreError: true)); + } + } + + bool hasMoreData() { + if (state is FetchMyPromotedPropertysSuccess) { + return (state as FetchMyPromotedPropertysSuccess).propertymodel.length < + (state as FetchMyPromotedPropertysSuccess).total; + } + return false; + } + void update(PropertyModel model) { + if (state is FetchMyPromotedPropertysSuccess) { + List properties = + (state as FetchMyPromotedPropertysSuccess).propertymodel; + + var index = properties.indexWhere((element) => element.id == model.id); + if (index != -1) { + properties[index] = model; + } + + emit((state as FetchMyPromotedPropertysSuccess) + .copyWith(propertymodel: properties)); + } + } +} diff --git a/lib/data/cubits/property/fetch_my_properties_cubit.dart b/lib/data/cubits/property/fetch_my_properties_cubit.dart new file mode 100644 index 0000000..c1c096a --- /dev/null +++ b/lib/data/cubits/property/fetch_my_properties_cubit.dart @@ -0,0 +1,182 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'package:ebroker/data/Repositories/property_repository.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class FetchMyPropertiesState {} + +class FetchMyPropertiesInitial extends FetchMyPropertiesState {} + +class FetchMyPropertiesInProgress extends FetchMyPropertiesState {} + +class FetchMyPropertiesSuccess extends FetchMyPropertiesState { + final int total; + final int offset; + final bool isLoadingMore; + final bool hasError; + final List myProperty; + FetchMyPropertiesSuccess({ + required this.total, + required this.offset, + required this.isLoadingMore, + required this.hasError, + required this.myProperty, + }); + + FetchMyPropertiesSuccess copyWith({ + int? total, + int? offset, + bool? isLoadingMore, + bool? hasMoreData, + List? myProperty, + }) { + return FetchMyPropertiesSuccess( + total: total ?? this.total, + offset: offset ?? this.offset, + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + hasError: hasMoreData ?? hasError, + myProperty: myProperty ?? this.myProperty, + ); + } +} + +class FetchMyPropertiesFailure extends FetchMyPropertiesState { + final dynamic errorMessage; + + FetchMyPropertiesFailure(this.errorMessage); +} + +class FetchMyPropertiesCubit extends Cubit { + FetchMyPropertiesCubit() : super(FetchMyPropertiesInitial()); + + final PropertyRepository _propertyRepository = PropertyRepository(); + Future fetchMyProperties({ + required String type, + }) async { + try { + emit(FetchMyPropertiesInProgress()); + DataOutput result = + await _propertyRepository.fetchMyProperties(offset: 0, type: type); + emit(FetchMyPropertiesSuccess( + hasError: false, + isLoadingMore: false, + myProperty: result.modelList, + total: result.total, + offset: 0)); + } catch (e) { + emit(FetchMyPropertiesFailure(e)); + } + } + + void updateStatus(int propertyId, String currentType) { + try { + if (state is FetchMyPropertiesSuccess) { + List propertyList = + (state as FetchMyPropertiesSuccess).myProperty; + int index = propertyList.indexWhere((element) { + return element.id == propertyId; + }); + if (currentType == "Sell") { + propertyList[index].properyType = "Sold"; + } + if (currentType == "Rent") { + propertyList[index].properyType = "Rented"; + } + + if (currentType == "Rented") { + propertyList[index].properyType = "Rent"; + } + if (kDebugMode) { + if (currentType == "Sold") { + propertyList[index].properyType = "Sell"; + } + } + + emit((state as FetchMyPropertiesSuccess) + .copyWith(myProperty: propertyList)); + } + } catch (e) {} + } + + void update(PropertyModel model) { + if (state is FetchMyPropertiesSuccess) { + List properties = + (state as FetchMyPropertiesSuccess).myProperty; + + var index = properties.indexWhere((element) => element.id == model.id); + + if (index != -1) { + properties[index] = model; + } + + emit( + (state as FetchMyPropertiesSuccess).copyWith(myProperty: properties)); + } + } + + Future fetchMoreProperties({required String type}) async { + try { + if (state is FetchMyPropertiesSuccess) { + if ((state as FetchMyPropertiesSuccess).isLoadingMore) { + return; + } + emit((state as FetchMyPropertiesSuccess).copyWith(isLoadingMore: true)); + + DataOutput result = + await _propertyRepository.fetchMyProperties( + offset: (state as FetchMyPropertiesSuccess).myProperty.length, + type: type); + + FetchMyPropertiesSuccess bookingsState = + (state as FetchMyPropertiesSuccess); + bookingsState.myProperty.addAll(result.modelList); + emit( + FetchMyPropertiesSuccess( + isLoadingMore: false, + hasError: false, + myProperty: bookingsState.myProperty, + offset: (state as FetchMyPropertiesSuccess).myProperty.length, + total: result.total, + ), + ); + } + } catch (e) { + emit( + (state as FetchMyPropertiesSuccess).copyWith( + isLoadingMore: false, + hasMoreData: true, + ), + ); + } + } + + void addLocal(PropertyModel model) { + try { + if (state is FetchMyPropertiesSuccess) { + List myProperty = + (state as FetchMyPropertiesSuccess).myProperty; + if (myProperty.isNotEmpty) { + myProperty.insert(0, model); + } else { + myProperty.add(model); + } + + emit((state as FetchMyPropertiesSuccess) + .copyWith(myProperty: myProperty)); + } + } catch (e, st) { + throw st; + } + } + + bool hasMoreData() { + if (state is FetchMyPropertiesSuccess) { + return (state as FetchMyPropertiesSuccess).myProperty.length < + (state as FetchMyPropertiesSuccess).total; + } + return false; + } +} diff --git a/lib/data/cubits/property/fetch_nearby_property_cubit.dart b/lib/data/cubits/property/fetch_nearby_property_cubit.dart new file mode 100644 index 0000000..2793f35 --- /dev/null +++ b/lib/data/cubits/property/fetch_nearby_property_cubit.dart @@ -0,0 +1,266 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +import 'package:ebroker/Ui/screens/proprties/viewAll.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../../settings.dart'; +import '../../../utils/Network/networkAvailability.dart'; +import '../../Repositories/property_repository.dart'; +import '../../model/data_output.dart'; +import '../../model/property_model.dart'; + +abstract class FetchNearbyPropertiesState {} + +class FetchNearbyPropertiesInitial extends FetchNearbyPropertiesState {} + +class FetchNearbyPropertiesInProgress extends FetchNearbyPropertiesState {} + +class FetchNearbyPropertiesSuccess extends FetchNearbyPropertiesState + implements PropertySuccessStateWireframe { + @override + final bool isLoadingMore; + final bool loadingMoreError; + @override + final List properties; + final int offset; + final int total; + FetchNearbyPropertiesSuccess({ + required this.isLoadingMore, + required this.loadingMoreError, + required this.properties, + required this.offset, + required this.total, + }); + + FetchNearbyPropertiesSuccess copyWith({ + bool? isLoadingMore, + bool? loadingMoreError, + List? properties, + int? offset, + int? total, + }) { + return FetchNearbyPropertiesSuccess( + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + loadingMoreError: loadingMoreError ?? this.loadingMoreError, + properties: properties ?? this.properties, + offset: offset ?? this.offset, + total: total ?? this.total, + ); + } + + Map toMap() { + return { + 'isLoadingMore': isLoadingMore, + 'loadingMoreError': loadingMoreError, + 'properties': properties.map((x) => x.toMap()).toList(), + 'offset': offset, + 'total': total, + }; + } + + factory FetchNearbyPropertiesSuccess.fromMap(Map map) { + return FetchNearbyPropertiesSuccess( + isLoadingMore: map['isLoadingMore'] as bool, + loadingMoreError: map['loadingMoreError'] as bool, + properties: List.from( + (map['properties']).map( + (x) => PropertyModel.fromMap(x as Map), + ), + ), + offset: map['offset'] as int, + total: map['total'] as int, + ); + } + + String toJson() => json.encode(toMap()); + + factory FetchNearbyPropertiesSuccess.fromJson(String source) => + FetchNearbyPropertiesSuccess.fromMap( + json.decode(source) as Map); + + @override + set isLoadingMore(bool _isLoadingMore) { + // TODO: implement isLoadingMore + } + + @override + set properties(List _properties) { + // TODO: implement properties + } +} + +class FetchNearbyPropertiesFailure extends FetchNearbyPropertiesState + implements PropertyErrorStateWireframe { + final dynamic error; + FetchNearbyPropertiesFailure(this.error); + + @override + set error(_error) { + // TODO: implement error + } +} + +class FetchNearbyPropertiesCubit extends Cubit + with HydratedMixin + implements PropertyCubitWireframe { + FetchNearbyPropertiesCubit() : super(FetchNearbyPropertiesInitial()); + + final PropertyRepository _propertyRepository = PropertyRepository(); + + @override + Future fetch({bool? forceRefresh, bool? loadWithoutDelay}) async { + // if (state is FetchNearbyPropertiesSuccess) { + // return; + // } + if (forceRefresh != true) { + if (state is FetchNearbyPropertiesSuccess) { + await Future.delayed(Duration( + seconds: loadWithoutDelay == true + ? 0 + : AppSettings.hiddenAPIProcessDelay)); + } else { + emit(FetchNearbyPropertiesInProgress()); + } + } else { + emit(FetchNearbyPropertiesInProgress()); + } + + try { + if (forceRefresh == true) { + DataOutput result = + await _propertyRepository.fetchNearByProperty( + offset: 0, + ); + emit(FetchNearbyPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total)); + } else { + if (state is! FetchNearbyPropertiesSuccess) { + DataOutput result = + await _propertyRepository.fetchNearByProperty( + offset: 0, + ); + emit(FetchNearbyPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total)); + } else { + await CheckInternet.check( + onInternet: () async { + DataOutput result = + await _propertyRepository.fetchNearByProperty( + offset: 0, + ); + emit(FetchNearbyPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total)); + }, + onNoInternet: () { + emit( + FetchNearbyPropertiesSuccess( + total: (state as FetchNearbyPropertiesSuccess).total, + offset: (state as FetchNearbyPropertiesSuccess).offset, + isLoadingMore: + (state as FetchNearbyPropertiesSuccess).isLoadingMore, + loadingMoreError: (state as FetchNearbyPropertiesSuccess) + .loadingMoreError, + properties: + (state as FetchNearbyPropertiesSuccess).properties), + ); + }, + ); + } + } + } catch (e) { + emit(FetchNearbyPropertiesFailure(e as dynamic)); + } + } + + void update(PropertyModel model) { + if (state is FetchNearbyPropertiesSuccess) { + List properties = + (state as FetchNearbyPropertiesSuccess).properties; + + var index = properties.indexWhere((element) => element.id == model.id); + + if (index != -1) { + properties[index] = model; + } + + emit((state as FetchNearbyPropertiesSuccess) + .copyWith(properties: properties)); + } + } + + @override + Future fetchMore() async { + try { + if (state is FetchNearbyPropertiesSuccess) { + if ((state as FetchNearbyPropertiesSuccess).isLoadingMore) { + return; + } + emit((state as FetchNearbyPropertiesSuccess) + .copyWith(isLoadingMore: true)); + DataOutput result = + await _propertyRepository.fetchNearByProperty( + offset: (state as FetchNearbyPropertiesSuccess).properties.length, + // sendCityName: true + ); + + FetchNearbyPropertiesSuccess propertiesState = + (state as FetchNearbyPropertiesSuccess); + propertiesState.properties.addAll(result.modelList); + emit(FetchNearbyPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: propertiesState.properties, + offset: (state as FetchNearbyPropertiesSuccess).properties.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchNearbyPropertiesSuccess) + .copyWith(isLoadingMore: false, loadingMoreError: true)); + } + } + + @override + bool hasMoreData() { + if (state is FetchNearbyPropertiesSuccess) { + return (state as FetchNearbyPropertiesSuccess).properties.length < + (state as FetchNearbyPropertiesSuccess).total; + } + return false; + } + + @override + FetchNearbyPropertiesState? fromJson(Map json) { + try { + var state = json['cubit_state']; + + if (state == "FetchNearbyPropertiesSuccess") { + return FetchNearbyPropertiesSuccess.fromMap(json); + } + } catch (e) {} + + return null; + } + + @override + Map? toJson(FetchNearbyPropertiesState state) { + if (state is FetchNearbyPropertiesSuccess) { + Map map = state.toMap(); + map['cubit_state'] = "FetchNearbyPropertiesSuccess"; + return map; + } + return null; + } +} diff --git a/lib/data/cubits/property/fetch_promoted_properties_cubit.dart b/lib/data/cubits/property/fetch_promoted_properties_cubit.dart new file mode 100644 index 0000000..74577cc --- /dev/null +++ b/lib/data/cubits/property/fetch_promoted_properties_cubit.dart @@ -0,0 +1,274 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +import 'package:ebroker/Ui/screens/proprties/viewAll.dart'; +import 'package:ebroker/utils/Network/networkAvailability.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../../settings.dart'; +import '../../Repositories/property_repository.dart'; +import '../../model/data_output.dart'; +import '../../model/property_model.dart'; + +abstract class FetchPromotedPropertiesState {} + +class FetchPromotedPropertiesInitial extends FetchPromotedPropertiesState {} + +class FetchPromotedPropertiesInProgress extends FetchPromotedPropertiesState {} + +class FetchPromotedPropertiesSuccess extends FetchPromotedPropertiesState + implements PropertySuccessStateWireframe { + @override + final bool isLoadingMore; + final bool loadingMoreError; + @override + final List properties; + final int offset; + final int total; + FetchPromotedPropertiesSuccess({ + required this.isLoadingMore, + required this.loadingMoreError, + required this.properties, + required this.offset, + required this.total, + }); + + FetchPromotedPropertiesSuccess copyWith({ + bool? isLoadingMore, + bool? loadingMoreError, + List? propertymodel, + int? offset, + int? total, + }) { + return FetchPromotedPropertiesSuccess( + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + loadingMoreError: loadingMoreError ?? this.loadingMoreError, + properties: propertymodel ?? this.properties, + offset: offset ?? this.offset, + total: total ?? this.total, + ); + } + + Map toMap() { + return { + 'isLoadingMore': isLoadingMore, + 'loadingMoreError': loadingMoreError, + 'propertymodel': properties.map((x) => x.toMap()).toList(), + 'offset': offset, + 'total': total, + }; + } + + factory FetchPromotedPropertiesSuccess.fromMap(Map map) { + return FetchPromotedPropertiesSuccess( + isLoadingMore: map['isLoadingMore'] as bool, + loadingMoreError: map['loadingMoreError'] as bool, + properties: List.from( + (map['propertymodel'] as List).map( + (x) => PropertyModel.fromMap(x as Map), + ), + ), + offset: map['offset'] as int, + total: map['total'] as int, + ); + } + + String toJson() => json.encode(toMap()); + + factory FetchPromotedPropertiesSuccess.fromJson(String source) => + FetchPromotedPropertiesSuccess.fromMap( + json.decode(source) as Map); + + @override + set isLoadingMore(bool _isLoadingMore) {} + + @override + set properties(List _properties) {} +} + +class FetchPromotedPropertiesFailure extends FetchPromotedPropertiesState + implements PropertyErrorStateWireframe { + final String error; + FetchPromotedPropertiesFailure(this.error); + + @override + set error(_error) {} +} + +class FetchPromotedPropertiesCubit extends Cubit + with HydratedMixin + implements PropertyCubitWireframe { + FetchPromotedPropertiesCubit() : super(FetchPromotedPropertiesInitial()); + + final PropertyRepository _propertyRepository = PropertyRepository(); + + @override + Future fetch({bool? forceRefresh, bool? loadWithoutDelay}) async { + ///if it is not force refresh + if (forceRefresh != true) { + ///This will check if state is success so it will delay 5 seconds to load data in background + if (state is FetchPromotedPropertiesSuccess) { + await Future.delayed(Duration( + seconds: loadWithoutDelay == true + ? 0 + : AppSettings.hiddenAPIProcessDelay)); + } else { + //if state is not success it will show shimmer + emit(FetchPromotedPropertiesInProgress()); + } + } else { + emit(FetchPromotedPropertiesInProgress()); + } + + try { + ///This will call api instantly when its force refresh + if (forceRefresh == true) { + DataOutput result = + await _propertyRepository.fetchPromotedProperty( + offset: 0, + sendCityName: true, + ); + emit( + FetchPromotedPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total, + ), + ); + } else { + ///And if it is not force refresh and state is not success, like its failed before so it will call API + if (state is! FetchPromotedPropertiesSuccess) { + DataOutput result = + await _propertyRepository.fetchPromotedProperty( + offset: 0, + sendCityName: true, + ); + emit( + FetchPromotedPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total, + ), + ); + } else { + await CheckInternet.check( + onInternet: () async { + ////If it is success state and internet is available it will call API to load new data + DataOutput result = + await _propertyRepository.fetchPromotedProperty( + offset: 0, + sendCityName: true, + ); + emit( + FetchPromotedPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: result.modelList, + offset: 0, + total: result.total, + ), + ); + }, + onNoInternet: () { + ///if there is no internet so it will load cached data + emit( + FetchPromotedPropertiesSuccess( + total: (state as FetchPromotedPropertiesSuccess).total, + offset: (state as FetchPromotedPropertiesSuccess).offset, + isLoadingMore: + (state as FetchPromotedPropertiesSuccess).isLoadingMore, + loadingMoreError: (state as FetchPromotedPropertiesSuccess) + .loadingMoreError, + properties: + (state as FetchPromotedPropertiesSuccess).properties), + ); + }, + ); + } + } + } catch (e) { + emit(FetchPromotedPropertiesFailure(e.toString())); + } + } + + void update(PropertyModel model) { + if (state is FetchPromotedPropertiesSuccess) { + List properties = + (state as FetchPromotedPropertiesSuccess).properties; + + var index = properties.indexWhere((element) => element.id == model.id); + if (index != -1) { + properties[index] = model; + } + + emit((state as FetchPromotedPropertiesSuccess) + .copyWith(propertymodel: properties)); + } + } + + @override + Future fetchMore() async { + try { + if (state is FetchPromotedPropertiesSuccess) { + if ((state as FetchPromotedPropertiesSuccess).isLoadingMore) { + return; + } + emit((state as FetchPromotedPropertiesSuccess) + .copyWith(isLoadingMore: true)); + DataOutput result = + await _propertyRepository.fetchPromotedProperty( + offset: + (state as FetchPromotedPropertiesSuccess).properties.length, + sendCityName: true); + + FetchPromotedPropertiesSuccess propertymodelState = + (state as FetchPromotedPropertiesSuccess); + propertymodelState.properties.addAll(result.modelList); + emit(FetchPromotedPropertiesSuccess( + isLoadingMore: false, + loadingMoreError: false, + properties: propertymodelState.properties, + offset: (state as FetchPromotedPropertiesSuccess).properties.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchPromotedPropertiesSuccess) + .copyWith(isLoadingMore: false, loadingMoreError: true)); + } + } + + bool hasMoreData() { + if (state is FetchPromotedPropertiesSuccess) { + return (state as FetchPromotedPropertiesSuccess).properties.length < + (state as FetchPromotedPropertiesSuccess).total; + } + return false; + } + + @override + FetchPromotedPropertiesState? fromJson(Map json) { + try { + FetchPromotedPropertiesSuccess fetchPromotedPropertiesSuccess = + FetchPromotedPropertiesSuccess.fromMap(json); + return fetchPromotedPropertiesSuccess; + } catch (e) { + // Log the error or handle it as needed + return null; // Ensure a value is returned even when an exception occurs + } + } + + @override + Map? toJson(FetchPromotedPropertiesState state) { + if (state is FetchPromotedPropertiesSuccess) { + Map mapped = state.toMap(); + mapped['cubit_state'] = "FetchPromotedPropertiesSuccess"; + return mapped; + } + + return null; + } +} diff --git a/lib/data/cubits/property/fetch_property_cubit.dart b/lib/data/cubits/property/fetch_property_cubit.dart new file mode 100644 index 0000000..1d93abe --- /dev/null +++ b/lib/data/cubits/property/fetch_property_cubit.dart @@ -0,0 +1,20 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class FetchPropertyState {} + +class FetchPropertyInitial extends FetchPropertyState {} + +class FetchPropertyInProgress extends FetchPropertyState {} + +class FetchPropertySuccess extends FetchPropertyState {} + +class FetchPropertyFailure extends FetchPropertyState { + final String errorMessage; + + FetchPropertyFailure(this.errorMessage); +} + +class FetchPropertyCubit extends Cubit { + // final PropertyRepository _propertyRepository = PropertyRepository(); + FetchPropertyCubit() : super(FetchPropertyInitial()); +} diff --git a/lib/data/cubits/property/fetch_property_from_category_cubit.dart b/lib/data/cubits/property/fetch_property_from_category_cubit.dart new file mode 100644 index 0000000..ae8947c --- /dev/null +++ b/lib/data/cubits/property/fetch_property_from_category_cubit.dart @@ -0,0 +1,138 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../Repositories/property_repository.dart'; +import '../../model/data_output.dart'; +import '../../model/property_model.dart'; + +abstract class FetchPropertyFromCategoryState {} + +class FetchPropertyFromCategoryInitial extends FetchPropertyFromCategoryState {} + +class FetchPropertyFromCategoryInProgress + extends FetchPropertyFromCategoryState {} + +class FetchPropertyFromCategorySuccess extends FetchPropertyFromCategoryState { + final bool isLoadingMore; + final bool loadingMoreError; + final List propertymodel; + final int offset; + final int total; + final int? categoryId; + FetchPropertyFromCategorySuccess( + {required this.isLoadingMore, + required this.loadingMoreError, + required this.propertymodel, + required this.offset, + required this.total, + this.categoryId}); + + FetchPropertyFromCategorySuccess copyWith( + {bool? isLoadingMore, + bool? loadingMoreError, + List? propertymodel, + int? offset, + int? total, + int? categoryId}) { + return FetchPropertyFromCategorySuccess( + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + loadingMoreError: loadingMoreError ?? this.loadingMoreError, + propertymodel: propertymodel ?? this.propertymodel, + offset: offset ?? this.offset, + total: total ?? this.total, + categoryId: categoryId ?? this.categoryId); + } +} + +class FetchPropertyFromCategoryFailure extends FetchPropertyFromCategoryState { + final dynamic errorMessage; + FetchPropertyFromCategoryFailure(this.errorMessage); +} + +class FetchPropertyFromCategoryCubit + extends Cubit { + FetchPropertyFromCategoryCubit() : super(FetchPropertyFromCategoryInitial()); + + final PropertyRepository _propertyRepository = PropertyRepository(); + + Future fetchPropertyFromCategory(int categoryId, + {bool? showPropertyType}) async { + try { + emit(FetchPropertyFromCategoryInProgress()); + + DataOutput result = + await _propertyRepository.fetchProperyFromCategoryId( + id: categoryId, + offset: 0, + showPropertyType: showPropertyType, + ); + emit( + FetchPropertyFromCategorySuccess( + isLoadingMore: false, + loadingMoreError: false, + propertymodel: result.modelList, + offset: 0, + total: result.total, + categoryId: categoryId, + ), + ); + } catch (e) { + emit( + FetchPropertyFromCategoryFailure( + e, + ), + ); + } + } + + Future fetchPropertyFromCategoryMore({bool? showPropertyType}) async { + try { + if (state is FetchPropertyFromCategorySuccess) { + if ((state as FetchPropertyFromCategorySuccess).isLoadingMore) { + return; + } + emit((state as FetchPropertyFromCategorySuccess) + .copyWith(isLoadingMore: true)); + + DataOutput result = + await _propertyRepository.fetchProperyFromCategoryId( + id: (state as FetchPropertyFromCategorySuccess).categoryId!, + showPropertyType: showPropertyType, + offset: (state as FetchPropertyFromCategorySuccess) + .propertymodel + .length); + + FetchPropertyFromCategorySuccess property = + (state as FetchPropertyFromCategorySuccess); + + property.propertymodel.addAll(result.modelList); + + emit( + FetchPropertyFromCategorySuccess( + isLoadingMore: false, + loadingMoreError: false, + propertymodel: property.propertymodel, + offset: (state as FetchPropertyFromCategorySuccess) + .propertymodel + .length, + total: result.total, + ), + ); + } + } catch (e) { + emit( + (state as FetchPropertyFromCategorySuccess).copyWith( + isLoadingMore: false, + loadingMoreError: true, + ), + ); + } + } + + bool hasMoreData() { + if (state is FetchPropertyFromCategorySuccess) { + return (state as FetchPropertyFromCategorySuccess).propertymodel.length < + (state as FetchPropertyFromCategorySuccess).total; + } + return false; + } +} diff --git a/lib/data/cubits/property/fetch_recent_properties.dart b/lib/data/cubits/property/fetch_recent_properties.dart new file mode 100644 index 0000000..967d2d1 --- /dev/null +++ b/lib/data/cubits/property/fetch_recent_properties.dart @@ -0,0 +1,251 @@ +// import 'dart:developer' as developer; +import 'package:ebroker/Ui/screens/proprties/viewAll.dart'; +import 'package:ebroker/data/Repositories/property_repository.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../../settings.dart'; +import '../../../utils/Network/networkAvailability.dart'; + +abstract class FetchRecentPropertiesState {} + +class FetchRecentProepertiesInitial extends FetchRecentPropertiesState {} + +class FetchRecentPropertiesInProgress extends FetchRecentPropertiesState {} + +class FetchRecentPropertiesSuccess extends FetchRecentPropertiesState + implements PropertySuccessStateWireframe { + final int total; + final int offset; + @override + final bool isLoadingMore; + final bool hasError; + @override + final List properties; + + FetchRecentPropertiesSuccess({ + required this.total, + required this.offset, + required this.isLoadingMore, + required this.hasError, + required this.properties, + }); + + FetchRecentPropertiesSuccess copyWith({ + int? total, + int? offset, + bool? isLoadingMore, + bool? hasError, + List? properties, + }) { + return FetchRecentPropertiesSuccess( + total: total ?? this.total, + offset: offset ?? this.offset, + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + hasError: hasError ?? this.hasError, + properties: properties ?? this.properties, + ); + } + + @override + set properties(List _properties) { + // TODO: implement properties + } + + @override + set isLoadingMore(bool _isLoadingMore) { + // TODO: implement isLoadingMore + } + + Map toMap() { + return { + 'total': this.total, + 'offset': this.offset, + 'isLoadingMore': this.isLoadingMore, + 'hasError': this.hasError, + 'properties': properties.map((e) => e.toMap()).toList(), + }; + } + + factory FetchRecentPropertiesSuccess.fromMap(Map map) { + return FetchRecentPropertiesSuccess( + total: map['total'] as int, + offset: map['offset'] as int, + isLoadingMore: map['isLoadingMore'] as bool, + hasError: map['hasError'] as bool, + properties: (map['properties'] as List) + .map((e) => PropertyModel.fromMap(e)) + .toList(), + ); + } +} + +class FetchRecentPropertiesFailur extends FetchRecentPropertiesState + implements PropertyErrorStateWireframe { + final dynamic error; + + FetchRecentPropertiesFailur(this.error); + + @override + set error(_error) {} +} + +class FetchRecentPropertiesCubit extends Cubit + with HydratedMixin + implements PropertyCubitWireframe { + FetchRecentPropertiesCubit() : super(FetchRecentProepertiesInitial()); + + final PropertyRepository _propertyRepository = PropertyRepository(); + @override + void fetch({bool? forceRefresh, bool? loadWithoutDelay}) async { + try { + if (forceRefresh != true) { + + if (state is FetchRecentPropertiesSuccess) { + // WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + await Future.delayed(Duration( + seconds: loadWithoutDelay == true + ? 0 + : AppSettings.hiddenAPIProcessDelay)); + // }); + } else { + emit(FetchRecentPropertiesInProgress()); + } + } else { + emit(FetchRecentPropertiesInProgress()); + } + + // if(forceRefresh==true){ + // + // + // }else{ + // if(state is! FetchRecentPropertiesSuccess){ + // + // }else{ + // + // } + // } + if (forceRefresh == true) { + DataOutput result = + await _propertyRepository.fetchRecentProperties(offset: 0); + // developer.log("API RESULT IS $result"); + // cek + emit( + FetchRecentPropertiesSuccess( + total: result.total, + offset: 0, + isLoadingMore: false, + hasError: false, + properties: result.modelList), + ); + } else { + if (state is! FetchRecentPropertiesSuccess) { + DataOutput result = + await _propertyRepository.fetchRecentProperties(offset: 0); + // developer.log("API RESULT IS $result"); + emit( + FetchRecentPropertiesSuccess( + total: result.total, + offset: 0, + isLoadingMore: false, + hasError: false, + properties: result.modelList), + ); + } else { + await CheckInternet.check( + onInternet: () async { + DataOutput result = + await _propertyRepository.fetchRecentProperties(offset: 0); + // developer.log("API RESULT IS $result"); + emit( + FetchRecentPropertiesSuccess( + total: result.total, + offset: 0, + isLoadingMore: false, + hasError: false, + properties: result.modelList), + ); + }, + onNoInternet: () { + emit( + FetchRecentPropertiesSuccess( + total: (state as FetchRecentPropertiesSuccess).total, + offset: (state as FetchRecentPropertiesSuccess).offset, + isLoadingMore: + (state as FetchRecentPropertiesSuccess).isLoadingMore, + hasError: (state as FetchRecentPropertiesSuccess).hasError, + properties: + (state as FetchRecentPropertiesSuccess).properties), + ); + }, + ); + } + } + } catch (e) { + emit(FetchRecentPropertiesFailur(e.toString())); + } + } + + @override + void fetchMore() async { + if (state is FetchRecentPropertiesSuccess) { + FetchRecentPropertiesSuccess mystate = + (state as FetchRecentPropertiesSuccess); + if (mystate.isLoadingMore) { + return; + } + emit((state as FetchRecentPropertiesSuccess) + .copyWith(isLoadingMore: true)); + DataOutput result = + await _propertyRepository.fetchRecentProperties( + offset: (state as FetchRecentPropertiesSuccess).properties.length, + ); + // developer.log("API RESULT IS $result"); + FetchRecentPropertiesSuccess propertymodelState = + (state as FetchRecentPropertiesSuccess); + propertymodelState.properties.addAll(result.modelList); + emit(FetchRecentPropertiesSuccess( + isLoadingMore: false, + hasError: false, + properties: propertymodelState.properties, + offset: (state as FetchRecentPropertiesSuccess).properties.length, + total: result.total)); + } + } + + @override + bool hasMoreData() { + if (state is FetchRecentPropertiesSuccess) { + return (state as FetchRecentPropertiesSuccess).properties.length < + (state as FetchRecentPropertiesSuccess).total; + } + return false; + } + + @override + FetchRecentPropertiesState? fromJson(Map json) { + try { + if (json['cubit_state'] == "FetchRecentPropertiesSuccess") { + FetchRecentPropertiesSuccess fetchRecentPropertiesSuccess = + FetchRecentPropertiesSuccess.fromMap(json); + + return fetchRecentPropertiesSuccess; + } + } catch (e) {} + return null; + } + + @override + Map? toJson(FetchRecentPropertiesState state) { + try { + if (state is FetchRecentPropertiesSuccess) { + Map mapped = state.toMap(); + mapped['cubit_state'] = "FetchRecentPropertiesSuccess"; + return mapped; + } + } catch (e) {} + + return null; + } +} diff --git a/lib/data/cubits/property/fetch_top_rated_properties_cubit.dart b/lib/data/cubits/property/fetch_top_rated_properties_cubit.dart new file mode 100644 index 0000000..01969d6 --- /dev/null +++ b/lib/data/cubits/property/fetch_top_rated_properties_cubit.dart @@ -0,0 +1,73 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:ebroker/data/Repositories/property_repository.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class FetchTopRatedPropertiesState {} + +class FetchTopRatedPropertiesInitial extends FetchTopRatedPropertiesState {} + +class FetchTopRatedPropertiesInProgress extends FetchTopRatedPropertiesState {} + +class FetchTopRatedPropertiesSuccess extends FetchTopRatedPropertiesState { + final int total; + final int offset; + final bool isLoadingMore; + final bool hasError; + final List properties; + + FetchTopRatedPropertiesSuccess({ + required this.total, + required this.offset, + required this.isLoadingMore, + required this.hasError, + required this.properties, + }); + + FetchTopRatedPropertiesSuccess copyWith({ + int? total, + int? offset, + bool? isLoadingMore, + bool? hasMoreData, + List? properties, + }) => + FetchTopRatedPropertiesSuccess( + total: total ?? this.total, + offset: offset ?? this.offset, + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + hasError: hasMoreData ?? hasError, + properties: properties ?? this.properties, + ); +} + +class FetchTopRatedPropertiesFailure extends FetchTopRatedPropertiesState { + final String errorMessage; + + FetchTopRatedPropertiesFailure(this.errorMessage); +} + +class FetchTopRatedPropertiesCubit extends Cubit { + FetchTopRatedPropertiesCubit() : super(FetchTopRatedPropertiesInitial()); + + final PropertyRepository _propertyRepository = PropertyRepository(); + Future fetchTopRatedProperty() async { + try { + emit(FetchTopRatedPropertiesInProgress()); + DataOutput result = + await _propertyRepository.fetchTopRatedProperty(); + + emit( + FetchTopRatedPropertiesSuccess( + total: result.total, + hasError: false, + isLoadingMore: false, + offset: 0, + properties: result.modelList, + ), + ); + } catch (e) { + emit(FetchTopRatedPropertiesFailure(e.toString())); + } + } +} diff --git a/lib/data/cubits/property/home_property_cubit.dart b/lib/data/cubits/property/home_property_cubit.dart new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/data/cubits/property/home_property_cubit.dart @@ -0,0 +1 @@ + diff --git a/lib/data/cubits/property/myPropHydrated.dart b/lib/data/cubits/property/myPropHydrated.dart new file mode 100644 index 0000000..83db44f --- /dev/null +++ b/lib/data/cubits/property/myPropHydrated.dart @@ -0,0 +1,268 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'package:ebroker/data/Repositories/property_repository.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:flutter/foundation.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../../settings.dart'; + +abstract class FetchMyPropertiesState {} + +class FetchMyPropertiesInitial extends FetchMyPropertiesState {} + +class FetchMyPropertiesInProgress extends FetchMyPropertiesState {} + +class FetchMyPropertiesSuccess extends FetchMyPropertiesState { + final int total; + final int offset; + final bool isLoadingMore; + final bool hasError; + final List myProperty; + FetchMyPropertiesSuccess({ + required this.total, + required this.offset, + required this.isLoadingMore, + required this.hasError, + required this.myProperty, + }); + + FetchMyPropertiesSuccess copyWith({ + int? total, + int? offset, + bool? isLoadingMore, + bool? hasMoreData, + List? myProperty, + }) { + return FetchMyPropertiesSuccess( + total: total ?? this.total, + offset: offset ?? this.offset, + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + hasError: hasMoreData ?? hasError, + myProperty: myProperty ?? this.myProperty, + ); + } + + Map toMap() { + return { + 'isLoadingMore': isLoadingMore, + 'loadingMoreError': hasError, + 'properties': myProperty.map((x) => x.toMap()).toList(), + 'offset': offset, + 'total': total, + }; + } + + factory FetchMyPropertiesSuccess.fromMap(Map map) { + return FetchMyPropertiesSuccess( + isLoadingMore: map['isLoadingMore'] as bool, + hasError: map['loadingMoreError'] as bool, + myProperty: List.from( + (map['properties']).map( + (x) => PropertyModel.fromMap(x as Map), + ), + ), + offset: map['offset'] as int, + total: map['total'] as int, + ); + } +} + +class FetchMyPropertiesFailure extends FetchMyPropertiesState { + final dynamic errorMessage; + + FetchMyPropertiesFailure(this.errorMessage); +} + +class FetchMyPropertiesCubit extends Cubit + with HydratedMixin { + FetchMyPropertiesCubit() : super(FetchMyPropertiesInitial()); + + final PropertyRepository _propertyRepository = PropertyRepository(); + Future fetchMyProperties({ + bool? forceRefresh, + required String type, + }) async { + try { + if (forceRefresh != true) { + if (state is FetchMyPropertiesSuccess) { + await Future.delayed( + Duration(seconds: AppSettings.hiddenAPIProcessDelay)); + } else { + emit(FetchMyPropertiesInProgress()); + } + } else { + emit(FetchMyPropertiesInProgress()); + } + if (forceRefresh == true) { + DataOutput result = + await _propertyRepository.fetchMyProperties( + offset: 0, + type: type, + ); + emit(FetchMyPropertiesSuccess( + hasError: false, + isLoadingMore: false, + myProperty: result.modelList, + total: result.total, + offset: 0)); + } else { + if (state is! FetchMyPropertiesSuccess) { + DataOutput result = + await _propertyRepository.fetchMyProperties( + offset: 0, + type: type, + ); + emit(FetchMyPropertiesSuccess( + hasError: false, + isLoadingMore: false, + myProperty: result.modelList, + total: result.total, + offset: 0)); + } else { + emit(FetchMyPropertiesSuccess( + total: (state as FetchMyPropertiesSuccess).total, + offset: (state as FetchMyPropertiesSuccess).offset, + isLoadingMore: (state as FetchMyPropertiesSuccess).isLoadingMore, + hasError: (state as FetchMyPropertiesSuccess).hasError, + myProperty: (state as FetchMyPropertiesSuccess).myProperty)); + } + } + } catch (e) { + emit(FetchMyPropertiesFailure(e)); + } + } + + void updateStatus(int propertyId, String currentType) { + try { + if (state is FetchMyPropertiesSuccess) { + List propertyList = + (state as FetchMyPropertiesSuccess).myProperty; + int index = propertyList.indexWhere((element) { + return element.id == propertyId; + }); + + if (currentType == "Sell") { + propertyList[index].properyType = "Sold"; + } + if (currentType == "Rent") { + propertyList[index].properyType = "Rented"; + } + + if (currentType == "Rented") { + propertyList[index].properyType = "Rent"; + } + if (kDebugMode) { + if (currentType == "Sold") { + propertyList[index].properyType = "Sell"; + } + } + + emit((state as FetchMyPropertiesSuccess) + .copyWith(myProperty: propertyList)); + } + } catch (e) {} + } + + void update(PropertyModel model) { + if (state is FetchMyPropertiesSuccess) { + List properties = + (state as FetchMyPropertiesSuccess).myProperty; + + var index = properties.indexWhere((element) => element.id == model.id); + + if (index != -1) { + properties[index] = model; + } + + emit( + (state as FetchMyPropertiesSuccess).copyWith(myProperty: properties)); + } + } + + Future fetchMoreProperties({required String type}) async { + try { + if (state is FetchMyPropertiesSuccess) { + if ((state as FetchMyPropertiesSuccess).isLoadingMore) { + return; + } + emit((state as FetchMyPropertiesSuccess).copyWith(isLoadingMore: true)); + + DataOutput result = + await _propertyRepository.fetchMyProperties( + offset: (state as FetchMyPropertiesSuccess).myProperty.length, + type: type); + + FetchMyPropertiesSuccess bookingsState = + (state as FetchMyPropertiesSuccess); + bookingsState.myProperty.addAll(result.modelList); + emit( + FetchMyPropertiesSuccess( + isLoadingMore: false, + hasError: false, + myProperty: bookingsState.myProperty, + offset: (state as FetchMyPropertiesSuccess).myProperty.length, + total: result.total, + ), + ); + } + } catch (e) { + emit( + (state as FetchMyPropertiesSuccess).copyWith( + isLoadingMore: false, + hasMoreData: true, + ), + ); + } + } + + void addLocal(PropertyModel model) { + try { + if (state is FetchMyPropertiesSuccess) { + List myProperty = + (state as FetchMyPropertiesSuccess).myProperty; + if (myProperty.isNotEmpty) { + myProperty.insert(0, model); + } else { + myProperty.add(model); + } + + emit((state as FetchMyPropertiesSuccess) + .copyWith(myProperty: myProperty)); + } + } catch (e, st) { + throw st; + } + } + + bool hasMoreData() { + if (state is FetchMyPropertiesSuccess) { + return (state as FetchMyPropertiesSuccess).myProperty.length < + (state as FetchMyPropertiesSuccess).total; + } + return false; + } + + @override + FetchMyPropertiesState? fromJson(Map json) { + try { + var state = json['cubit_state']; + + if (state == "FetchMyPropertiesSuccess") { + return FetchMyPropertiesSuccess.fromMap(json); + } + } catch (e) {} + return null; + } + + @override + Map? toJson(FetchMyPropertiesState state) { + if (state is FetchMyPropertiesSuccess) { + Map map = state.toMap(); + map['cubit_state'] = "FetchMyPropertiesSuccess"; + return map; + } + return null; + } +} diff --git a/lib/data/cubits/property/property_cubit.dart b/lib/data/cubits/property/property_cubit.dart new file mode 100644 index 0000000..16f0a8b --- /dev/null +++ b/lib/data/cubits/property/property_cubit.dart @@ -0,0 +1,67 @@ +// ignore_for_file: invalid_return_type_for_catch_error + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +// import 'package:google_maps_webservice/directions.dart'; + +import '../../../utils/api.dart'; +import '../../model/property_model.dart'; + +abstract class PropertyState {} + +class PropertyInitial extends PropertyState {} + +class PropertyFetchProgress extends PropertyState {} + +class PropertyFetchSuccess extends PropertyState { + List propertylist = []; + int total = 0; + PropertyFetchSuccess(this.propertylist, this.total); +} + +class PropertyFetchFailure extends PropertyState { + final String errmsg; + PropertyFetchFailure(this.errmsg); +} + +class PropertyCubit extends Cubit { + PropertyCubit() : super(PropertyInitial()); + + void fetchProperty(BuildContext context, Map mbodyparam, + {bool fromUserlist = false}) { + emit(PropertyFetchProgress()); + fetchPropertyFromDb(context, mbodyparam, fromUserlist: fromUserlist) + .then((value) { + emit(PropertyFetchSuccess(value['list'], value['total'])); + }).catchError((e, st) => emit(PropertyFetchFailure(st.toString()))); + } + + Future fetchPropertyFromDb( + BuildContext context, + Map bodyparam, { + bool fromUserlist = false, + }) async { + //String? propertyId, + Map result = {}; + List propertylist = []; + int mtotal = 0; + var response = await Api.post(url: Api.apiGetProprty, parameter: {}); + // log("server data $map"); + // var response = await HelperUtils.sendApiRequest( + // Api.apiGetProprty, + // bodyparam, + // true, + // context, + // passUserid: fromUserlist, + // ); + // var getdata = json.decode(response); + List list = response['data']; + mtotal = response["total"]; + result['total'] = mtotal; + propertylist = list.map((model) => PropertyModel.fromMap(model)).toList(); + + result['list'] = propertylist; + return result; + } +} diff --git a/lib/data/cubits/property/search_property_cubit.dart b/lib/data/cubits/property/search_property_cubit.dart new file mode 100644 index 0000000..b3ff9b3 --- /dev/null +++ b/lib/data/cubits/property/search_property_cubit.dart @@ -0,0 +1,130 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'package:ebroker/data/Repositories/property_repository.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class SearchPropertyState {} + +class SearchPropertyInitial extends SearchPropertyState {} + +class SearchPropertyFetchProgress extends SearchPropertyState {} + +class SearchPropertyProgress extends SearchPropertyState {} + +class SearchPropertySuccess extends SearchPropertyState { + final int total; + final int offset; + final String searchQuery; + final bool isLoadingMore; + final bool hasError; + final List searchedroperties; + + SearchPropertySuccess({ + required this.searchQuery, + required this.total, + required this.offset, + required this.isLoadingMore, + required this.hasError, + required this.searchedroperties, + }); + + SearchPropertySuccess copyWith({ + int? total, + int? offset, + String? searchQuery, + bool? isLoadingMore, + bool? hasError, + List? searchedroperties, + }) { + return SearchPropertySuccess( + total: total ?? this.total, + offset: offset ?? this.offset, + searchQuery: searchQuery ?? this.searchQuery, + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + hasError: hasError ?? this.hasError, + searchedroperties: searchedroperties ?? this.searchedroperties, + ); + } +} + +class SearchPropertyFailure extends SearchPropertyState { + final dynamic errorMessage; + SearchPropertyFailure(this.errorMessage); +} + +class SearchPropertyCubit extends Cubit { + SearchPropertyCubit() : super(SearchPropertyInitial()); + + final PropertyRepository _propertyRepository = PropertyRepository(); + Future searchProperty(String query, + {required int offset, bool? useOffset}) async { + try { + emit(SearchPropertyFetchProgress()); + DataOutput result = + await _propertyRepository.searchProperty(query, offset: 0); + + emit(SearchPropertySuccess( + searchQuery: query, + total: result.total, + hasError: false, + isLoadingMore: false, + offset: 0, + searchedroperties: result.modelList)); + } catch (e) { + emit(SearchPropertyFailure(e)); + } + } + + void clearSearch() { + if (state is SearchPropertySuccess) { + emit(SearchPropertyInitial()); + } + } + + Future fetchMoreSearchData() async { + try { + if (state is SearchPropertySuccess) { + if ((state as SearchPropertySuccess).isLoadingMore) { + return; + } + emit((state as SearchPropertySuccess).copyWith(isLoadingMore: true)); + + DataOutput result = + await _propertyRepository.searchProperty( + (state as SearchPropertySuccess).searchQuery, + offset: (state as SearchPropertySuccess).searchedroperties.length, + ); + + SearchPropertySuccess bookingsState = (state as SearchPropertySuccess); + bookingsState.searchedroperties.addAll(result.modelList); + emit( + SearchPropertySuccess( + searchQuery: (state as SearchPropertySuccess).searchQuery, + isLoadingMore: false, + hasError: false, + searchedroperties: bookingsState.searchedroperties, + offset: (state as SearchPropertySuccess).searchedroperties.length, + total: result.total, + ), + ); + } + } catch (e) { + emit( + (state as SearchPropertySuccess).copyWith( + isLoadingMore: false, + hasError: true, + ), + ); + } + } + + bool hasMoreData() { + if (state is SearchPropertySuccess) { + return (state as SearchPropertySuccess).searchedroperties.length < + (state as SearchPropertySuccess).total; + } + return false; + } +} diff --git a/lib/data/cubits/property/set_property_view_cubit.dart b/lib/data/cubits/property/set_property_view_cubit.dart new file mode 100644 index 0000000..c6416b6 --- /dev/null +++ b/lib/data/cubits/property/set_property_view_cubit.dart @@ -0,0 +1,33 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../Repositories/property_repository.dart'; + +abstract class SetPropertyViewState {} + +class SetPropertyViewInitial extends SetPropertyViewState {} + +class SetPropertyViewInProgress extends SetPropertyViewState {} + +class SetPropertyViewSuccess extends SetPropertyViewState {} + +class SetPropertyViewFailure extends SetPropertyViewState { + final String errorMessage; + + SetPropertyViewFailure(this.errorMessage); +} + +class SetPropertyViewCubit extends Cubit { + final PropertyRepository _propertyRepository = PropertyRepository(); + + SetPropertyViewCubit() : super(SetPropertyViewInitial()); + + Future set(String propertyId) async { + try { + emit(SetPropertyViewInProgress()); + await _propertyRepository.setProeprtyView(propertyId); + emit(SetPropertyViewSuccess()); + } catch (e) { + emit(SetPropertyViewFailure(e.toString())); + } + } +} diff --git a/lib/data/cubits/property/top_viewed_property_cubit.dart b/lib/data/cubits/property/top_viewed_property_cubit.dart new file mode 100644 index 0000000..6f01def --- /dev/null +++ b/lib/data/cubits/property/top_viewed_property_cubit.dart @@ -0,0 +1,67 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../utils/api.dart'; +import '../../helper/custom_exception.dart'; +import '../../../utils/helper_utils.dart'; +import '../../model/property_model.dart'; + +abstract class TopPropertyState {} + +class TopPropertyInitial extends TopPropertyState {} + +class TopPropertyFetchProgress extends TopPropertyState {} + +class TopPropertyFetchSuccess extends TopPropertyState { + List propertylist = []; + TopPropertyFetchSuccess(this.propertylist); +} + +class TopPropertyFetchFailure extends TopPropertyState { + final String errmsg; + TopPropertyFetchFailure(this.errmsg); +} + +class TopViewedPropertyCubit extends Cubit { + TopViewedPropertyCubit() : super(TopPropertyInitial()); + + void fetchTopProperty(BuildContext context, + {String? categoryId, bool fromUserlist = false}) { + emit(TopPropertyFetchProgress()); + fetchTopPropertyFromDb(context) + .then((value) => emit(TopPropertyFetchSuccess(value))) + .catchError((e) => emit(TopPropertyFetchFailure(e.toString()))); + } + + Future> fetchTopPropertyFromDb( + BuildContext context, + ) async { + //String? propertyId, + List propertylist = []; + Map body = { + Api.topRated: "1", + Api.offset: "0", + Api.limit: "10", + }; + + var response = await HelperUtils.sendApiRequest( + Api.apiGetProprty, body, true, context, + passUserid: false); + var getdata = json.decode(response); + if (getdata != null) { + if (!getdata[Api.error]) { + getdata['data']; + // propertylist = + // list.map((model) => PropertyModel.fromJson(model)).toList(); + } else { + throw CustomException(getdata[Api.message]); + } + } else { + throw CustomException("nodatafound"); + } + + return propertylist; + } +} diff --git a/lib/data/cubits/property/update_property_status.dart b/lib/data/cubits/property/update_property_status.dart new file mode 100644 index 0000000..1ea06d0 --- /dev/null +++ b/lib/data/cubits/property/update_property_status.dart @@ -0,0 +1,36 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'package:ebroker/data/Repositories/property_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class UpdatePropertyStatusState {} + +class UpdatePropertyStatusInitial extends UpdatePropertyStatusState {} + +class UpdatePropertyStatusInProgress extends UpdatePropertyStatusState {} + +class UpdatePropertyStatusSuccess extends UpdatePropertyStatusState {} + +class UpdatePropertyStatusFail extends UpdatePropertyStatusState { + final dynamic error; + UpdatePropertyStatusFail({ + required this.error, + }); +} + +class UpdatePropertyStatusCubit extends Cubit { + UpdatePropertyStatusCubit() : super(UpdatePropertyStatusInitial()); + + final PropertyRepository _propertyRepository = PropertyRepository(); + + void update({required dynamic propertyId, required dynamic status}) async { + try { + emit(UpdatePropertyStatusInProgress()); + await _propertyRepository.updatePropertyStatus( + propertyId: propertyId, status: status); + emit(UpdatePropertyStatusSuccess()); + } catch (e) { + emit(UpdatePropertyStatusFail(error: e.toString())); + } + } +} diff --git a/lib/data/cubits/slider_cubit.dart b/lib/data/cubits/slider_cubit.dart new file mode 100644 index 0000000..7774c8c --- /dev/null +++ b/lib/data/cubits/slider_cubit.dart @@ -0,0 +1,181 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +import 'package:ebroker/utils/api.dart'; +import 'package:flutter/material.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../settings.dart'; +import '../../utils/Network/networkAvailability.dart'; +import '../helper/custom_exception.dart'; +import '../model/home_slider.dart'; + +abstract class SliderState {} + +class SliderInitial extends SliderState {} + +class SliderFetchInProgress extends SliderState {} + +class SliderFetchInInternalProgress extends SliderState {} + +class SliderFetchSuccess extends SliderState { + List sliderlist = []; + + SliderFetchSuccess(this.sliderlist); + + Map toMap() { + return { + 'sliderlist': sliderlist.map((x) => x.toMap()).toList(), + }; + } + + factory SliderFetchSuccess.fromMap(Map map) { + return SliderFetchSuccess( + List.from( + (map['sliderlist']).map( + (x) => HomeSlider.fromMap(x as Map), + ), + ), + ); + } + + String toJson() => json.encode(toMap()); + + factory SliderFetchSuccess.fromJson(String source) => + SliderFetchSuccess.fromMap(json.decode(source) as Map); +} + +class SliderFetchFailure extends SliderState { + final String errorMessage; + final bool isUserDeactivated; + SliderFetchFailure( + this.errorMessage, this.isUserDeactivated); //, this.isUserDeactivated +} + +class SliderCubit extends Cubit with HydratedMixin { + SliderCubit() : super(SliderInitial()); + + void fetchSlider(BuildContext context, + {bool? forceRefresh, bool? loadWithoutDelay}) async { + if (forceRefresh != true) { + if (state is SliderFetchSuccess) { + await Future.delayed(Duration( + seconds: loadWithoutDelay == true + ? 0 + : AppSettings.hiddenAPIProcessDelay)); + } else { + emit(SliderFetchInProgress()); + } + } else { + emit(SliderFetchInProgress()); + } + + if (forceRefresh == true) { + fetchSliderFromDb(context, sendCityName: true) + .then((value) => emit(SliderFetchSuccess(value))) + .catchError((e) { + if (isClosed) return; + bool isUserActive = true; + if (e.toString() == + "your account has been deactivate! please contact admin") { + //message from API + isUserActive = false; + } else { + isUserActive = true; + } + emit(SliderFetchFailure(e.toString(), isUserActive)); //, isUserActive + }); + } else { + if (state is! SliderFetchSuccess) { + fetchSliderFromDb(context, sendCityName: true) + .then((value) => emit(SliderFetchSuccess(value))) + .catchError((e) { + if (isClosed) return; + bool isUserActive = true; + if (e.toString() == + "your account has been deactivate! please contact admin") { + //message from API + isUserActive = false; + } else { + isUserActive = true; + } + emit(SliderFetchFailure(e.toString(), isUserActive)); //, isUserActive + }); + } else { + await CheckInternet.check( + onInternet: () async { + fetchSliderFromDb(context, sendCityName: true) + .then((value) => emit(SliderFetchSuccess(value))) + .catchError((e) { + if (isClosed) return; + bool isUserActive = true; + if (e.toString() == + "your account has been deactivate! please contact admin") { + //message from API + isUserActive = false; + } else { + isUserActive = true; + } + emit(SliderFetchFailure( + e.toString(), isUserActive)); //, isUserActive + }); + }, + onNoInternet: () { + emit(SliderFetchSuccess((state as SliderFetchSuccess).sliderlist)); + }, + ); + } + } + + Future.delayed( + Duration.zero, + () {}, + ); + } + + Future> fetchSliderFromDb(BuildContext context, + {required bool sendCityName}) async { + List sliderlist = []; + Map body = {}; + + if (sendCityName) { + // if (HiveUtils.getCityName() != null) { + // body['city'] = HiveUtils.getCityName(); + // } + } + + var response = await Api.get(url: Api.apiGetSlider, queryParameters: body); + + if (!response[Api.error]) { + List list = response['data']; + sliderlist = list.map((model) => HomeSlider.fromJson(model)).toList(); + } else { + throw CustomException(response[Api.message]); + } + + return sliderlist; + } + + @override + SliderState? fromJson(Map json) { + try { + var state = json['cubit_state']; + + if (state == "SliderFetchSuccess") { + return SliderFetchSuccess.fromMap(json); + } + } catch (e) {} + + return null; + } + + @override + Map? toJson(SliderState state) { + if (state is SliderFetchSuccess) { + Map map = state.toMap(); + map['cubit_state'] = "SliderFetchSuccess"; + return map; + } + return null; + } +} diff --git a/lib/data/cubits/subscription/assign_free_package.dart b/lib/data/cubits/subscription/assign_free_package.dart new file mode 100644 index 0000000..40586c5 --- /dev/null +++ b/lib/data/cubits/subscription/assign_free_package.dart @@ -0,0 +1,32 @@ +import 'package:ebroker/data/Repositories/subscription_repository.dart'; +import 'package:ebroker/exports/main_export.dart'; + +abstract class AssignFreePackageState {} + +class AssignFreePackageInitial extends AssignFreePackageState {} + +class AssignFreePackageInProgress extends AssignFreePackageState {} + +class AssignFreePackageSuccess extends AssignFreePackageState {} + +class AssignFreePackageFail extends AssignFreePackageState { + final dynamic error; + AssignFreePackageFail(this.error); +} + +class AssignFreePackageCubit extends Cubit { + AssignFreePackageCubit() : super(AssignFreePackageInitial()); + + final SubscriptionRepository _subscriptionRepository = + SubscriptionRepository(); + + void assign(int packageId) async { + try { + emit(AssignFreePackageInProgress()); + await _subscriptionRepository.assignFreePackage(packageId); + emit(AssignFreePackageSuccess()); + } catch (e) { + emit(AssignFreePackageFail(e.toString())); + } + } +} diff --git a/lib/data/cubits/subscription/assign_package.dart b/lib/data/cubits/subscription/assign_package.dart new file mode 100644 index 0000000..9bea9e6 --- /dev/null +++ b/lib/data/cubits/subscription/assign_package.dart @@ -0,0 +1,36 @@ +import 'package:ebroker/data/Repositories/subscription_repository.dart'; +import 'package:ebroker/exports/main_export.dart'; + +abstract class AssignInAppPackageState {} + +class AssignInAppPackageInitial extends AssignInAppPackageState {} + +class AssignInAppPackageInProgress extends AssignInAppPackageState {} + +class AssignInAppPackageSuccess extends AssignInAppPackageState {} + +class AssignInAppPackageFail extends AssignInAppPackageState { + final dynamic error; + AssignInAppPackageFail(this.error); +} + +class AssignInAppPackageCubit extends Cubit { + AssignInAppPackageCubit() : super(AssignInAppPackageInitial()); + final SubscriptionRepository _subscriptionRepository = + SubscriptionRepository(); + + /// + ///This will assign in app product + void assign({required String packageId, required String productId}) async { + try { + emit(AssignInAppPackageInProgress()); + await _subscriptionRepository.assignPackage( + packageId: packageId, + productId: productId, + ); + emit(AssignInAppPackageSuccess()); + } catch (e) { + emit(AssignInAppPackageFail(e)); + } + } +} diff --git a/lib/data/cubits/subscription/fetch_subscription_packages_cubit.dart b/lib/data/cubits/subscription/fetch_subscription_packages_cubit.dart new file mode 100644 index 0000000..cb9da04 --- /dev/null +++ b/lib/data/cubits/subscription/fetch_subscription_packages_cubit.dart @@ -0,0 +1,114 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:ebroker/data/Repositories/subscription_repository.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/subscription_pacakage_model.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class FetchSubscriptionPackagesState {} + +class FetchSubscriptionPackagesInitial extends FetchSubscriptionPackagesState {} + +class FetchSubscriptionPackagesInProgress + extends FetchSubscriptionPackagesState {} + +class FetchSubscriptionPackagesSuccess extends FetchSubscriptionPackagesState { + final List subscriptionPacakges; + final bool isLoadingMore; + final bool hasError; + final int offset; + final int total; + + FetchSubscriptionPackagesSuccess({ + required this.subscriptionPacakges, + required this.isLoadingMore, + required this.hasError, + required this.offset, + required this.total, + }); + + FetchSubscriptionPackagesSuccess copyWith({ + List? subscriptionPacakges, + bool? isLoadingMore, + bool? hasError, + int? offset, + int? total, + }) { + return FetchSubscriptionPackagesSuccess( + subscriptionPacakges: subscriptionPacakges ?? this.subscriptionPacakges, + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + hasError: hasError ?? this.hasError, + offset: offset ?? this.offset, + total: total ?? this.total, + ); + } +} + +class FetchSubscriptionPackagesFailure extends FetchSubscriptionPackagesState { + final dynamic errorMessage; + + FetchSubscriptionPackagesFailure(this.errorMessage); +} + +class FetchSubscriptionPackagesCubit + extends Cubit { + FetchSubscriptionPackagesCubit() : super(FetchSubscriptionPackagesInitial()); + final SubscriptionRepository _subscriptionRepository = + SubscriptionRepository(); + Future fetchPackages() async { + try { + emit(FetchSubscriptionPackagesInProgress()); + DataOutput result = + await _subscriptionRepository.getSubscriptionPackages(offset: 0); + emit(FetchSubscriptionPackagesSuccess( + subscriptionPacakges: result.modelList, + offset: 0, + isLoadingMore: false, + total: result.total, + hasError: false)); + } catch (e) { + emit(FetchSubscriptionPackagesFailure(e)); + } + } + + bool hasMore() { + if (state is FetchSubscriptionPackagesSuccess) { + return (state as FetchSubscriptionPackagesSuccess) + .subscriptionPacakges + .length < + (state as FetchSubscriptionPackagesSuccess).total; + } + return false; + } + + fetchMorePackages() async { + if (state is FetchSubscriptionPackagesInProgress) { + return; + } + try { + if (state is FetchSubscriptionPackagesSuccess) { + emit((state as FetchSubscriptionPackagesSuccess) + .copyWith(isLoadingMore: true)); + DataOutput result = + await _subscriptionRepository.getSubscriptionPackages( + offset: (state as FetchSubscriptionPackagesSuccess) + .subscriptionPacakges + .length, + ); + + List subscriptionPacakges = + (state as FetchSubscriptionPackagesSuccess).subscriptionPacakges; + subscriptionPacakges.addAll(result.modelList); + + emit(FetchSubscriptionPackagesSuccess( + subscriptionPacakges: subscriptionPacakges, + isLoadingMore: false, + hasError: false, + offset: subscriptionPacakges.length, + total: result.total)); + } + } catch (e) { + emit((state as FetchSubscriptionPackagesSuccess) + .copyWith(isLoadingMore: false, hasError: true)); + } + } +} diff --git a/lib/data/cubits/subscription/get_subsctiption_package_limits_cubit.dart b/lib/data/cubits/subscription/get_subsctiption_package_limits_cubit.dart new file mode 100644 index 0000000..201be6c --- /dev/null +++ b/lib/data/cubits/subscription/get_subsctiption_package_limits_cubit.dart @@ -0,0 +1,46 @@ +import 'package:ebroker/data/Repositories/subscription_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../model/subscription_package_limit.dart'; + +abstract class GetSubsctiptionPackageLimitsState {} + +class GetSubsctiptionPackageLimitsInitial + extends GetSubsctiptionPackageLimitsState {} + +class GetSubsctiptionPackageLimitsInProgress + extends GetSubsctiptionPackageLimitsState {} + +class GetSubsctiptionPackageLimitsSuccess + extends GetSubsctiptionPackageLimitsState { + final SubcriptionPackageLimit packageLimit; + + GetSubsctiptionPackageLimitsSuccess(this.packageLimit); +} + +class GetSubsctiptionPackageLimitsFailure + extends GetSubsctiptionPackageLimitsState { + final String errorMessage; + GetSubsctiptionPackageLimitsFailure(this.errorMessage); +} + +class GetSubsctiptionPackageLimitsCubit + extends Cubit { + final SubscriptionRepository _subscriptionRepository = + SubscriptionRepository(); + + GetSubsctiptionPackageLimitsCubit() + : super(GetSubsctiptionPackageLimitsInitial()); + + Future getLimits(SubscriptionLimitType type) async { + try { + emit(GetSubsctiptionPackageLimitsInProgress()); + SubcriptionPackageLimit subscriptionPackageLimit = + await _subscriptionRepository.getPackageLimit(type); + emit(GetSubsctiptionPackageLimitsSuccess(subscriptionPackageLimit)); + } catch (error) { + print("errro di tampilkan $error"); + emit(GetSubsctiptionPackageLimitsFailure(error.toString())); + } + } +} diff --git a/lib/data/cubits/system/app_theme_cubit.dart b/lib/data/cubits/system/app_theme_cubit.dart new file mode 100644 index 0000000..e6d0e4d --- /dev/null +++ b/lib/data/cubits/system/app_theme_cubit.dart @@ -0,0 +1,38 @@ +// ignore_for_file: depend_on_referenced_packages + +import 'package:bloc/bloc.dart'; + +import '../../../app/app_theme.dart'; +import '../../../utils/hive_utils.dart'; + +class AppThemeCubit extends Cubit { + AppThemeCubit() : super(ThemeState(AppTheme.light)); +// HiveUtils.getCurrentTheme() + void changeTheme(AppTheme appTheme) { + HiveUtils.setCurrentTheme(appTheme); + emit(ThemeState(appTheme)); + } + + //dev! + void toggleTheme() { + if (state.appTheme == AppTheme.dark) { + HiveUtils.setCurrentTheme(AppTheme.light); + + emit(ThemeState(AppTheme.light)); + } else { + HiveUtils.setCurrentTheme(AppTheme.dark); + + emit(ThemeState(AppTheme.dark)); + } + } + + bool isDarkMode() { + return state.appTheme == AppTheme.dark; + } +} + +class ThemeState { + final AppTheme appTheme; + + ThemeState(this.appTheme); +} diff --git a/lib/data/cubits/system/delete_account_cubit.dart b/lib/data/cubits/system/delete_account_cubit.dart new file mode 100644 index 0000000..d8df986 --- /dev/null +++ b/lib/data/cubits/system/delete_account_cubit.dart @@ -0,0 +1,67 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../utils/api.dart'; +import '../../../utils/hive_utils.dart'; +import '../../helper/custom_exception.dart'; + +abstract class DeleteAccountState {} + +class DeleteAccountInitial extends DeleteAccountState {} + +class DeleteAccountProgress extends DeleteAccountState {} + +class DeleteAccountFailure extends DeleteAccountState { + final String errorMessage; + DeleteAccountFailure(this.errorMessage); +} + +class AccountDeleted extends DeleteAccountState { + final String successMessage; + AccountDeleted({required this.successMessage}); +} + +class DeleteAccountCubit extends Cubit { + DeleteAccountCubit() : super(DeleteAccountInitial()); + void deleteUserAccount(BuildContext context) { + emit(DeleteAccountProgress()); + deleteAccount(context) + .then((value) => emit(AccountDeleted(successMessage: value))) + .catchError((e) => emit(DeleteAccountFailure(e.toString()))); + } + + Future deleteAccount(BuildContext context) async { + String message = ''; + try { + /* User? currentUser = await FirebaseAuth.instance.currentUser; + if (currentUser != null) { + await currentUser.reload(); + }*/ + await FirebaseAuth.instance.currentUser!.delete().then((value) async { + Map parameter = {Api.userid: HiveUtils.getUserId()!}; + + var response = + await Api.post(url: Api.apiDeleteUser, parameter: parameter); + User? user = FirebaseAuth.instance.currentUser; + await user?.delete(); + if (response["error"]) { + throw CustomException(response["message"]); + } else { + Future.delayed( + Duration.zero, + () { + HiveUtils.logoutUser(context, onLogout: () {}, isRedirect: false); + }, + ); + message = response['message']; + } + }); + } on FirebaseAuthException catch (e) { + if (e.code == 'requires-recent-login') { + // throw CustomException(Strings.userDeleteErrorMessage); + } + } + return message; + } +} diff --git a/lib/data/cubits/system/fetch_language_cubit.dart b/lib/data/cubits/system/fetch_language_cubit.dart new file mode 100644 index 0000000..4c7fa01 --- /dev/null +++ b/lib/data/cubits/system/fetch_language_cubit.dart @@ -0,0 +1,68 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'dart:developer'; + +import 'package:ebroker/utils/api.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class FetchLanguageState {} + +class FetchLanguageInitial extends FetchLanguageState {} + +class FetchLanguageInProgress extends FetchLanguageState {} + +class FetchLanguageSuccess extends FetchLanguageState { + final String code; + final String name; + final Map data; + FetchLanguageSuccess({ + required this.code, + required this.name, + required this.data, + }); + + Map toMap() { + return { + 'code': code, + 'name': name, + 'file_name': data, + }; + } + + factory FetchLanguageSuccess.fromMap(Map map) { + return FetchLanguageSuccess( + code: map['code'] as String, + name: map['name'] as String, + data: map['file_name'] as Map, + ); + } +} + +class FetchLanguageFailure extends FetchLanguageState { + final String errorMessage; + + FetchLanguageFailure(this.errorMessage); +} + +class FetchLanguageCubit extends Cubit { + FetchLanguageCubit() : super(FetchLanguageInitial()); + + Future getLanguage(String languageCode) async { + try { + emit(FetchLanguageInProgress()); + + Map response = await Api.get( + url: Api.getLanguagae, + queryParameters: {Api.languageCode: languageCode}, + useAuthToken: false); + log("LANG_RESP ${(response['data']['file_name'] as Map).keys.toList()}"); + + emit(FetchLanguageSuccess( + code: response['data']['code'], + data: response['data']['file_name'], + name: response['data']['name'])); + } catch (e) { + emit(FetchLanguageFailure(e.toString())); + } + } +} diff --git a/lib/data/cubits/system/fetch_system_settings_cubit.dart b/lib/data/cubits/system/fetch_system_settings_cubit.dart new file mode 100644 index 0000000..321f7e6 --- /dev/null +++ b/lib/data/cubits/system/fetch_system_settings_cubit.dart @@ -0,0 +1,200 @@ +import 'package:ebroker/exports/main_export.dart'; +import 'package:ebroker/utils/Encryption/rsa.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../Repositories/system_repository.dart'; +import '../../model/system_settings_model.dart'; + +abstract class FetchSystemSettingsState {} + +class FetchSystemSettingsInitial extends FetchSystemSettingsState {} + +class FetchSystemSettingsInProgress extends FetchSystemSettingsState {} + +class FetchSystemSettingsSuccess extends FetchSystemSettingsState { + final Map settings; + FetchSystemSettingsSuccess({ + required this.settings, + }); + + Map toMap() { + return { + 'settings': settings, + }; + } + + factory FetchSystemSettingsSuccess.fromMap(Map map) { + return FetchSystemSettingsSuccess( + settings: map['settings'] as Map, + ); + } +} + +class FetchSystemSettingsFailure extends FetchSystemSettingsState { + final String errorMessage; + + FetchSystemSettingsFailure(this.errorMessage); +} + +class FetchSystemSettingsCubit extends Cubit + with HydratedMixin { + FetchSystemSettingsCubit() : super(FetchSystemSettingsInitial()); + final SystemRepository _systemRepository = SystemRepository(); + Future fetchSettings( + {required bool isAnonymouse, bool? forceRefresh}) async { + try { + if (forceRefresh != true) { + if (state is FetchSystemSettingsSuccess) { + await Future.delayed( + const Duration(seconds: AppSettings.hiddenAPIProcessDelay)); + } else { + emit(FetchSystemSettingsInProgress()); + } + } else { + emit(FetchSystemSettingsInProgress()); + } + + if (forceRefresh == true) { + Map settings = await _systemRepository.fetchSystemSettings( + isAnonymouse: isAnonymouse); + + Constant.currencySymbol = + _getSetting(settings, SystemSetting.currencySymball); + Constant.googlePlaceAPIkey = RSAEncryption().decrypt( + privateKey: Constant.keysDecryptionPasswordRSA, + encryptedData: settings['data']['place_api_key']); + Constant.isAdmobAdsEnabled = + (settings['data']['show_admob_ads'] == "1"); + Constant.adaptThemeColorSvg = (settings['data']['svg_clr'] == "1"); + Constant.admobBannerAndroid = + settings['data']?['android_banner_ad_id'] ?? ""; + Constant.admobBannerIos = settings['data']?['ios_banner_ad_id'] ?? ""; + + Constant.admobInterstitialAndroid = + settings['data']?['android_interstitial_ad_id'] ?? ""; + Constant.admobInterstitialIos = + settings['data']?['ios_interstitial_ad_id'] ?? ""; + emit(FetchSystemSettingsSuccess(settings: settings)); + } else { + if (state is! FetchSystemSettingsSuccess) { + Map settings = await _systemRepository.fetchSystemSettings( + isAnonymouse: isAnonymouse); + Constant.currencySymbol = + _getSetting(settings, SystemSetting.currencySymball); + Constant.googlePlaceAPIkey = RSAEncryption().decrypt( + privateKey: Constant.keysDecryptionPasswordRSA, + encryptedData: settings['data']['place_api_key']); + Constant.isAdmobAdsEnabled = + (settings['data']['show_admob_ads'] == "1"); + Constant.adaptThemeColorSvg = (settings['data']['svg_clr'] == "1"); + Constant.admobBannerAndroid = + settings['data']['android_banner_ad_id'] ?? ""; + Constant.admobBannerIos = settings['data']['ios_banner_ad_id'] ?? ""; + + Constant.admobInterstitialAndroid = + settings['data']['android_interstitial_ad_id'] ?? ""; + Constant.admobInterstitialIos = + settings['data']['ios_interstitial_ad_id'] ?? ""; + + emit(FetchSystemSettingsSuccess(settings: settings)); + } else { + Constant.googlePlaceAPIkey = RSAEncryption().decrypt( + privateKey: Constant.keysDecryptionPasswordRSA, + encryptedData: (state as FetchSystemSettingsSuccess) + .settings['data']['place_api_key']); + Constant.currencySymbol = (state as FetchSystemSettingsSuccess) + .settings['data']['currency_symbol'] ?? + ""; + Constant.isAdmobAdsEnabled = ((state as FetchSystemSettingsSuccess) + .settings['data']['show_admob_ads'] == + "1"); + Constant.admobBannerAndroid = (state as FetchSystemSettingsSuccess) + .settings['data']['android_banner_ad_id'] ?? + ""; + Constant.admobBannerIos = (state as FetchSystemSettingsSuccess) + .settings['data']['ios_banner_ad_id'] ?? + ""; + Constant.adaptThemeColorSvg = ((state as FetchSystemSettingsSuccess) + .settings['data']['svg_clr'] == + "1"); + Constant.admobInterstitialAndroid = + (state as FetchSystemSettingsSuccess).settings['data'] + ['android_interstitial_ad_id'] ?? + ""; + Constant.admobInterstitialIos = (state as FetchSystemSettingsSuccess) + .settings['data']['ios_interstitial_ad_id'] ?? + ""; + + emit(FetchSystemSettingsSuccess( + settings: (state as FetchSystemSettingsSuccess).settings)); + } + } + } catch (e) { + emit(FetchSystemSettingsFailure(e.toString())); + } + } + + dynamic getSetting(SystemSetting selected) { + if (state is FetchSystemSettingsSuccess) { + Map settings = (state as FetchSystemSettingsSuccess).settings['data']; + + if (selected == SystemSetting.language) { + return settings['languages']; + } + + if (selected == SystemSetting.demoMode) { + if (settings.containsKey("demo_mode")) { + return settings['demo_mode']; + } else { + return false; + } + } + + /// where selected is equals to type + var selectedSettingData = + (settings[Constant.systemSettingKeys[selected]]); + + return selectedSettingData; + } + } + + Map getRawSettings() { + if (state is FetchSystemSettingsSuccess) { + return (state as FetchSystemSettingsSuccess).settings['data']; + } + return {}; + } + + dynamic _getSetting(Map settings, SystemSetting selected) { + var selectedSettingData = + settings['data'][Constant.systemSettingKeys[selected]]; + + return selectedSettingData; + } + + @override + FetchSystemSettingsState? fromJson(Map json) { + try { + if (json['cubit_state'] == "FetchSystemSettingsSuccess") { + FetchSystemSettingsSuccess fetchSystemSettingsSuccess = + FetchSystemSettingsSuccess.fromMap(json); + + return fetchSystemSettingsSuccess; + } + } catch (e) {} + return null; + } + + @override + Map? toJson(FetchSystemSettingsState state) { + try { + if (state is FetchSystemSettingsSuccess) { + Map mapped = state.toMap(); + mapped['cubit_state'] = "FetchSystemSettingsSuccess"; + return mapped; + } + } catch (e) {} + + return null; + } +} diff --git a/lib/data/cubits/system/get_api_keys_cubit.dart b/lib/data/cubits/system/get_api_keys_cubit.dart new file mode 100644 index 0000000..9e5058d --- /dev/null +++ b/lib/data/cubits/system/get_api_keys_cubit.dart @@ -0,0 +1,129 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +import 'dart:developer'; + +import 'package:ebroker/utils/api.dart'; + +import '../../../exports/main_export.dart'; +import '../../../utils/Encryption/rsa.dart'; + +class GetApiKeysCubit extends Cubit { + GetApiKeysCubit() : super(GetApiKeysInitial()); + + Future fetch() async { + try { + emit(GetApiKeysInProgress()); + + Map result = await Api.get( + url: Api.getPaymentApiKeys, + queryParameters: {}, + ); + + List data = (result['data'] as List); + var razorpayKey = _getDataFromKey(data, "razor_key"); + var razorPaySecret = _getDataFromKey(data, "razor_secret"); + var paystackPublicKey = _getDataFromKey(data, "paystack_public_key"); + var paystackSecretKey = _getDataFromKey(data, "paystack_secret_key"); + var paystackCurrency = _getDataFromKey(data, "paystack_currency"); + var stripeCurrency = _getDataFromKey(data, "stripe_currency"); + var stripePublishableKey = + _getDataFromKey(data, "stripe_publishable_key"); + var stripeSecretKey = _getDataFromKey(data, "stripe_secret_key"); + String enabledGatway = ""; + + if (_getDataFromKey(data, "paypal_gateway") == "1") { + enabledGatway = "paypal"; + } else if (_getDataFromKey(data, "razorpay_gateway") == "1") { + enabledGatway = "razorpay"; + } else if (_getDataFromKey(data, "paystack_gateway") == "1") { + enabledGatway = "paystack"; + } else if (_getDataFromKey(data, "stripe_gateway") == "1") { + enabledGatway = "stripe"; + } + + emit(GetApiKeysSuccess( + razorPayKey: razorpayKey ?? "", + enabledPaymentGatway: enabledGatway, + razorPaySecret: razorPaySecret ?? "", + paystackPublicKey: paystackPublicKey ?? "", + paystackCurrency: paystackCurrency ?? "", + paystackSecret: paystackSecretKey ?? "", + stripeCurrency: stripeCurrency ?? "", + stripePublishableKey: stripePublishableKey ?? "", + stripeSecretKey: stripeSecretKey ?? "")); + } catch (e) { + emit(GetApiKeysFail(e.toString())); + } + } + + void setAPIKeys() { + //setKeys + if (state is GetApiKeysSuccess) { + GetApiKeysSuccess _state = (state as GetApiKeysSuccess); + + AppSettings.paystackKey = _state.paystackPublicKey; + AppSettings.razorpayKey = _state.razorPayKey; + AppSettings.enabledPaymentGatway = _state.enabledPaymentGatway; + AppSettings.paystackCurrency = _state.paystackCurrency; + AppSettings.stripeCurrency = _state.stripeCurrency; + AppSettings.stripePublishableKey = _state.stripePublishableKey; + AppSettings.stripeSecrateKey = RSAEncryption().decrypt( + privateKey: Constant.keysDecryptionPasswordRSA, + encryptedData: _state.stripeSecretKey, + ); + paystack.init(AppSettings.paystackKey); + } + if (state is GetApiKeysFail) { + log((state as GetApiKeysFail).error.toString(), name: "API KEY FAIL"); + } + } + + dynamic _getDataFromKey(List data, String key) { + try { + return data.where((element) => element['type'] == key).first['data']; + } catch (e) { + if (e.toString().contains("Bad state")) { + throw "The key>>> $key is not comming from API"; + } + } + } +} + +abstract class GetApiKeysState {} + +class GetApiKeysInitial extends GetApiKeysState {} + +class GetApiKeysInProgress extends GetApiKeysState {} + +class GetApiKeysSuccess extends GetApiKeysState { + final String razorPayKey; + final String razorPaySecret; + final String paystackPublicKey; + final String paystackSecret; + final String paystackCurrency; + final String enabledPaymentGatway; + final String stripeCurrency; + final String stripePublishableKey; + final String stripeSecretKey; + GetApiKeysSuccess({ + required this.razorPayKey, + required this.razorPaySecret, + required this.paystackPublicKey, + required this.paystackSecret, + required this.paystackCurrency, + required this.enabledPaymentGatway, + required this.stripeCurrency, + required this.stripePublishableKey, + required this.stripeSecretKey, + }); + + @override + String toString() { + return 'GetApiKeysSuccess(razorPayKey: $razorPayKey, razorPaySecret: $razorPaySecret, paystackPublicKey: $paystackPublicKey, paystackSecret: $paystackSecret, paystackCurrency: $paystackCurrency, enabledPaymentGatway: $enabledPaymentGatway, stripeCurrency: $stripeCurrency, stripePublishableKey: $stripePublishableKey, stripeSecretKey: $stripeSecretKey)'; + } +} + +class GetApiKeysFail extends GetApiKeysState { + final dynamic error; + GetApiKeysFail(this.error); +} diff --git a/lib/data/cubits/system/language_cubit.dart b/lib/data/cubits/system/language_cubit.dart new file mode 100644 index 0000000..9110b2d --- /dev/null +++ b/lib/data/cubits/system/language_cubit.dart @@ -0,0 +1,39 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hive/hive.dart'; + +import '../../../utils/hive_keys.dart'; + +class LanguageState {} + +class LanguageInitial extends LanguageState {} + +class LanguageLoader extends LanguageState { + final dynamic languageCode; + + LanguageLoader(this.languageCode); +} + +class LanguageLoadFail extends LanguageState { + final dynamic error; + LanguageLoadFail({required this.error}); +} + +class LanguageCubit extends Cubit { + LanguageCubit() : super(LanguageInitial()); + + void loadCurrentLanguage() { + var language = + Hive.box(HiveKeys.languageBox).get(HiveKeys.currentLanguageKey); + if (language != null) { + emit(LanguageLoader(language['code'])); + } else { + emit(LanguageLoader("en")); + } + } + + dynamic currentLanguageCode() { + return Hive.box(HiveKeys.languageBox) + .get(HiveKeys.currentLanguageKey)['code']; + } +} diff --git a/lib/data/cubits/system/notification_cubit.dart b/lib/data/cubits/system/notification_cubit.dart new file mode 100644 index 0000000..2363ca0 --- /dev/null +++ b/lib/data/cubits/system/notification_cubit.dart @@ -0,0 +1,72 @@ +import 'dart:convert'; + +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../utils/api.dart'; +import '../../../utils/helper_utils.dart'; +import '../../helper/custom_exception.dart'; +import '../../model/notification_data.dart'; + +abstract class NotificationState {} + +class NotificationInitial extends NotificationState {} + +class NotificationSetProgress extends NotificationState {} + +class NotificationSetSuccess extends NotificationState { + List notificationlist = []; + NotificationSetSuccess(this.notificationlist); +} + +class NotificationSetFailure extends NotificationState { + final String errmsg; + NotificationSetFailure(this.errmsg); +} + +class NotificationCubit extends Cubit { + NotificationCubit() : super(NotificationInitial()); + + void getNotification( + BuildContext context, + ) { + emit(NotificationSetProgress()); + getNotificationFromDb( + context, + ) + .then((value) => emit(NotificationSetSuccess(value))) + .catchError((e) => emit(NotificationSetFailure(e.toString()))); + } + + Future> getNotificationFromDb( + BuildContext context, + ) async { + Map body = {}; + List notificationList = []; + var response = await HelperUtils.sendApiRequest( + Api.apiGetNotificationList, + body, + false, + context, + ); + var getdata = json.decode(response); + if (getdata != null) { + if (!getdata[Api.error]) { + List list = getdata['data']; + notificationList = + list.map((model) => NotificationData.fromJson(model)).toList(); + } else { + throw CustomException(getdata[Api.message]); + } + } else { + Future.delayed( + Duration.zero, + () { + throw CustomException("nodatafound".translate(context)); + }, + ); + } + return notificationList; + } +} diff --git a/lib/data/cubits/system/user_details.dart b/lib/data/cubits/system/user_details.dart new file mode 100644 index 0000000..1c55d88 --- /dev/null +++ b/lib/data/cubits/system/user_details.dart @@ -0,0 +1,37 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:ebroker/data/model/user_model.dart'; +import 'package:ebroker/utils/hive_utils.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class UserDetailsCubit extends Cubit { + UserDetailsCubit() + : super(UserDetailsState( + user: HiveUtils.isGuest() ? null : HiveUtils.getUserDetails())); + + void fill(UserModel model) { + emit(UserDetailsState(user: model)); + } + + void copy(UserModel model) { + emit(state.copyWith(user: model)); + } + + void clear() { + emit(UserDetailsState(user: null)); + } +} + +class UserDetailsState { + final UserModel? user; + UserDetailsState({ + required this.user, + }); + + UserDetailsState copyWith({ + UserModel? user, + }) { + return UserDetailsState( + user: user ?? this.user, + ); + } +} diff --git a/lib/data/helper/custom_exception.dart b/lib/data/helper/custom_exception.dart new file mode 100644 index 0000000..86dcbb2 --- /dev/null +++ b/lib/data/helper/custom_exception.dart @@ -0,0 +1,34 @@ +class CustomException implements Exception { + final dynamic _message; + // ignore: unused_field + final dynamic _prefix; + + CustomException([this._message, this._prefix]); + + @override + String toString() { + return "$_message"; + } +} + +class FetchDataException extends CustomException { + FetchDataException([message]) + : super(message, "Error During Communication: "); +} + +class BadRequestException extends CustomException { + BadRequestException([message]) : super(message, "Invalid Request: "); +} + +class UnauthorisedException extends CustomException { + UnauthorisedException([message]) : super(message, "Unauthorised: "); +} + +class VerificationException extends CustomException { + VerificationException([message]) + : super(message, "Please verify email first "); +} + +class InvalidInputException extends CustomException { + InvalidInputException([message]) : super(message, "Invalid Input: "); +} diff --git a/lib/data/helper/design_configs.dart b/lib/data/helper/design_configs.dart new file mode 100644 index 0000000..89f1df1 --- /dev/null +++ b/lib/data/helper/design_configs.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +class DesignConfig { + static RoundedRectangleBorder setRoundedBorder(double bradius, bool isboarder, + {Color bordercolor = Colors.transparent}) { + return RoundedRectangleBorder( + side: BorderSide(color: bordercolor, width: isboarder ? 1.0 : 0), + borderRadius: BorderRadius.circular(bradius)); + } + + static BoxDecoration boxDecorationBorder( + {required Color color, + required double radius, + Color? borderColor, + double? borderWidth}) { + return BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(radius), + border: borderColor == null + ? null + : Border.all(color: borderColor, width: borderWidth ?? 1), + ); + } +} diff --git a/lib/data/helper/designs.dart b/lib/data/helper/designs.dart new file mode 100644 index 0000000..8398247 --- /dev/null +++ b/lib/data/helper/designs.dart @@ -0,0 +1,244 @@ +import 'dart:ui'; + +import '../../Ui/Theme/theme.dart'; +import '../../utils/constant.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +const double defaultPadding = 20; + +Widget setTextbutton(String titleTxt, Color txtColor, FontWeight? fontWeight, + VoidCallback onPressed, BuildContext context) { + return TextButton( + onPressed: onPressed, + child: Text(titleTxt, + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: txtColor, + fontWeight: fontWeight, + letterSpacing: 0.5, + )), + ); +} + +Widget setTitleText(String titleTxt, BuildContext context) { + return Text( + titleTxt, + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + color: Theme.of(context).colorScheme.textColorDark, + fontWeight: FontWeight.bold, + letterSpacing: 0.5), + textAlign: TextAlign.center, + ); +} + +Widget setMessageText( + {required String titleTxt, + required Color txtColor, + required TextStyle? txtStyle, + required BuildContext context, + FontWeight fontWeight = FontWeight.w400, + TextAlign txtAlign = TextAlign.center, + double? textheight, + int? txtmaxline, + TextOverflow? txtoverflow}) { + return Text( + titleTxt, //Theme.of(context).textTheme.titleMedium? + style: txtStyle?.copyWith( + color: txtColor, + fontWeight: fontWeight, + letterSpacing: 0.5, + height: textheight), + maxLines: txtmaxline, overflow: txtoverflow, + textAlign: txtAlign, + ); +} + +Widget setNetworkImg(String? murl, + {double? height, + double? width, + Color? imgColor, + BoxFit boxFit = BoxFit.contain, + BoxFit? placeboxfit}) { + String url = murl ??= ""; + return CachedNetworkImage( + imageUrl: url, + width: width, + height: height, + fit: boxFit, + errorWidget: (context, url, error) { + return setSVGImage("placeholder", + height: height, width: width, boxFit: placeboxfit ??= boxFit); + }, + placeholder: (context, url) { + return Center( + child: setSVGImage("placeholder", + height: height, width: width, boxFit: placeboxfit ??= boxFit)); + }, + ); +} + +Widget setSVGImage(String imageName, + {double? height, + double? width, + Color? imgColor, + BoxFit boxFit = BoxFit.contain}) { + String path = "$svgPath$imageName.svg"; + return SvgPicture.asset( + path, + height: height, + width: width, + color: imgColor, + fit: boxFit, + ); +} + +/* showSnackBar(String msg, BuildContext context) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + msg, + textAlign: TextAlign.center, + style: const TextStyle(color: Theme.of(context).colorScheme.textColor), ), + duration: const Duration(milliseconds: 1000), //bydefault 4000 ms + backgroundColor: Theme.of(context).colorScheme.bgColor, elevation: 1.0, + ), + ); +} */ + +///textStyles +TextStyle setTextStyle( + {required Color color, + // required double? fontSize, + required FontWeight fontW, + required double? letterSpace, + required BuildContext context}) { + return TextStyle( + //Theme.of(context).textTheme.headlineSmall?.copyWith(), + color: + color, //Theme.of(context).colorScheme.blackColor, // fontSize: fontSize, //20, + fontWeight: fontW, //FontWeight.w700, + letterSpacing: letterSpace, + ); +} + +///border +RoundedRectangleBorder setRoundedBorder(double bradius, + {bool isboarder = false, Color bordercolor = Colors.transparent}) { + return RoundedRectangleBorder( + side: BorderSide(color: bordercolor, width: isboarder ? 1.0 : 0), + borderRadius: BorderRadius.circular(bradius)); +} + +///appbar with or without back & action button +AppBar appBarWidget(BuildContext context, String titleText, + {IconData backButtonIcon = Icons.arrow_back}) { + return AppBar( + iconTheme: const IconThemeData( + color: Colors.black, + ), + title: setMessageText( + context: context, + titleTxt: titleText, + txtColor: Theme.of(context).colorScheme.blackColor, + txtStyle: Theme.of(context).textTheme.titleMedium, + fontWeight: FontWeight.w700), + leading: addBackButton(context, backButtonIcon), + ); +} + +IconButton addBackButton(BuildContext context, IconData backButtonIcon) { + return IconButton( + onPressed: () { + Navigator.of(context).pop(); + }, + icon: Icon( + backButtonIcon, + color: Theme.of(context).colorScheme.backgroundColor, + ), //Icons.arrow_back + ); +} + +AppBar appBarWithActionWidget( + BuildContext context, String titleText, Widget actionWidget, + {IconData backButtonIcon = Icons.arrow_back}) { + return AppBar( + iconTheme: const IconThemeData( + color: Colors.black, + ), + title: setMessageText( + context: context, + titleTxt: titleText, + txtColor: Theme.of(context).colorScheme.blackColor, + txtStyle: Theme.of(context).textTheme.titleMedium, + fontWeight: FontWeight.w700), + leading: addBackButton(context, backButtonIcon), + actions: [actionWidget], + ); +} + +///gradient Container +Container setGradientContainer( + {required Color gradientColor1, + required Color gradientColor2, + required BoxShape shape, + Radius? radius, + required Widget child}) { + return Container( + //MediaQuery.of(context).size.width + // height: 20, + // width: 20, + alignment: Alignment.center, + decoration: ShapeDecoration( + shape: const CircleBorder(), + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [gradientColor1, gradientColor2]), + ), + /* BoxDecoration( + // borderRadius: BorderRadius.all(radius), + shape: shape, + /* ShapeDecoration( + shape: CircleBorder(),*/ + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [gradientColor1, gradientColor2]), + ), */ + child: child); +} + +///blurred background for buttons & text +ClipRRect setBlurBg( + {required BuildContext context, required Widget childWidget}) { + return ClipRRect( + borderRadius: BorderRadius.circular(8.0), //circular(25.0), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: childWidget)); + /*, + VoidCallback? callBack + GestureDetector( + onTap: callBack, + child: */ +} + +///url to file converter for images @ AddProperty -- not working as expected +/* Future urlToFile(String imageUrl) async { +// generate random number. + var rng = Random(); +// get temporary directory of device. + Directory tempDir = await getTemporaryDirectory(); +// get temporary path from temporary directory. + String tempPath = tempDir.path; +// create a new file in temporary path with random file name. + File file = File('$tempPath${rng.nextInt(100)}.png'); +// call http.get method and pass imageUrl into it to get response. + Response response = await get(Uri.parse(imageUrl)); +// write bodyBytes received in response to file. + await file.writeAsBytes(response.bodyBytes); +// now return the file which is created with random name in +// temporary directory and image bytes from response is written to // that file. + return file; +} */ diff --git a/lib/data/helper/slide_animation.dart b/lib/data/helper/slide_animation.dart new file mode 100644 index 0000000..28a90f7 --- /dev/null +++ b/lib/data/helper/slide_animation.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; + +class AnimationFromRightSide extends StatefulWidget { + final Widget child; + final int delay; + + const AnimationFromRightSide( + {super.key, required this.child, required this.delay}); + + @override + AnimationFromRightSideState createState() => AnimationFromRightSideState(); +} + +class AnimationFromRightSideState extends State + with TickerProviderStateMixin { + late AnimationController _animController; + late Animation _animOffset; + + @override + void initState() { + super.initState(); + + _animController = AnimationController( + vsync: this, duration: const Duration(milliseconds: 500)); + final curve = + CurvedAnimation(curve: Curves.decelerate, parent: _animController); + _animOffset = + Tween(begin: const Offset(0.35, 0.00), end: Offset.zero) + .animate(curve); + + // if (widget.delay == null) { + if (mounted) _animController.forward(); + // } else { + // Timer(Duration(milliseconds: widget.delay), () { + // if (mounted) _animController.forward(); + // }); + // } + } + + @override + void dispose() { + _animController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return FadeTransition( + opacity: _animController, + child: SlideTransition( + position: _animOffset, + child: widget.child, + ), + ); + } +} + +class AnimationFromBottomSide extends StatefulWidget { + final Widget child; + final int delay; + + const AnimationFromBottomSide( + {super.key, required this.child, required this.delay}); + + @override + AnimationFromBottomSideState createState() => AnimationFromBottomSideState(); +} + +class AnimationFromBottomSideState extends State + with TickerProviderStateMixin { + late AnimationController _animController; + late Animation _animOffset; + + @override + void initState() { + super.initState(); + + _animController = AnimationController( + vsync: this, duration: const Duration(milliseconds: 500)); + final curve = + CurvedAnimation(curve: Curves.decelerate, parent: _animController); + _animOffset = + Tween(begin: const Offset(0.0, -1.0), end: Offset.zero) + .animate(curve); + + _animController.forward(); + // Timer(Duration(milliseconds: widget.delay), () { + // _animController.forward(); + // }); + } + + @override + void dispose() { + super.dispose(); + _animController.dispose(); + } + + @override + Widget build(BuildContext context) { + return FadeTransition( + opacity: _animController, + child: SlideTransition( + position: _animOffset, + child: widget.child, + ), + ); + } +} diff --git a/lib/data/helper/widgets.dart b/lib/data/helper/widgets.dart new file mode 100644 index 0000000..1b84c87 --- /dev/null +++ b/lib/data/helper/widgets.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../../utils/Extensions/extensions.dart'; +import '../../utils/ui_utils.dart'; + +class Widgets { + static bool isLoaderShowing = false; + static void showLoader(BuildContext context) async { + if (isLoaderShowing == true) return; + + isLoaderShowing=true; + showDialog( + context: context, + barrierDismissible: false, + useSafeArea: true, + builder: (BuildContext context) { + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: Colors.black.withOpacity(0), + ), + child: SafeArea( + child: WillPopScope( + child: Center( + child: UiUtils.progress( + normalProgressColor: context.color.tertiaryColor, + ), + ), + onWillPop: () { + return Future( + () => false, + ); + }, + ), + ), + ); + }); + } + + static void hideLoder(BuildContext context) { + if (!isLoaderShowing) return; + isLoaderShowing=false; + Navigator.of(context).pop(); + } + + static Center noDataFound(String errorMsg) { + return Center(child: Text(errorMsg)); + } +} + +//string Extension -- for ₹ +extension FormatAmount on String { + //working with static strings and not textFormField +} diff --git a/lib/data/model/Personalized/personalized_settings.dart b/lib/data/model/Personalized/personalized_settings.dart new file mode 100644 index 0000000..605d5d5 --- /dev/null +++ b/lib/data/model/Personalized/personalized_settings.dart @@ -0,0 +1,54 @@ +import 'package:ebroker/utils/Extensions/lib/list.dart'; + +class PersonalizedInterestSettings { + final int userId; + final List categoryIds; + final List priceRange; + final List propertyType; + final List outdoorFacilityIds; + final String city; + + PersonalizedInterestSettings({ + required this.userId, + required this.categoryIds, + required this.priceRange, + required this.propertyType, + required this.outdoorFacilityIds, + required this.city, + }); + + factory PersonalizedInterestSettings.fromMap(Map map) { + return PersonalizedInterestSettings( + userId: map['user_id'] ?? 0, + categoryIds: (map['category_ids'] is String) + ? [] + : ((map['category_ids']) as List).forceInt(), + priceRange: (map['price_range'] is String) + ? [0, 50] + : (map['price_range'] as List).forceDouble(), + propertyType: (map['property_type'] is String) + ? [] + : (map['property_type'] as List).forceInt(), + outdoorFacilityIds: (map['outdoor_facilitiy_ids'] is String) + ? [] + : ((map['outdoor_facilitiy_ids'] ?? []) as List).forceInt(), + city: map['city'] ?? '', + ); + } + + factory PersonalizedInterestSettings.empty() { + return PersonalizedInterestSettings( + userId: 0, + categoryIds: [], + priceRange: [0, 1], + propertyType: [], + outdoorFacilityIds: [], + city: '', + ); + } + + @override + String toString() { + return 'PersonalizedInterestSettings{userId: $userId, categoryIds: $categoryIds, priceRange: $priceRange, propertyType: $propertyType, outdoorFacilityIds: $outdoorFacilityIds, city: $city}'; + } +} diff --git a/lib/data/model/ReportProperty/reason_model.dart b/lib/data/model/ReportProperty/reason_model.dart new file mode 100644 index 0000000..82031f0 --- /dev/null +++ b/lib/data/model/ReportProperty/reason_model.dart @@ -0,0 +1,20 @@ +class ReportReason { + final int id; + final String reason; + + ReportReason({required this.id, required this.reason}); + + Map toMap() { + return { + 'id': this.id, + 'reason': this.reason, + }; + } + + factory ReportReason.fromMap(Map map) { + return ReportReason( + id: map['id'] as int, + reason: map['reason'] as String, + ); + } +} diff --git a/lib/data/model/app_language.dart b/lib/data/model/app_language.dart new file mode 100644 index 0000000..e03a155 --- /dev/null +++ b/lib/data/model/app_language.dart @@ -0,0 +1,10 @@ +class AppLanguage { + final String languageName; + final String languageCode; + final String imageURL; + + const AppLanguage( + {required this.languageCode, + required this.languageName, + required this.imageURL}); +} diff --git a/lib/data/model/app_settings_datamodel.dart b/lib/data/model/app_settings_datamodel.dart new file mode 100644 index 0000000..fc3e195 --- /dev/null +++ b/lib/data/model/app_settings_datamodel.dart @@ -0,0 +1,66 @@ +import 'dart:ui'; + +class AppSettingsDataModel { + Color lightTertiary; + String? placeholderLogo; + Color lightSecondary; + Color lightPrimary; + Color darkTertiary; + Color darkSecondary; + Color darkPrimary; + String? splashLogo; + String? appHomeScreen; + bool? isUserActive; + + AppSettingsDataModel( + {required this.lightTertiary, + required this.lightSecondary, + required this.lightPrimary, + required this.darkTertiary, + required this.darkSecondary, + required this.darkPrimary, + this.splashLogo, + this.placeholderLogo, + this.appHomeScreen, + this.isUserActive}); + + AppSettingsDataModel.fromJson(Map json) + : lightTertiary = _colorFromHex(json['light_tertiary']), + placeholderLogo = json['placeholder_logo'], + lightSecondary = _colorFromHex(json['light_secondary']), + lightPrimary = _colorFromHex(json['light_primary']), + darkTertiary = _colorFromHex(json['dark_tertiary']), + darkSecondary = _colorFromHex(json['dark_secondary']), + darkPrimary = _colorFromHex(json['dark_primary']), + splashLogo = json['splash_logo'], + isUserActive = json['is_active'] ?? true, + appHomeScreen = json['app_home_screen']; + + Map toJson() { + final Map data = {}; + data['light_tertiary'] = _colorToHex(lightTertiary); + data['placeholder_logo'] = placeholderLogo; + data['light_secondary'] = _colorToHex(lightSecondary); + data['light_primary'] = _colorToHex(lightPrimary); + data['dark_tertiary'] = _colorToHex(darkTertiary); + data['dark_secondary'] = _colorToHex(darkSecondary); + data['dark_primary'] = _colorToHex(darkPrimary); + data['splash_logo'] = splashLogo; + data['app_home_screen'] = appHomeScreen; + data['isUserActive']=isUserActive; + return data; + } + + // Helper function to convert color from hex string to Color + static Color _colorFromHex(String hexColor) { + final buffer = StringBuffer(); + if (hexColor.length == 6 || hexColor.length == 7) buffer.write('ff'); + buffer.write(hexColor.replaceFirst('#', '')); + return Color(int.parse(buffer.toString(), radix: 16)); + } + + // Helper function to convert Color to hex string + static String _colorToHex(Color color) { + return '#${color.value.toRadixString(16).substring(2)}'; + } +} diff --git a/lib/data/model/article_model.dart b/lib/data/model/article_model.dart new file mode 100644 index 0000000..3d87298 --- /dev/null +++ b/lib/data/model/article_model.dart @@ -0,0 +1,29 @@ +import '../../utils/Extensions/lib/adaptive_type.dart'; + +class ArticleModel { + int? id; + String? image; + String? title; + String? description; + String? date; + + ArticleModel({this.id, this.image, this.title, this.description, this.date}); + + ArticleModel.fromJson(Map json) { + id = Adapter.forceInt(json['id']); + image = json['image']; + title = json['title']; + description = json['description']; + date = json['created_at']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['image'] = image; + data['title'] = title; + data['description'] = description; + data['created_at'] = date; + return data; + } +} diff --git a/lib/data/model/category.dart b/lib/data/model/category.dart new file mode 100644 index 0000000..950c72b --- /dev/null +++ b/lib/data/model/category.dart @@ -0,0 +1,64 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +import 'package:ebroker/utils/api.dart'; + +class Category { + String? id; + String? category; + String? image; + //String? typeids; + //List? type; + Map? parameterTypes; + //List fields = []; + Category({this.id, this.category, this.image, this.parameterTypes}); + + Category.fromJson(Map json) { + id = json[Api.id].toString(); + category = json[Api.category]; + image = json[Api.image] ?? ""; + parameterTypes = json[Api.parameterTypes] ?? {}; + } + + Category.fromProperty(Map json) { + id = json[Api.id].toString(); + category = json[Api.category]; + } + + Map toMap() { + return { + 'id': id, + 'category': category, + 'image': image, + 'parameterTypes': parameterTypes, + }; + } + + factory Category.fromMap(Map map) { + return Category( + id: map['id'] != null ? map['id'] as String : null, + category: map['category'] != null ? map['category'] as String : null, + image: map['image'] != null ? map['image'] as String : null, + parameterTypes: map['parameterTypes'], + ); + } + + String toJson() => json.encode(toMap()); + + @override + String toString() { + return 'Category(id: $id, category: $category, image: $image, parameterTypes: $parameterTypes)'; + } +} + +class Type { + String? id; + String? type; + + Type({this.id, this.type}); + + Type.fromJson(Map json) { + id = json[Api.id].toString(); + type = json[Api.type]; + } +} diff --git a/lib/data/model/chat/chat_message_modal.dart b/lib/data/model/chat/chat_message_modal.dart new file mode 100644 index 0000000..bb38ab9 --- /dev/null +++ b/lib/data/model/chat/chat_message_modal.dart @@ -0,0 +1,48 @@ +class ChatMessageModal { + int? id; + int? senderId; + int? receiverId; + int? propertyId; + String? message; + String? file; + String? audio; + String? createdAt; + String? updatedAt; + + ChatMessageModal( + {this.id, + this.senderId, + this.receiverId, + this.propertyId, + this.message, + this.file, + this.audio, + this.createdAt, + this.updatedAt}); + + ChatMessageModal.fromJson(Map json) { + id = json['id']; + senderId = json['sender_id']; + receiverId = json['receiver_id']; + propertyId = json['property_id']; + message = json['message']; + file = json['file']; + audio = json['audio']; + createdAt = json['created_at']; + updatedAt = json['updated_at']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['sender_id'] = senderId; + data['receiver_id'] = receiverId; + data['property_id'] = propertyId; + data['message'] = message; + data['file'] = file; + data['audio'] = audio; + data['created_at'] = createdAt; + data['updated_at'] = updatedAt; + return data; + } +} diff --git a/lib/data/model/chat/chated_user_model.dart b/lib/data/model/chat/chated_user_model.dart new file mode 100644 index 0000000..4e4c6f0 --- /dev/null +++ b/lib/data/model/chat/chated_user_model.dart @@ -0,0 +1,52 @@ +// import 'dart:developer'; + +import 'package:flutter/material.dart'; + +class ChatedUser { + int? propertyId; + String? title; + String? titleImage; + int? userId; + String? name; + String? profile; + String? firebaseId; + String? fcmId; + + ChatedUser( + {this.propertyId, + this.title, + this.titleImage, + this.userId, + this.name, + this.profile, + this.firebaseId, + this.fcmId}); + + ChatedUser.fromJson(Map json, {BuildContext? context}) { + if (context != null) { + precacheImage(NetworkImage(json['profile']), context); + precacheImage(NetworkImage(json['title_image']), context); + } + propertyId = json['property_id']; + title = json['title']; + titleImage = json['title_image']; + userId = json['user_id']; + name = json['name']; + profile = json['profile']; + firebaseId = json['firebase_id']; + fcmId = json['fcm_id']; + } + + Map toJson() { + final Map data = {}; + data['property_id'] = propertyId; + data['title'] = title; + data['title_image'] = titleImage; + data['user_id'] = userId; + data['name'] = name; + data['profile'] = profile; + data['firebase_id'] = firebaseId; + data['fcm_id'] = fcmId; + return data; + } +} diff --git a/lib/data/model/city_model.dart b/lib/data/model/city_model.dart new file mode 100644 index 0000000..c1ea002 --- /dev/null +++ b/lib/data/model/city_model.dart @@ -0,0 +1,39 @@ +import 'dart:convert'; + +// ignore_for_file: public_member_api_docs, sort_constructors_first +class City { + final String name; + final int count; + final String image; + City({ + required this.name, + required this.count, + required this.image, + }); + + @override + String toString() => 'City(name: $name, count: $count, image: $image)'; + + City copyWith({String? name, int? count, String? image}) { + return City( + name: name ?? this.name, + count: count ?? this.count, + image: image ?? this.image); + } + + Map toMap() { + return {'City': name, 'Count': count, "image": image}; + } + + factory City.fromMap(Map map) { + return City( + name: map['City'] as String, + count: map['Count'] as int, + image: map['image']); + } + + String toJson() => json.encode(toMap()); + + factory City.fromJson(String source) => + City.fromMap(json.decode(source) as Map); +} diff --git a/lib/data/model/company.dart b/lib/data/model/company.dart new file mode 100644 index 0000000..403e6e6 --- /dev/null +++ b/lib/data/model/company.dart @@ -0,0 +1,36 @@ +class Company { + String? companyName; + String? companyWebsite; + String? companyEmail; + String? companyAddress; + String? companyTel1; + String? companyTel2; + + Company( + {this.companyName, + this.companyWebsite, + this.companyEmail, + this.companyAddress, + this.companyTel1, + this.companyTel2}); + + Company.fromJson(Map json) { + companyName = json['company_name']; + companyWebsite = json['company_website']; + companyEmail = json['company_email']; + companyAddress = json['company_address']; + companyTel1 = json['company_tel1']; + companyTel2 = json['company_tel2']; + } + + Map toJson() { + final Map data = new Map(); + data['company_name'] = this.companyName; + data['company_website'] = this.companyWebsite; + data['company_email'] = this.companyEmail; + data['company_address'] = this.companyAddress; + data['company_tel1'] = this.companyTel1; + data['company_tel2'] = this.companyTel2; + return data; + } +} diff --git a/lib/data/model/data_output.dart b/lib/data/model/data_output.dart new file mode 100644 index 0000000..5865249 --- /dev/null +++ b/lib/data/model/data_output.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +/*when we need to parse API data. this class will helpfull it will give you easy + access of data without using Map and if you see in this class it will be any type, +its like dynamic, instead of creating new model for data output we use T, or any english Capital +alphabets you can use any like */ +class DataOutput { + final int total; + final List modelList; + final ExtraData? extraData; + DataOutput({required this.total, required this.modelList, this.extraData}); + + DataOutput copyWith({ + int? total, + int? offset, + List? modelList, + ExtraData? extraData, + }) { + return DataOutput( + total: total ?? this.total, + modelList: modelList ?? this.modelList, + extraData: extraData ?? this.extraData, + ); + } +} + +@protected +class ExtraData { + final T data; + ExtraData({ + required this.data, + }); +} diff --git a/lib/data/model/enquiry_status.dart b/lib/data/model/enquiry_status.dart new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/data/model/enquiry_status.dart @@ -0,0 +1 @@ + diff --git a/lib/data/model/gallery.dart b/lib/data/model/gallery.dart new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/data/model/gallery.dart @@ -0,0 +1 @@ + diff --git a/lib/data/model/google_place_model.dart b/lib/data/model/google_place_model.dart new file mode 100644 index 0000000..df4a76a --- /dev/null +++ b/lib/data/model/google_place_model.dart @@ -0,0 +1,95 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +class GooglePlaceModel { + final String city; + final String description; + final String placeId; + final String latitude; + final String longitude; + final String state; + final String country; + + GooglePlaceModel({ + required this.state, + required this.country, + required this.city, + required this.description, + required this.placeId, + required this.latitude, + required this.longitude, + }); + + GooglePlaceModel copyWith( + {String? name, + String? cityName, + String? placeId, + String? latitude, + String? longitude, + String? state, + String? country}) { + return GooglePlaceModel( + city: name ?? city, + state: state ?? this.state, + country: country ?? this.country, + description: cityName ?? description, + placeId: placeId ?? this.placeId, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + ); + } + + Map toMap() { + return { + 'name': city, + 'desctiption': description, + 'placeId': placeId, + 'latitude': latitude, + 'longitude': longitude, + 'state': state, + 'country': country + }; + } + + factory GooglePlaceModel.fromMap(Map map) { + return GooglePlaceModel( + country: map['country'] as String, + state: map['state'] as String, + city: map['name'] as String, + description: map['cityName'] as String, + placeId: map['placeId'] as String, + latitude: map['latitude'] as String, + longitude: map['longitude'] as String, + ); + } + + String toJson() => json.encode(toMap()); + + factory GooglePlaceModel.fromJson(String source) => + GooglePlaceModel.fromMap(json.decode(source) as Map); + + @override + String toString() { + return 'GooglePlaceModel(city: $city, description: $description, placeId: $placeId, latitude: $latitude, longitude: $longitude, state: $state, country: $country)'; + } + + @override + bool operator ==(covariant GooglePlaceModel other) { + if (identical(this, other)) return true; + + return other.city == city && + other.description == description && + other.placeId == placeId && + other.latitude == latitude && + other.longitude == longitude; + } + + @override + int get hashCode { + return city.hashCode ^ + description.hashCode ^ + placeId.hashCode ^ + latitude.hashCode ^ + longitude.hashCode; + } +} diff --git a/lib/data/model/google_place_predictions_model.dart b/lib/data/model/google_place_predictions_model.dart new file mode 100644 index 0000000..7968efe --- /dev/null +++ b/lib/data/model/google_place_predictions_model.dart @@ -0,0 +1,54 @@ +class GooglePlaceResponseModel { + List? predictions; + String? status; + + GooglePlaceResponseModel({this.predictions, this.status}); + + GooglePlaceResponseModel.fromJson(Map json) { + if (json['predictions'] != null) { + predictions = []; + json['predictions'].forEach((v) { + predictions!.add(Predictions.fromJson(v)); + }); + } + status = json['status']; + } + + Map toJson() { + final Map data = {}; + if (predictions != null) { + data['predictions'] = predictions!.map((v) => v.toJson()).toList(); + } + data['status'] = status; + return data; + } +} + +class Predictions { + String? description; + String? placeId; + String? reference; + + Predictions({ + this.description, + this.placeId, + this.reference, + }); + + Predictions.fromJson(Map json) { + description = json['description']; + + placeId = json['place_id']; + reference = json['reference']; + } + + Map toJson() { + final Map data = {}; + data['description'] = description; + + data['place_id'] = placeId; + data['reference'] = reference; + + return data; + } +} diff --git a/lib/data/model/home_slider.dart b/lib/data/model/home_slider.dart new file mode 100644 index 0000000..ae16599 --- /dev/null +++ b/lib/data/model/home_slider.dart @@ -0,0 +1,52 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +import 'package:ebroker/utils/api.dart'; + +class HomeSlider { + String? id; + String? image; + String? categoryId; + String? propertysId; + bool? promoted; + + HomeSlider( + {this.id, this.image, this.categoryId, this.propertysId, this.promoted}); + + HomeSlider.fromJson(Map json) { + id = json[Api.id].toString(); + categoryId = json[Api.categoryId].toString(); + image = json[Api.image]; + propertysId = json[Api.propertysId].toString(); + promoted = json[Api.promoted]; + } + + @override + String toString() { + return 'HomeSlider(id: $id, image: $image, categoryId: $categoryId, propertysId: $propertysId, promoted: $promoted)'; + } + + Map toMap() { + return { + 'id': id, + 'image': image, + 'categoryId': categoryId, + 'propertysId': propertysId, + 'promoted': promoted, + }; + } + + factory HomeSlider.fromMap(Map map) { + return HomeSlider( + id: map['id'] != null ? map['id'] as String : null, + image: map['image'] != null ? map['image'] as String : null, + categoryId: + map['categoryId'] != null ? map['categoryId'] as String : null, + propertysId: + map['propertysId'] != null ? map['propertysId'] as String : null, + promoted: map['promoted'] != null ? map['promoted'] as bool : null, + ); + } + + String toJson() => json.encode(toMap()); +} diff --git a/lib/data/model/house_type.dart b/lib/data/model/house_type.dart new file mode 100644 index 0000000..4dd699b --- /dev/null +++ b/lib/data/model/house_type.dart @@ -0,0 +1,13 @@ +import '../../utils/api.dart'; + +class HouseType { + String? id; + String? type; + + HouseType({this.id, this.type}); + + HouseType.fromJson(Map json) { + id = json[Api.id].toString(); + type = json[Api.type]; + } +} diff --git a/lib/data/model/interested_user_model.dart b/lib/data/model/interested_user_model.dart new file mode 100644 index 0000000..c468fd2 --- /dev/null +++ b/lib/data/model/interested_user_model.dart @@ -0,0 +1,49 @@ +import 'package:ebroker/utils/Extensions/lib/adaptive_type.dart'; + +class InterestedUserModel { + int? id; + String? name; + String? image; + String? email; + String? mobile; + int? customertotalpost; + String? runtimeTypeLog; + + InterestedUserModel( + {this.id, + this.name, + this.image, + this.email, + this.mobile, + this.customertotalpost, + this.runtimeTypeLog}); + + InterestedUserModel.fromJson(Map json) { + try { + id = Adapter.forceInt(json['id']); + name = json['name']; + image = json['profile'] ?? ""; + email = json['email']; + mobile = json['mobile']; + customertotalpost = json['customertotalpost']; + runtimeTypeLog = + json.map((key, value) => MapEntry(key, value.runtimeType)).toString(); + } catch (e) { + runtimeTypeLog = + json.map((key, value) => MapEntry(key, value.runtimeType)).toString(); + print( + "Issue while create Interested Model ${e} : ${json.map((key, value) => MapEntry(key, value.runtimeType)).toString()}"); + } + } + + Map toJson() { + final Map data = new Map(); + data['id'] = id; + data['name'] = name; + data['profile'] = image; + data['email'] = email; + data['mobile'] = mobile; + data['customertotalpost'] = customertotalpost; + return data; + } +} diff --git a/lib/data/model/notification_data.dart b/lib/data/model/notification_data.dart new file mode 100644 index 0000000..f8ad7a3 --- /dev/null +++ b/lib/data/model/notification_data.dart @@ -0,0 +1,39 @@ +import 'package:ebroker/utils/Extensions/lib/adaptive_type.dart'; + +class NotificationData { + String? id; + String? title; + String? message; + String? image; + String? type; + int? sendType; + String? customersId; + String? propertysId; + String? createdAt; + String? created; + + NotificationData( + {this.id, + this.title, + this.message, + this.image, + this.type, + this.sendType, + this.customersId, + this.propertysId, + this.createdAt, + this.created}); + + NotificationData.fromJson(Map json) { + id = json['id'].toString(); + title = json['title']; + message = json['message']; + image = json['image']; + type = json['type'].toString(); + sendType = Adapter.forceInt(json['send_type']); + customersId = json['customers_id']; + propertysId = json['propertys_id'].toString(); + createdAt = json['created_at']; + created = json['created']; + } +} diff --git a/lib/data/model/outdoor_facility.dart b/lib/data/model/outdoor_facility.dart new file mode 100644 index 0000000..8a6d03d --- /dev/null +++ b/lib/data/model/outdoor_facility.dart @@ -0,0 +1,36 @@ +class OutdoorFacility { + int? id; + String? name; + String? image; + String? distance; + String? createdAt; + String? updatedAt; + + OutdoorFacility( + {this.id, this.name, this.image, this.createdAt, this.updatedAt,this.distance}); + + OutdoorFacility.fromJson(Map json) { + id = json['id']; + name = json['name']; + image = json['image']; + createdAt = json['created_at']; + updatedAt = json['updated_at']; + distance=json['distance']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['name'] = name; + data['image'] = image; + data['created_at'] = createdAt; + data['updated_at'] = updatedAt; + data['distance']=distance; + return data; + } + + @override + String toString() { + return 'OutdoorFacility{id: $id, name: $name, image: $image, distance: $distance, createdAt: $createdAt, updatedAt: $updatedAt}'; + } +} diff --git a/lib/data/model/project_model.dart b/lib/data/model/project_model.dart new file mode 100644 index 0000000..d4fd4fc --- /dev/null +++ b/lib/data/model/project_model.dart @@ -0,0 +1,279 @@ +class ProjectModel { + int? id; + String? slugId; + int? categoryId; + String? title; + String? description; + String? metaTitle; + String? metaDescription; + String? metaKeywords; + String? metaImage; + String? image; + String? videoLink; + String? location; + String? latitude; + String? longitude; + String? city; + String? state; + String? country; + String? type; + int? status; + String? createdAt; + String? updatedAt; + int? addedBy; + Customer? customer; + List? gallaryImages; + List? documents; + List? plans; + ProjectCategory? category; + + ProjectModel({ + this.id, + this.slugId, + this.categoryId, + this.title, + this.description, + this.metaTitle, + this.metaDescription, + this.metaKeywords, + this.metaImage, + this.image, + this.videoLink, + this.location, + this.latitude, + this.longitude, + this.city, + this.state, + this.country, + this.type, + this.status, + this.createdAt, + this.updatedAt, + this.addedBy, + this.customer, + this.gallaryImages, + this.documents, + this.plans, + this.category, + }); + + factory ProjectModel.fromMap(Map map) { + print("PROJECT RESPONSE IS $map"); + return ProjectModel( + id: map['id'], + slugId: map['slug_id'], + categoryId: map['category_id'], + title: map['title'], + description: map['description'], + metaTitle: map['meta_title'], + metaDescription: map['meta_description'], + metaKeywords: map['meta_keywords'], + metaImage: map['meta_image'], + image: map['image'], + videoLink: map['video_link'], + location: map['location'], + latitude: map['latitude'], + longitude: map['longitude'], + city: map['city'], + state: map['state'], + country: map['country'], + type: map['type'], + status: map['status'], + createdAt: map['created_at'], + updatedAt: map['updated_at'], + addedBy: map['added_by'], + customer: Customer.fromMap(map['customer']), + gallaryImages: List.from( + map['gallary_images'].map((x) => Document.fromMap(x))), + documents: + List.from(map['documents'].map((x) => Document.fromMap(x))), + plans: List.from(map['plans'].map((x) => Plan.fromMap(x))), + category: ProjectCategory.fromMap(map['category']), + ); + } + + Map toMap() { + return { + 'id': id, + 'slug_id': slugId, + 'category_id': categoryId, + 'title': title, + 'description': description, + 'meta_title': metaTitle, + 'meta_description': metaDescription, + 'meta_keywords': metaKeywords, + 'meta_image': metaImage, + 'image': image, + 'video_link': videoLink, + 'location': location, + 'latitude': latitude, + 'longitude': longitude, + 'city': city, + 'state': state, + 'country': country, + 'type': type, + 'status': status, + 'created_at': createdAt, + 'updated_at': updatedAt, + 'added_by': addedBy, + 'customer': customer?.toMap(), + 'gallary_images': gallaryImages?.map((e) => e.toMap()).toList(), + 'documents': documents?.map((x) => x.toMap()).toList(), + 'plans': plans?.map((x) => x.toMap()).toList(), + 'category': category?.toMap(), + }; + } + + @override + String toString() { + return 'ProjectModel(id: $id, slugId: $slugId, categoryId: $categoryId, title: $title, description: $description, metaTitle: $metaTitle, metaDescription: $metaDescription, metaKeywords: $metaKeywords, metaImage: $metaImage, image: $image, videoLink: $videoLink, location: $location, latitude: $latitude, longitude: $longitude, city: $city, state: $state, country: $country, type: $type, status: $status, createdAt: $createdAt, updatedAt: $updatedAt, addedBy: $addedBy, customer: $customer, gallaryImages: $gallaryImages, documents: $documents, plans: $plans, category: $category)'; + } +} + +class Customer { + int? id; + String? name; + String? profile; + String? email; + String? mobile; + int? customertotalpost; + + Customer({ + this.id, + this.name, + this.profile, + this.email, + this.mobile, + this.customertotalpost, + }); + + factory Customer.fromMap(Map map) { + return Customer( + id: map['id'], + name: map['name'], + profile: map['profile'], + email: map['email'], + mobile: map['mobile'], + customertotalpost: map['customertotalpost'], + ); + } + + Map toMap() { + return { + 'id': id, + 'name': name, + 'profile': profile, + 'email': email, + 'mobile': mobile, + 'customertotalpost': customertotalpost, + }; + } +} + +class Document { + int? id; + String? name; + String? type; + String? createdAt; + String? updatedAt; + int? projectId; + + Document({ + this.id, + this.name, + this.type, + this.createdAt, + this.updatedAt, + this.projectId, + }); + + factory Document.fromMap(Map map) { + return Document( + id: map['id'], + name: map['name'], + type: map['type'], + createdAt: map['created_at'], + updatedAt: map['updated_at'], + projectId: map['project_id'], + ); + } + + Map toMap() { + return { + 'id': id, + 'name': name, + 'type': type, + 'created_at': createdAt, + 'updated_at': updatedAt, + 'project_id': projectId, + }; + } +} + +class Plan { + int? id; + String? title; + String? document; + String? createdAt; + String? updatedAt; + int? projectId; + + Plan({ + this.id, + this.title, + this.document, + this.createdAt, + this.updatedAt, + this.projectId, + }); + + factory Plan.fromMap(Map map) { + return Plan( + id: map['id'], + title: map['title'], + document: map['document'], + createdAt: map['created_at'], + updatedAt: map['updated_at'], + projectId: map['project_id'], + ); + } + + Map toMap() { + return { + 'id': id, + 'title': title, + 'document': document, + 'created_at': createdAt, + 'updated_at': updatedAt, + 'project_id': projectId, + }; + } +} + +class ProjectCategory { + final int? id; + final String? category; + final String? image; + + ProjectCategory({ + this.id, + this.category, + this.image, + }); + + factory ProjectCategory.fromMap(Map map) { + return ProjectCategory( + id: map['id'], + category: map['category'], + image: map['image'], + ); + } + + Map toMap() { + return { + 'id': id, + 'category': category, + 'image': image, + }; + } +} diff --git a/lib/data/model/property_model.dart b/lib/data/model/property_model.dart new file mode 100644 index 0000000..643edd0 --- /dev/null +++ b/lib/data/model/property_model.dart @@ -0,0 +1,582 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first, unused_import +// To parse this JSON data, do +// +// final propertyModel = propertyModelFromMap(jsonString); +import 'dart:convert'; +import 'dart:developer'; + +import 'package:ebroker/utils/Extensions/lib/adaptive_type.dart'; + +import '../../utils/helper_utils.dart'; + +class PropertyModel { + PropertyModel( + {this.id, + this.title, + this.customerName, + this.customerEmail, + this.customerNumber, + this.customerProfile, + this.price, + this.category, + this.builtUpArea, + this.plotArea, + this.hectaArea, + this.acre, + this.houseType, + this.furnished, + this.unitType, + this.description, + this.address, + this.clientAddress, + this.properyType, + this.titleImage, + this.postCreated, + this.gallery, + this.totalView, + this.status, + this.state, + this.city, + this.country, + this.addedBy, + this.inquiry, + this.promoted, + this.isFavourite, + this.rentduration, + this.isInterested, + this.favouriteUsers, + this.interestedUsers, + this.totalInterestedUsers, + this.totalFavouriteUsers, + this.parameters, + this.latitude, + this.longitude, + this.threeDImage, + this.advertisment, + this.video, + this.assignedOutdoorFacility, + this.slugId, + this.allPropData, + this.titleimagehash}); + + final int? id; + final String? title; + final String? price; + final String? customerName; + final String? customerEmail; + final String? customerProfile; + final String? customerNumber; + final String? rentduration; + final Categorys? category; + final dynamic builtUpArea; + final dynamic plotArea; + final dynamic hectaArea; + final dynamic acre; + final dynamic houseType; + final dynamic furnished; + final UnitType? unitType; + final String? description; + final String? address; + final String? clientAddress; + String? properyType; + final String? titleImage; + final String? titleimagehash; + final String? postCreated; + final List? gallery; + final int? totalView; + final int? status; + final String? state; + final String? city; + final String? country; + final int? addedBy; + final bool? inquiry; + final bool? promoted; + final int? isFavourite; + final int? isInterested; + final List? favouriteUsers; + final List? interestedUsers; + final int? totalInterestedUsers; + final int? totalFavouriteUsers; + final List? parameters; + final List? assignedOutdoorFacility; + final String? latitude; + final String? longitude; + final String? threeDImage; + final String? video; + final dynamic advertisment; + final String? slugId; + final dynamic allPropData; + PropertyModel copyWith( + {int? id, + String? title, + String? price, + Categorys? category, + dynamic builtUpArea, + dynamic plotArea, + dynamic hectaArea, + dynamic acre, + dynamic houseType, + dynamic furnished, + UnitType? unitType, + String? description, + String? address, + String? clientAddress, + String? properyType, + String? titleImage, + String? postCreated, + List? gallery, + int? totalView, + int? status, + String? state, + String? city, + String? country, + int? addedBy, + bool? inquiry, + bool? promoted, + int? isFavourite, + int? isInterested, + List? favouriteUsers, + List? interestedUsers, + int? totalInterestedUsers, + int? totalFavouriteUsers, + List? parameters, + List? assignedOutdoorFacility, + String? latitude, + String? longitude, + String? threeDimage, + String? video, + dynamic advertisment, + String? rentduration, + String? titleImageHash}) => + PropertyModel( + id: id ?? this.id, + rentduration: rentduration ?? this.rentduration, + advertisment: advertisment ?? this.advertisment, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + title: title ?? this.title, + price: price ?? this.price, + category: category ?? this.category, + builtUpArea: builtUpArea ?? this.builtUpArea, + plotArea: plotArea ?? this.plotArea, + hectaArea: hectaArea ?? this.hectaArea, + acre: acre ?? this.acre, + houseType: houseType ?? this.houseType, + furnished: furnished ?? this.furnished, + unitType: unitType ?? this.unitType, + description: description ?? this.description, + address: address ?? this.address, + clientAddress: clientAddress ?? this.clientAddress, + properyType: properyType ?? this.properyType, + titleImage: titleImage ?? this.titleImage, + postCreated: postCreated ?? this.postCreated, + gallery: gallery ?? this.gallery, + totalView: totalView ?? this.totalView, + status: status ?? this.status, + state: state ?? this.state, + city: city ?? this.city, + country: country ?? this.country, + addedBy: addedBy ?? this.addedBy, + inquiry: inquiry ?? this.inquiry, + promoted: promoted ?? this.promoted, + isFavourite: isFavourite ?? this.isFavourite, + isInterested: isInterested ?? this.isInterested, + favouriteUsers: favouriteUsers ?? this.favouriteUsers, + interestedUsers: interestedUsers ?? this.interestedUsers, + totalInterestedUsers: + totalInterestedUsers ?? this.totalInterestedUsers, + totalFavouriteUsers: totalFavouriteUsers ?? this.totalFavouriteUsers, + parameters: parameters ?? this.parameters, + threeDImage: threeDimage ?? threeDImage, + video: video ?? this.video, + assignedOutdoorFacility: + assignedOutdoorFacility ?? this.assignedOutdoorFacility, + titleimagehash: titleImageHash ?? titleimagehash); + + factory PropertyModel.fromMap(Map rawjson) { + try { + List list = + (rawjson['parameters'] as List).map((e) => e['image']).toList(); + HelperUtils.precacheSVG(List.from(list)); + } catch (e) {} + print(rawjson); + return PropertyModel( + id: rawjson["id"], + allPropData: rawjson, + slugId: rawjson['slug_id'], + rentduration: rawjson['rentduration'], + customerEmail: rawjson['email'], + customerProfile: rawjson['profile'], + customerNumber: rawjson['mobile'], + customerName: rawjson['customer_name'], + video: rawjson['video_link'], + threeDImage: rawjson['threeD_image'], + latitude: rawjson['latitude'].toString(), + longitude: rawjson["longitude"].toString(), + title: rawjson["title"].toString(), + price: rawjson["price"].toString(), + category: rawjson["category"] == null + ? null + : Categorys.fromMap(rawjson["category"]), + builtUpArea: rawjson["built_up_area"], + plotArea: rawjson["plot_area"], + hectaArea: rawjson["hecta_area"], + acre: rawjson["acre"], + houseType: rawjson["house_type"], + furnished: rawjson["furnished"], + advertisment: rawjson['advertisement'], + unitType: rawjson["unit_type"] == null + ? null + : UnitType.fromMap(rawjson["unit_type"]), + description: rawjson["description"], + address: rawjson["address"], + clientAddress: rawjson["client_address"], + properyType: rawjson["propery_type"].toString(), + titleImage: rawjson["title_image"], + postCreated: rawjson["post_created"], + gallery: List.from((rawjson["gallery"] as List) + .map((x) => Gallery.fromMap(x is String ? json.decode(x) : x))), + totalView: Adapter.forceInt((rawjson["total_view"] as dynamic)), + status: Adapter.forceInt(rawjson["status"]), + state: rawjson["state"], + city: rawjson["city"], + country: rawjson["country"], + addedBy: Adapter.forceInt((rawjson["added_by"] as dynamic)), + inquiry: rawjson["inquiry"], + promoted: rawjson["promoted"], + isFavourite: Adapter.forceInt(rawjson["is_favourite"]), + isInterested: Adapter.forceInt(rawjson["is_interested"]), + favouriteUsers: rawjson["favourite_users"] == null + ? null + : List.from(rawjson["favourite_users"].map((x) => x)), + interestedUsers: rawjson["interested_users"] == null + ? null + : List.from(rawjson["interested_users"].map((x) => x)), + totalInterestedUsers: + Adapter.forceInt(rawjson["total_interested_users"]), + totalFavouriteUsers: Adapter.forceInt(rawjson["total_favourite_users"]), + parameters: rawjson["parameters"] == null + ? [] + : List.from((rawjson["parameters"] as List).map((x) { + return Parameter.fromMap(x); + })), + assignedOutdoorFacility: rawjson["assign_facilities"] == null + ? [] + : List.from( + (rawjson["assign_facilities"] as List).map((x) { + return AssignedOutdoorFacility.fromJson(x); + })), + titleimagehash: rawjson['title_image_hash']); + } + + Map toMap() => { + "id": id, + 'allPropData': allPropData, + "rentduration": rentduration, + "mobile": customerNumber, + "email": customerEmail, + "customer_name": customerName, + "profile": customerProfile, + "threeD_image": threeDImage, + "title": title, + "latitude": latitude, + "longitude": longitude, + "advertisment": advertisment, + 'video_link': video, + "price": price, + "category": category?.toMap() ?? {}, + "built_up_area": builtUpArea, + "plot_area": plotArea, + "hecta_area": hectaArea, + "acre": acre, + "house_type": houseType, + "furnished": furnished, + "unit_type": unitType?.toMap() ?? {}, + "description": description, + "address": address, + "client_address": clientAddress, + "property_type": properyType, + "title_image": titleImage, + "post_created": postCreated, + "gallery": List.from(gallery?.map((x) => x) ?? []), + "total_view": totalView, + "status": status, + "state": state, + "city": city, + "country": country, + "added_by": addedBy, + "inquiry": inquiry, + "promoted": promoted, + "is_favourite": isFavourite, + "is_interested": isInterested, + "favourite_users": favouriteUsers == null + ? null + : List.from(favouriteUsers?.map((x) => x) ?? []), + "interested_users": interestedUsers == null + ? null + : List.from(interestedUsers?.map((x) => x) ?? []), + "total_interested_users": totalInterestedUsers, + "total_favourite_users": totalFavouriteUsers, + "assign_facilities": assignedOutdoorFacility == null + ? null + : List.from( + assignedOutdoorFacility?.map((e) => e.toJson()) ?? []), + "parameters": parameters == null + ? null + : List.from(parameters?.map((x) => x.toMap()) ?? []), + "title_image_hash": titleimagehash + }; + + @override + String toString() { + return 'PropertyModel(id: $id,rentduration:$rentduration , title: $title,assigned_facilities:[$assignedOutdoorFacility] advertisment:$advertisment, price: $price, category: $category,, builtUpArea: $builtUpArea, plotArea: $plotArea, hectaArea: $hectaArea, acre: $acre, houseType: $houseType, furnished: $furnished, unitType: $unitType, description: $description, address: $address, clientAddress: $clientAddress, properyType: $properyType, titleImage: $titleImage, title_image_hash: $titleimagehash, postCreated: $postCreated, gallery: $gallery, totalView: $totalView, status: $status, state: $state, city: $city, country: $country, addedBy: $addedBy, inquiry: $inquiry, promoted: $promoted, isFavourite: $isFavourite, isInterested: $isInterested, favouriteUsers: $favouriteUsers, interestedUsers: $interestedUsers, totalInterestedUsers: $totalInterestedUsers, totalFavouriteUsers: $totalFavouriteUsers, parameters: $parameters, latitude: $latitude, longitude: $longitude, threeD_image: $threeDImage, video: $video)'; + } +} + +class Categorys { + Categorys({ + this.id, + this.category, + this.image, + }); + + final int? id; + final String? category; + final String? image; + + Categorys copyWith({ + int? id, + String? category, + String? image, + }) => + Categorys( + id: id ?? this.id, + category: category ?? this.category, + image: image ?? this.image, + ); + + factory Categorys.fromJson(String str) => Categorys.fromMap(json.decode(str)); + + String toJson() => json.encode(toMap()); + + factory Categorys.fromMap(Map json) => Categorys( + id: json["id"], + category: json["category"], + image: json["image"], + ); + + Map toMap() => { + "id": id, + "category": category, + "image": image, + }; +} + +class Parameter { + Parameter({ + this.id, + this.name, + this.typeOfParameter, + this.typeValues, + this.image, + this.value, + }); + + final int? id; + final String? name; + final String? typeOfParameter; + final dynamic typeValues; + final String? image; + final dynamic value; + + Parameter copyWith({ + int? id, + String? name, + String? typeOfParameter, + dynamic typeValues, + String? image, + dynamic value, + }) => + Parameter( + id: id ?? this.id, + name: name ?? this.name, + typeOfParameter: typeOfParameter ?? this.typeOfParameter, + typeValues: typeValues ?? this.typeValues, + image: image ?? this.image, + value: value ?? this.value, + ); + + static dynamic ifListConvertToString(dynamic value) { + if (value is List) { + return value.join(","); + } + + return value; + } + + factory Parameter.fromMap(Map json) { + return Parameter( + id: json["id"], + name: json["name"], + typeOfParameter: json["type_of_parameter"], + typeValues: json["type_values"], + image: json["image"], + value: ifListConvertToString(json['value']), + ); + } + + Map toMap() => { + "id": id, + "name": name, + "type_of_parameter": typeOfParameter, + "type_values": typeValues, + "image": image, + "value": value, + }; + + @override + String toString() { + return 'Parameter(id: $id, name: $name, typeOfParameter: $typeOfParameter, typeValues: $typeValues, image: $image, value: $value)'; + } +} + +class UnitType { + UnitType({ + this.id, + this.measurement, + }); + + final int? id; + final String? measurement; + + UnitType copyWith({ + int? id, + String? measurement, + }) => + UnitType( + id: id ?? this.id, + measurement: measurement ?? this.measurement, + ); + + factory UnitType.fromJson(String str) => UnitType.fromMap(json.decode(str)); + + String toJson() => json.encode(toMap()); + + factory UnitType.fromMap(Map json) => UnitType( + id: json["id"], + measurement: json["measurement"], + ); + + Map toMap() => { + "id": id, + "measurement": measurement, + }; +} + +class Gallery { + final int id; + final String image; + final String imageUrl; + final bool? isVideo; + Gallery( + {required this.id, + required this.image, + required this.imageUrl, + this.isVideo}); + + Gallery copyWith({ + int? id, + String? image, + String? imageUrl, + }) { + return Gallery( + id: id ?? this.id, + image: image ?? this.image, + imageUrl: imageUrl ?? this.imageUrl, + ); + } + + Map toMap() { + return { + 'id': id, + 'image': image, + 'image_url': imageUrl, + }; + } + + factory Gallery.fromMap(Map map) { + return Gallery( + id: map['id'] as int, + image: map['image'] as String, + imageUrl: map['image_url'] ?? "", + ); + } + + String toJson() => json.encode(toMap()); + + factory Gallery.fromJson(String source) => + Gallery.fromMap(json.decode(source) as Map); + + @override + String toString() => 'Gallery(id: $id, image: $image, imageUrl: $imageUrl)'; + + @override + bool operator ==(covariant Gallery other) { + if (identical(this, other)) return true; + + return other.id == id && other.image == image && other.imageUrl == imageUrl; + } + + @override + int get hashCode => id.hashCode ^ image.hashCode ^ imageUrl.hashCode; +} + +class AssignedOutdoorFacility { + int? id; + int? propertyId; + int? facilityId; + int? distance; + String? image; + String? name; + String? createdAt; + String? updatedAt; + + AssignedOutdoorFacility( + {this.id, + this.propertyId, + this.facilityId, + this.distance, + this.createdAt, + this.name, + this.image, + this.updatedAt}); + + AssignedOutdoorFacility.fromJson(Map json) { + id = json['id']; + propertyId = Adapter.forceInt(json['property_id']); + facilityId = Adapter.forceInt(json['facility_id']); + distance = Adapter.forceInt(json['distance']); + createdAt = json['created_at']; + image = json['image']; + name = json['name']; + updatedAt = json['updated_at']; + } + + Map toJson() { + final Map data = Map(); + data['id'] = this.id; + data['property_id'] = this.propertyId; + data['facility_id'] = this.facilityId; + data['distance'] = this.distance; + data['created_at'] = this.createdAt; + data['updated_at'] = this.updatedAt; + data['image'] = image; + data['name'] = name; + return data; + } + + @override + String toString() { + return 'AssignedOutdoorFacility{id: $id, propertyId: $propertyId, facilityId: $facilityId, distance: $distance, image: $image, name: $name, createdAt: $createdAt, updatedAt: $updatedAt}'; + } +} diff --git a/lib/data/model/propery_filter_model.dart b/lib/data/model/propery_filter_model.dart new file mode 100644 index 0000000..ee1292f --- /dev/null +++ b/lib/data/model/propery_filter_model.dart @@ -0,0 +1,109 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +class PropertyFilterModel { + final String propertyType; + final String maxPrice; + final String minPrice; + final String categoryId; + final String postedSince; + final String city; + final String state; + final String country; + PropertyFilterModel( + {required this.propertyType, + required this.maxPrice, + required this.minPrice, + required this.categoryId, + required this.postedSince, + required this.city, + required this.state, + required this.country}); + + PropertyFilterModel copyWith( + {String? propertyType, + String? maxPrice, + String? minPrice, + String? categoryId, + String? postedSince, + String? city, + String? state, + String? country}) { + return PropertyFilterModel( + propertyType: propertyType ?? this.propertyType, + maxPrice: maxPrice ?? this.maxPrice, + minPrice: minPrice ?? this.minPrice, + categoryId: categoryId ?? this.categoryId, + postedSince: postedSince ?? this.postedSince, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country); + } + + Map toMap() { + return { + 'property_type': propertyType, + 'max_price': maxPrice, + 'min_price': minPrice, + 'category_id': categoryId, + 'posted_since': postedSince, + "city": city, + "state": state, + "country": country + }; + } + + @override + String toString() { + return 'PropertyFilterModel(propertyType: $propertyType, maxPrice: $maxPrice, minPrice: $minPrice, categoryId: $categoryId, postedSince: $postedSince)'; + } + + factory PropertyFilterModel.createEmpty() { + return PropertyFilterModel( + propertyType: "", + maxPrice: "", + minPrice: "", + categoryId: "", + postedSince: "", + city: '', + country: '', + state: ''); + } + factory PropertyFilterModel.fromMap(Map map) { + return PropertyFilterModel( + city: map['city'].toString(), + state: map['state'].toString(), + country: map['country'].toString(), + propertyType: map['property_type'].toString(), + maxPrice: map['max_price'].toString(), + minPrice: map['min_price'].toString(), + categoryId: map['category_id'].toString(), + postedSince: map['posted_since'].toString(), + ); + } + + String toJson() => json.encode(toMap()); + + factory PropertyFilterModel.fromJson(String source) => + PropertyFilterModel.fromMap(json.decode(source) as Map); + + @override + bool operator ==(covariant PropertyFilterModel other) { + if (identical(this, other)) return true; + + return other.propertyType == propertyType && + other.maxPrice == maxPrice && + other.minPrice == minPrice && + other.categoryId == categoryId && + other.postedSince == postedSince; + } + + @override + int get hashCode { + return propertyType.hashCode ^ + maxPrice.hashCode ^ + minPrice.hashCode ^ + categoryId.hashCode ^ + postedSince.hashCode; + } +} diff --git a/lib/data/model/subscription_pacakage_model.dart b/lib/data/model/subscription_pacakage_model.dart new file mode 100644 index 0000000..7778b92 --- /dev/null +++ b/lib/data/model/subscription_pacakage_model.dart @@ -0,0 +1,91 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first + +// import 'package:ebroker/utils/helper_utils.dart'; + +class SubscriptionPackageModel { + int? id; + String? iosProductId; + String? name; + int? duration; + num? price; + int? status; + dynamic propertyLimit; + dynamic advertisementLimit; + String? createdAt; + String? updatedAt; + int? isActive; + int? usedLimitForProperty; + int? usedLimitForAdvertisement; + int? propertyStatus; + int? advertisementStatus; + String? startDate; + String? endDate; + int? remainingDays; + String? type; + SubscriptionPackageModel( + {this.id, + this.iosProductId, + this.name, + this.duration, + this.price, + this.status, + this.propertyLimit, + this.advertisementLimit, + this.createdAt, + this.updatedAt, + this.isActive, + this.usedLimitForProperty, + this.usedLimitForAdvertisement, + this.propertyStatus, + this.advertisementStatus, + this.startDate, + this.type, + this.endDate, + this.remainingDays}); + + SubscriptionPackageModel.fromJson(Map json) { + id = json['id']; + type = json['type']; + iosProductId = json['ios_product_id']; + name = json['name']; + duration = json['duration']; + price = json['price']; + status = json['status']; + propertyLimit = json['property_limit']; + advertisementLimit = json['advertisement_limit']; + createdAt = json['created_at']; + updatedAt = json['updated_at']; + isActive = json['is_active']; + usedLimitForProperty = json['used_limit_for_property']; + usedLimitForAdvertisement = json['used_limit_for_advertisement']; + propertyStatus = json['property_status']; + advertisementStatus = json['advertisement_status']; + startDate = json['start_date']; + endDate = json['end_date']; + remainingDays = json['remaining_days']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = id; + data['type'] = type; + data['ios_product_id'] = iosProductId; + data['name'] = name; + data['duration'] = duration; + data['price'] = price; + data['status'] = status; + data['property_limit'] = propertyLimit; + data['advertisement_limit'] = advertisementLimit; + data['created_at'] = createdAt; + data['updated_at'] = updatedAt; + data['is_active'] = isActive; + data['used_limit_for_property'] = usedLimitForProperty; + data['used_limit_for_advertisement'] = usedLimitForAdvertisement; + data['property_status'] = propertyStatus; + data['advertisement_status'] = advertisementStatus; + data['start_date'] = startDate; + data['end_date'] = endDate; + data['remaining_days'] = remainingDays; + return data; + } +} diff --git a/lib/data/model/subscription_package_limit.dart b/lib/data/model/subscription_package_limit.dart new file mode 100644 index 0000000..22a24cc --- /dev/null +++ b/lib/data/model/subscription_package_limit.dart @@ -0,0 +1,42 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +// To parse this JSON data, do +// +// final subcriptionPackageLimit = subcriptionPackageLimitFromMap(jsonString); + +class SubcriptionPackageLimit { + final bool error; + final String message; + final bool hasPackage; + final bool isPremium; + + const SubcriptionPackageLimit({ + required this.error, + required this.isPremium, + required this.message, + required this.hasPackage, + }); + + Map toMap() { + return { + 'error': error, + 'message': message, + 'package': hasPackage, + 'isPremium': isPremium + }; + } + + factory SubcriptionPackageLimit.fromMap(Map map) { + return SubcriptionPackageLimit( + error: map['error'] as bool, + message: map['message'] as String, + hasPackage: map['package'] as bool, + isPremium: map['is_premium'] as bool, + ); + } +} + +//*{ +// "error": false, +// "message": "User able to upload", +// "package": true +// } diff --git a/lib/data/model/system_settings_model.dart b/lib/data/model/system_settings_model.dart new file mode 100644 index 0000000..6c057e1 --- /dev/null +++ b/lib/data/model/system_settings_model.dart @@ -0,0 +1,49 @@ +enum SystemSetting { + maintenanceMode, + currencySymball, + subscription, + privacyPolicy, + termsConditions, + contactUs, + language, + defaultLanguage, + forceUpdate, + androidVersion, + numberWithSuffix, + iosVersion, + demoMode +} + + + + + + + + + + + + + + + + + + + + + /// we made this method because from our api all data comes in {'type':"",'data':"demo data"} this formate so we have list of these data and instead of create different methods and parse in it we have made enum and checking where condition in list + // T getSetting(SystemSetting setting) { + // if (setting == SystemSetting.subscription) { + // if (subscription == true) { + // return package as T; + // } else { + // return null as T; + // } + // } + // return data! + // .where((Data element) => + // element.type == Constant.systemSettingKey[setting]) + // .toList()[0] as T; + // } \ No newline at end of file diff --git a/lib/data/model/transaction_model.dart b/lib/data/model/transaction_model.dart new file mode 100644 index 0000000..7e250e4 --- /dev/null +++ b/lib/data/model/transaction_model.dart @@ -0,0 +1,50 @@ +import 'package:ebroker/utils/Extensions/lib/adaptive_type.dart'; + +class TransactionModel { + int? id; + String? transactionId; + dynamic amount; + String? paymentGateway; + int? packageId; + int? customerId; + dynamic status; + String? createdAt; + String? updatedAt; + + TransactionModel( + {this.id, + this.transactionId, + this.amount, + this.paymentGateway, + this.packageId, + this.customerId, + this.status, + this.createdAt, + this.updatedAt}); + + TransactionModel.fromMap(Map json) { + id = json['id']; + transactionId = json['transaction_id']; + amount = (json['amount']); + paymentGateway = json['payment_gateway']; + packageId = Adapter.forceInt(json['package_id']); + customerId = Adapter.forceInt(json['customer_id']); + status = json['status']; + createdAt = json['created_at']; + updatedAt = json['updated_at']; + } + + Map toMap() { + final Map data = {}; + data['id'] = id; + data['transaction_id'] = transactionId; + data['amount'] = amount; + data['payment_gateway'] = paymentGateway; + data['package_id'] = packageId; + data['customer_id'] = customerId; + data['status'] = status; + data['created_at'] = createdAt; + data['updated_at'] = updatedAt; + return data; + } +} diff --git a/lib/data/model/user_model.dart b/lib/data/model/user_model.dart new file mode 100644 index 0000000..fdeecaf --- /dev/null +++ b/lib/data/model/user_model.dart @@ -0,0 +1,86 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:ebroker/utils/Extensions/lib/adaptive_type.dart'; + +class UserModel { + String? address; + String? createdAt; + int? customertotalpost; + String? email; + String? fcmId; + String? firebaseId; + int? id; + int? isActive; + bool? isProfileCompleted; + String? logintype; + String? mobile; + String? name; + int? notification; + String? profile; + String? token; + String? updatedAt; + + UserModel( + {this.address, + this.createdAt, + this.customertotalpost, + this.email, + this.fcmId, + this.firebaseId, + this.id, + this.isActive, + this.isProfileCompleted, + this.logintype, + this.mobile, + this.name, + this.notification, + this.profile, + this.token, + this.updatedAt}); + + UserModel.fromJson(Map json) { + address = json['address']; + createdAt = json['created_at']; + customertotalpost = Adapter.forceInt(json['customertotalpost']); + email = json['email']; + fcmId = json['fcm_id']; + firebaseId = json['firebase_id']; + id = json['id']; + isActive = Adapter.forceInt(json['isActive']); + isProfileCompleted = json['isProfileCompleted']; + logintype = json['logintype']; + mobile = json['mobile']; + name = json['name']; + notification = (json['notification'] is int) + ? json['notification'] + : int.parse((json['notification'] ?? "0")); + profile = json['profile']; + token = json['token']; + updatedAt = json['updated_at']; + } + + Map toJson() { + final Map data = {}; + data['address'] = address; + data['created_at'] = createdAt; + data['customertotalpost'] = customertotalpost; + data['email'] = email; + data['fcm_id'] = fcmId; + data['firebase_id'] = firebaseId; + data['id'] = id; + data['isActive'] = isActive; + data['isProfileCompleted'] = isProfileCompleted; + data['logintype'] = logintype; + data['mobile'] = mobile; + data['name'] = name; + data['notification'] = notification; + data['profile'] = profile; + data['token'] = token; + data['updated_at'] = updatedAt; + return data; + } + + @override + String toString() { + return 'UserModel(address: $address, createdAt: $createdAt, customertotalpost: $customertotalpost, email: $email, fcmId: $fcmId, firebaseId: $firebaseId, id: $id, isActive: $isActive, isProfileCompleted: $isProfileCompleted, logintype: $logintype, mobile: $mobile, name: $name, notification: $notification, profile: $profile, token: $token, updatedAt: $updatedAt)'; + } +} diff --git a/lib/exports/main_export.dart b/lib/exports/main_export.dart new file mode 100644 index 0000000..5c17c33 --- /dev/null +++ b/lib/exports/main_export.dart @@ -0,0 +1,64 @@ +export 'package:device_preview/device_preview.dart'; +export 'package:ebroker/utils/payment/lib/list_gatways.dart'; +export 'package:flutter_bloc/flutter_bloc.dart'; +export 'package:flutter_localizations/flutter_localizations.dart'; + +export '../Ui/screens/chat/chatAudio/globals.dart'; +export '../app/app.dart'; +export '../app/app_localization.dart'; +export '../app/app_theme.dart'; +export '../app/routes.dart'; +export '../data/cubits/Report/fetch_property_report_reason_list.dart'; +export '../data/cubits/Utility/google_place_autocomplate_cubit.dart'; +export '../data/cubits/Utility/house_type_cubit.dart'; +export '../data/cubits/Utility/like_properties.dart'; +export '../data/cubits/Utility/proeprty_edit_global.dart'; +export '../data/cubits/auth/auth_cubit.dart'; +export '../data/cubits/auth/auth_state_cubit.dart'; +export '../data/cubits/auth/login_cubit.dart'; +export '../data/cubits/auth/send_otp_cubit.dart'; +export '../data/cubits/auth/verify_otp_cubit.dart'; +export '../data/cubits/category/fetch_category_cubit.dart'; +export '../data/cubits/category/fetch_cities_category.dart'; +export '../data/cubits/chatCubits/get_chat_users.dart'; +export '../data/cubits/company_cubit.dart'; +export '../data/cubits/enquiry/store_enqury_id.dart'; +export '../data/cubits/favorite/add_to_favorite_cubit.dart'; +export '../data/cubits/favorite/fetch_favorites_cubit.dart'; +export '../data/cubits/favorite/remove_favoriteubit.dart'; +export '../data/cubits/fetch_articles_cubit.dart'; +export '../data/cubits/fetch_notifications_cubit.dart'; +export '../data/cubits/outdoorfacility/fetch_outdoor_facility_list.dart'; +export '../data/cubits/profile_setting_cubit.dart'; +export '../data/cubits/property/create_property_cubit.dart'; +export '../data/cubits/property/favorite_id_properties.dart'; +export '../data/cubits/property/fetch_home_properties_cubit.dart'; +export '../data/cubits/property/fetch_most_liked_properties.dart'; +export '../data/cubits/property/fetch_most_viewed_properties_cubit.dart'; +export '../data/cubits/property/fetch_my_properties_cubit.dart'; +export '../data/cubits/property/fetch_nearby_property_cubit.dart'; +export '../data/cubits/property/fetch_promoted_properties_cubit.dart'; +export '../data/cubits/property/fetch_property_from_category_cubit.dart'; +export '../data/cubits/property/fetch_recent_properties.dart'; +export '../data/cubits/property/fetch_top_rated_properties_cubit.dart'; +export '../data/cubits/property/property_cubit.dart'; +export '../data/cubits/property/search_property_cubit.dart'; +export '../data/cubits/property/set_property_view_cubit.dart'; +export '../data/cubits/property/top_viewed_property_cubit.dart'; +export '../data/cubits/slider_cubit.dart'; +export '../data/cubits/subscription/fetch_subscription_packages_cubit.dart'; +export '../data/cubits/subscription/get_subsctiption_package_limits_cubit.dart'; +export '../data/cubits/system/app_theme_cubit.dart'; +export '../data/cubits/system/delete_account_cubit.dart'; +export '../data/cubits/system/fetch_language_cubit.dart'; +export '../data/cubits/system/fetch_system_settings_cubit.dart'; +export '../data/cubits/system/get_api_keys_cubit.dart'; +export '../data/cubits/system/language_cubit.dart'; +export '../data/cubits/system/notification_cubit.dart'; +export '../data/cubits/system/user_details.dart'; +export '../settings.dart'; +export '../utils/Notification/awsomeNotification.dart'; +export '../utils/Notification/notification_service.dart'; +export '../utils/constant.dart'; +export '../utils/deeplinkManager.dart'; +export '../utils/hive_utils.dart'; diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart new file mode 100644 index 0000000..bca2fc4 --- /dev/null +++ b/lib/firebase_options.dart @@ -0,0 +1,78 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + return web; + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for macos - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.windows: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for windows - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions web = FirebaseOptions( + apiKey: 'AIzaSyAP4lTOZVNyMOo18MYObKOq9W9Ewj-M-6A', + appId: '1:981964114588:web:a618051aeb014542abfb59', + messagingSenderId: '981964114588', + projectId: 'rumahjo123-28a54', + authDomain: 'rumahjo123-28a54.firebaseapp.com', + storageBucket: 'rumahjo123-28a54.appspot.com', + measurementId: 'G-BB0WSX6E1D', + ); + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyBVM2DvCSmwfkwydzfxVgr5IIQyKdSiGx0', + appId: '1:981964114588:android:bb609c3b3d377c07abfb59', + messagingSenderId: '981964114588', + projectId: 'rumahjo123-28a54', + storageBucket: 'rumahjo123-28a54.appspot.com', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'AIzaSyAc9oMfPDPriUh37lbTdVc2xXV_TLFWgSY', + appId: '1:981964114588:ios:6bbab471426bea35abfb59', + messagingSenderId: '981964114588', + projectId: 'rumahjo123-28a54', + storageBucket: 'rumahjo123-28a54.appspot.com', + androidClientId: '981964114588-uh3jjk48ug414f130uq44860qsdd5p0s.apps.googleusercontent.com', + iosClientId: '981964114588-lgetp58i4h4q640vk9jh563ss69vcf0j.apps.googleusercontent.com', + iosBundleId: 'com.ebroker.wrteam', + ); + +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..d603224 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,44 @@ +import 'package:ebroker/app/register_cubits.dart'; +import 'package:flutter/material.dart'; + +import 'Ui/screens/ChatNew/MessageTypes/registerar.dart'; +import 'exports/main_export.dart'; + +///////////////// +////V-1.1.3///// +/////////////// + +/// + +void main() async { + await getAppSettings(); + initApp(); +} + +class EntryPoint extends StatefulWidget { + const EntryPoint({ + Key? key, + }) : super(key: key); + @override + EntryPointState createState() => EntryPointState(); +} + +class EntryPointState extends State { + @override + void initState() { + super.initState(); + ChatMessageHandler.handle(); + ChatGlobals.init(); + } + + @override + Widget build(BuildContext context) { + return MultiBlocProvider( + providers: [ + ...RegisterCubits().register(), + ], + child: Builder(builder: (BuildContext context) { + return const App(); + })); + } +} diff --git a/lib/sandBox/language.dart b/lib/sandBox/language.dart new file mode 100644 index 0000000..a23ad81 --- /dev/null +++ b/lib/sandBox/language.dart @@ -0,0 +1,360 @@ +// // ignore_for_file: public_member_api_docs, sort_constructors_first +// import 'package:flutter/material.dart'; +// import 'package:flutter/rendering.dart'; + +// import 'package:ebroker/utils/uiUtils.dart'; +// import 'package:shimmer/shimmer.dart'; + +// class MyLang { +// static BuildContext? context; + +// static demoText() { +// return UiUtils.getTranslatedLabel(context!, "getCodeBtnLbl"); +// } +// } + +// class DottedShimmerPainter extends CustomPainter { +// final Gradient gradient; +// final double spacing; + +// DottedShimmerPainter({required this.gradient, required this.spacing}); + +// @override +// void paint(Canvas canvas, Size size) { +// final paint = Paint() +// ..style = PaintingStyle.fill +// ..style = PaintingStyle.fill +// ..strokeWidth = 2; + +// final dots = List.generate( +// (size.width / spacing).floor() + 1, +// (int index) => Offset(index * spacing, size.height / 2), +// ); +// } + +// @override +// bool shouldRepaint(CustomPainter oldDelegate) => true; +// } + +// class DemoRendererr extends RenderProxyBox { +// Gradient _gradient; +// double _percent; +// @override +// bool get alwaysNeedsCompositing => child != null; + +// set percent(double newValue) { +// if (newValue == _percent) { +// return; +// } +// _percent = newValue; +// markNeedsPaint(); +// } + +// set gradient(Gradient newValue) { +// if (newValue == _gradient) { +// return; +// } +// _gradient = newValue; +// markNeedsPaint(); +// } + +// DemoRendererr( +// this._gradient, +// this._percent, +// ); + +// @override +// ShaderMaskLayer? get layer => super.layer as ShaderMaskLayer?; + +// @override +// void paint(PaintingContext context, Offset offset) { +// // final painter = DottedShimmerPainter( + +// // spacing: 3, +// // ); +// // painter.paint(context.canvas, size); +// } +// } + +// // class DottedShimmer extends StatelessWidget { +// // final Color color; +// // final double spacing; + +// // DottedShimmer({this.color = Colors.white, this.spacing = 10}); + +// // @override +// // Widget build(BuildContext context) { +// // // return RenderProxyBox( + +// // // child: Container(), +// // // onPaint: (context, offset) { +// // // final painter = DottedShimmerPainter( +// // // color: color, +// // // spacing: spacing, +// // // ); +// // // final painterSize = painter.paint(context, offset); +// // // return +// // // }, +// // // ); +// // } +// // } + +// class MyShimmer extends SingleChildRenderObjectWidget { +// final double percent; +// final Gradient gradient; + +// const MyShimmer({ +// Widget? child, +// required this.percent, +// required this.gradient, +// }) : super(child: child); + +// @override +// DemoRendererr createRenderObject(BuildContext context) { +// return DemoRendererr(gradient, percent); +// } + +// @override +// updateRenderObject(context, DemoRendererr obj) {} +// } + +// /// +// /// * author: hunghd +// /// * email: hunghd.yb@gmail.com +// /// +// /// A package provides an easy way to add shimmer effect to Flutter application +// /// + +// @immutable +// class Shimmer extends StatefulWidget { +// final Widget child; +// final Duration period; +// final ShimmerDirection direction; +// final Gradient gradient; +// final int loop; +// final bool enabled; + +// const Shimmer({ +// Key? key, +// required this.child, +// required this.gradient, +// this.direction = ShimmerDirection.ltr, +// this.period = const Duration(milliseconds: 1500), +// this.loop = 0, +// this.enabled = true, +// }) : super(key: key); + +// /// +// /// A convenient constructor provides an easy and convenient way to create a +// /// [Shimmer] which [gradient] is [LinearGradient] made up of `baseColor` and +// /// `highlightColor`. +// /// +// Shimmer.fromColors({ +// Key? key, +// required this.child, +// required Color baseColor, +// required Color highlightColor, +// this.period = const Duration(milliseconds: 1500), +// this.direction = ShimmerDirection.ltr, +// this.loop = 0, +// this.enabled = true, +// }) : gradient = LinearGradient( +// begin: Alignment.topLeft, +// end: Alignment.centerRight, +// colors: [ +// baseColor, +// baseColor, +// highlightColor, +// baseColor, +// baseColor +// ], +// stops: const [ +// 0.0, +// 0.35, +// 0.5, +// 0.65, +// 1.0 +// ]), +// super(key: key); + +// @override +// _ShimmerState createState() => _ShimmerState(); + +// @override +// void debugFillProperties(DiagnosticPropertiesBuilder properties) { +// super.debugFillProperties(properties); +// properties.add(DiagnosticsProperty('gradient', gradient, +// defaultValue: null)); +// properties.add(EnumProperty('direction', direction)); +// properties.add( +// DiagnosticsProperty('period', period, defaultValue: null)); +// properties +// .add(DiagnosticsProperty('enabled', enabled, defaultValue: null)); +// properties.add(DiagnosticsProperty('loop', loop, defaultValue: 0)); +// } +// } + +// class _ShimmerState extends State with SingleTickerProviderStateMixin { +// late AnimationController _controller; +// int _count = 0; + +// @override +// void initState() { +// super.initState(); +// _controller = AnimationController(vsync: this, duration: widget.period) +// ..addStatusListener((AnimationStatus status) { +// if (status != AnimationStatus.completed) { +// return; +// } +// _count++; +// if (widget.loop <= 0) { +// _controller.repeat(); +// } else if (_count < widget.loop) { +// _controller.forward(from: 0.0); +// } +// }); +// if (widget.enabled) { +// _controller.forward(); +// } +// } + +// @override +// void didUpdateWidget(Shimmer oldWidget) { +// if (widget.enabled) { +// _controller.forward(); +// } else { +// _controller.stop(); +// } +// super.didUpdateWidget(oldWidget); +// } + +// @override +// Widget build(BuildContext context) { +// return AnimatedBuilder( +// animation: _controller, +// child: widget.child, +// builder: (BuildContext context, Widget? child) => _Shimmer( +// direction: widget.direction, +// gradient: widget.gradient, +// percent: _controller.value, +// child: child, +// ), +// ); +// } + +// @override +// void dispose() { +// _controller.dispose(); +// super.dispose(); +// } +// } + +// @immutable +// class _Shimmer extends SingleChildRenderObjectWidget { +// final double percent; +// final ShimmerDirection direction; +// final Gradient gradient; + +// const _Shimmer({ +// Widget? child, +// required this.percent, +// required this.direction, +// required this.gradient, +// }) : super(child: child); + +// @override +// _ShimmerFilter createRenderObject(BuildContext context) { +// return _ShimmerFilter(percent, direction, gradient); +// } + +// @override +// void updateRenderObject(BuildContext context, _ShimmerFilter shimmer) { +// shimmer.percent = percent; +// shimmer.gradient = gradient; +// shimmer.direction = direction; +// } +// } + +// class _ShimmerFilter extends RenderProxyBox { +// ShimmerDirection _direction; +// Gradient _gradient; +// double _percent; + +// _ShimmerFilter(this._percent, this._direction, this._gradient); + +// @override +// ShaderMaskLayer? get layer => super.layer as ShaderMaskLayer?; + +// @override +// bool get alwaysNeedsCompositing => child != null; + +// set percent(double newValue) { +// if (newValue == _percent) { +// return; +// } +// _percent = newValue; +// markNeedsPaint(); +// } + +// set gradient(Gradient newValue) { +// if (newValue == _gradient) { +// return; +// } +// _gradient = newValue; +// markNeedsPaint(); +// } + +// set direction(ShimmerDirection newDirection) { +// if (newDirection == _direction) { +// return; +// } +// _direction = newDirection; +// markNeedsLayout(); +// } + +// @override +// void paint(PaintingContext context, Offset offset) { +// if (child != null) { +// assert(needsCompositing); + +// final double width = child!.size.width; +// final double height = child!.size.height; +// Rect rect; +// double dx, dy; +// if (_direction == ShimmerDirection.rtl) { +// dx = _offset(width, -width, _percent); +// dy = 0.0; +// rect = Rect.fromLTWH(dx - width, dy, 3 * width, height); +// } else if (_direction == ShimmerDirection.ttb) { +// dx = 0.0; +// dy = _offset(-height, height, _percent); +// rect = Rect.fromLTWH(dx, dy - height, width, 3 * height); +// } else if (_direction == ShimmerDirection.btt) { +// dx = 0.0; +// dy = _offset(height, -height, _percent); +// rect = Rect.fromLTWH(dx, dy - height, width, 3 * height); +// } else { +// dx = _offset(-width, width, _percent); +// dy = 0.0; +// rect = Rect.fromLTWH(dx - width, dy, 3 * width, height); +// } + +// var dottedShimmerPainter = +// DottedShimmerPainter(gradient: _gradient, spacing: 4); +// dottedShimmerPainter.paint(context.canvas, size); +// // layer ??= ShaderMaskLayer(); +// // layer! +// // ..shader = _gradient.createShader(rect) +// // ..maskRect = offset & size +// // ..blendMode = BlendMode.srcIn; + +// // context.pushLayer(layer!, super.paint, offset); +// } else { +// layer = null; +// } +// } + +// double _offset(double start, double end, double percent) { +// return start + (end - start) * percent; +// } +// } diff --git a/lib/sandBox/language_overlay_changer.dart b/lib/sandBox/language_overlay_changer.dart new file mode 100644 index 0000000..5da8019 --- /dev/null +++ b/lib/sandBox/language_overlay_changer.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +class LanguageOverlay { + Future addTool(context) async { + OverlayState? overlayState = Overlay.of(context); + OverlayEntry overlayEntry; + overlayEntry = OverlayEntry(builder: (context) { + return const LanguageOverlayChanger(); + }); + SchedulerBinding.instance.addPostFrameCallback((timeStamp) { + overlayState.insert(overlayEntry); + }); + // await Future.delayed(duration ?? const Duration(seconds: 3)); + + // overlayEntry.remove(); + // onMessageClosed?.call(); + } +} + +class LanguageOverlayChanger extends StatefulWidget { + const LanguageOverlayChanger({super.key}); + + @override + State createState() => LanguageOverlayChangerState(); +} + +class LanguageOverlayChangerState extends State { + double x = 50; + double y = 50; + + @override + Widget build(BuildContext context) { + return Positioned( + top: y, + left: x, + child: Material( + textStyle: const TextStyle(decoration: TextDecoration.none), + child: Draggable( + onDragUpdate: (d) { + x = d.globalPosition.dx; + y = d.globalPosition.dy; + setState(() {}); + }, + feedbackOffset: const Offset(-1, 0), + feedback: Container(), + child: InkWell( + onTap: () { + // if (context.read().currentLanguageCode() == "en") { + // context.read().changeLanguage("ar"); + // } else { + // context.read().changeLanguage("en"); + // } + }, + child: Container( + height: 50, + color: const Color.fromARGB(255, 85, 40, 65), + child: const Center( + child: Padding( + padding: EdgeInsets.all(8.0), + child: Text( + "Change language", + style: TextStyle(color: Colors.white), + ), + )), + ), + ), + ), + ), + ); + } +} diff --git a/lib/sandBox/playground.dart b/lib/sandBox/playground.dart new file mode 100644 index 0000000..d812cc0 --- /dev/null +++ b/lib/sandBox/playground.dart @@ -0,0 +1,77 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:flutter/material.dart'; + +import '../Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; + +class PlayGround extends StatefulWidget { + const PlayGround({super.key}); + + static Route route(RouteSettings routeSettings) { + return BlurredRouter( + builder: (_) => const PlayGround(), + ); + } + + @override + State createState() => _PlayGroundState(); +} + +class _PlayGroundState extends State { + // Completer completer = Completer(); + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + body: Center( + child: STFLW(), + ), + ), + ); + } +} + +class STFLW extends StatefulWidget { + const STFLW({super.key}); + + @override + State createState() => _STFLWState(); +} + +class _STFLWState extends State { + @override + Widget build(BuildContext context) { + return ClipPath( + clipper: Hexagon(), + child: Container( + width: 200, + height: 200, + color: Colors.blue, + ), + ); + } +} + +class Hexagon extends CustomClipper { + @override + Path getClip(Size size) { + Path path = Path(); + double spaceFactor = size.width * 00.15; + path.moveTo(spaceFactor, 0); + + // Timer.periodic(Duration(seconds: 2), (timer) { + // print("HEHE"); + // path.lineTo(size.width / (size.width / Random().nextInt(50)), + // size.height / (size.height / Random().nextInt(50))); + // }); + + path.close(); + // path.lineTo(, y); + + return path; + } + + @override + bool shouldReclip(covariant CustomClipper oldClipper) { + return true; + } +} diff --git a/lib/sandBox/section.dart b/lib/sandBox/section.dart new file mode 100644 index 0000000..6fb23e9 --- /dev/null +++ b/lib/sandBox/section.dart @@ -0,0 +1,54 @@ +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; + +import '../Ui/screens/proprties/viewAll.dart'; + +enum SectionStyle { BigCard, HorizontalCard, GradientCard, Grid } + +abstract class Section { + late BuildContext context; + abstract T s; + abstract bool seeAll; + String get sectionTitle; + abstract StateMap stateMap; + abstract SectionStyle style; + + Widget render(); +} + +class FeaturedSection extends Section { + @override + Widget render() { + return Container(); + } + + @override + String get sectionTitle => "h".translate(context); + + @override + bool seeAll = true; + + @override + StateMap stateMap = StateMap(); + + @override + SectionStyle style = SectionStyle.BigCard; + + @override + int s = 4; +} + +class RenderSection extends StatefulWidget { + final Section section; + const RenderSection({super.key, required this.section}); + + @override + State createState() => _RenderSectionState(); +} + +class _RenderSectionState extends State { + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} diff --git a/lib/sandBox/theme_overlay_toggler.dart b/lib/sandBox/theme_overlay_toggler.dart new file mode 100644 index 0000000..7a0867c --- /dev/null +++ b/lib/sandBox/theme_overlay_toggler.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../data/cubits/system/app_theme_cubit.dart'; + +class ThemeToggler { + void addTool(context) async { + OverlayState? overlayState = Overlay.of(context); + OverlayEntry overlayEntry; + overlayEntry = OverlayEntry(builder: (context) { + return const ThemeOverlayToggler(); + }); + SchedulerBinding.instance.addPostFrameCallback((timeStamp) { + overlayState.insert(overlayEntry); + }); + } +} + +class ThemeOverlayToggler extends StatefulWidget { + const ThemeOverlayToggler({super.key}); + + @override + State createState() => ThemeOverlayTogglerState(); +} + +class ThemeOverlayTogglerState extends State { + double x = 50; + double y = 50; + + @override + Widget build(BuildContext context) { + return Positioned( + top: y, + left: x, + child: Material( + textStyle: const TextStyle(decoration: TextDecoration.none), + child: Draggable( + onDragUpdate: (d) { + x = d.globalPosition.dx; + y = d.globalPosition.dy; + setState(() {}); + }, + feedbackOffset: const Offset(-1, 0), + feedback: Container(), + child: InkWell( + onTap: () { + context.read().toggleTheme(); + }, + child: Container( + height: 50, + color: const Color.fromARGB(255, 85, 40, 65), + child: const Center( + child: Padding( + padding: EdgeInsets.all(8.0), + child: Text( + "Change Theme", + style: TextStyle(color: Colors.white), + ), + )), + ), + ), + ), + ), + ); + } +} diff --git a/lib/settings.dart b/lib/settings.dart new file mode 100644 index 0000000..63ceb5e --- /dev/null +++ b/lib/settings.dart @@ -0,0 +1,138 @@ +import 'package:ebroker/utils/helper_utils.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +///eBroker configuration file +/// Configure your app from here +/// Most of basic configuration will be from here +/// For theme colors go to [lib/Ui/Theme/theme.dart] +class AppSettings { + ///Basic Settings + static const String applicationName = 'Rumahjo'; + static const String androidPackageName = 'com.rumahjo.vds'; + static const String iOSAppId = '12345678'; + static const String playstoreURLAndroid = + "https://play.google.com/store/apps/details?id=$androidPackageName"; + static const String appstoreURLios = "https://apps.apple.com/app/$iOSAppId"; + static const String shareAppText = "Share this App"; + + ///API Setting + static const String hostUrl = "https://backoffice.rumahjo.com/"; + static const String apiUrl = "https://app.rumahjo.com/"; + + static const int apiDataLoadLimit = 20; + static const int maxCategoryShowLengthInHomeScreen = 5; + static String appNumber = ''; + + static final String baseUrl = + "${HelperUtils.checkHost(hostUrl)}api/"; //Don't change this + + static const int hiddenAPIProcessDelay = + 1; /* this is for load data when open app if old data is already available so +it will call API in background without showing the process and when data available it will replace it with new data */ + + ///Set type here + static const DeepLinkType deepLinkingType = DeepLinkType.native; + + ///Native deep link + static const String shareNavigationWebUrl = "YOUR WEB VERSION URL"; + + /// + + //TODO: Deprecated [We do not recommend using this as this will stop running in few time] + /// You will find this prefix from firebase console in dynamic link section + static const String deepLinkPrefix = + "https://rumahjo.page.link"; //demo.page.link + ///set anything you want + static const String deepLinkName = "rumahjo"; //deeplink demo.com + ///!TODO: End deprecated + + static const MapType googleMapType = + MapType.normal; //none , normal , satellite , terrain , hybrid + + ///Firebase authentication OTP timer. + static const int otpResendSecond = 60 * 2; + static const int otpTimeOutSecond = 60 * 2; + + ///This code will show on login screen [Note: don't add + symbol] + static const String defaultCountryCode = "62"; + static const bool disableCountrySelection = + false; /* Default [False], this will hide + Country number choose option in login screen. if your App is for only one country this might be helpful*/ + + static List sections = [ + HomeScreenSections.Search, + HomeScreenSections.Slider, + HomeScreenSections.Category, + HomeScreenSections.NearbyProperties, + HomeScreenSections.FeaturedProperties, + HomeScreenSections.PersonalizedFeed, + HomeScreenSections.project, + HomeScreenSections.RecentlyAdded, + HomeScreenSections.MostLikedProperties, + HomeScreenSections.MostViewed, + HomeScreenSections.PopularCities + ]; //[Note: We Recommend default setting you can make arrangement by your choice or you can hide any section if you do not want] + + ///Lottie animation + ///Put your loading json file in [lib/assets/lottie/] folder + static const String progressLottieFile = "loading.json"; + static const String progressLottieFileWhite = + "loading_white.json"; //When there is dark background and you want to show progress so it will be used + + static const String maintenanceModeLottieFile = "maintenancemode.json"; + + static const bool useLottieProgress = + true; //if you don't want to use lottie progress then set it to false' + + ///Other settings + static const String notificationChannel = "basic_channel"; // + static int uploadImageQuality = 50; //0 to 100th + static const Set additionalRTLlanguages = + {}; //Add language code in bracket {"ab","bc"} + +//Advance settings +//This file is located in assets/riveAnimations + static const String riveAnimationFile = "rive_animation.riv"; + + static const Map riveAnimationConfigurations = { + "add_button": { + "artboard_name": "Add", + "state_machine": "click", + "boolean_name": "isReverse", + "boolean_initial_value": true, + "add_button_shape_name": "shape", + }, + }; + + //// Don't change these + //// Payment gatway API keys + ///Here is for only reference you have to change it from panel + static String enabledPaymentGatway = ""; + static String razorpayKey = ""; + static String paystackKey = ""; // public key + static String paystackCurrency = ""; + static String paypalClientId = ""; + static String paypalServerKey = ""; //secrete + static bool isSandBoxMode = true; //testing mode + static String paypalCancelURL = ""; + static String paypalReturnURL = ""; + static String stripeCurrency = ""; + static String stripePublishableKey = ""; + static String stripeSecrateKey = ""; +} + +enum HomeScreenSections { + Search, + Slider, + PersonalizedFeed, + NearbyProperties, + FeaturedProperties, + RecentlyAdded, + MostLikedProperties, + PopularCities, + MostViewed, + Category, + project +} + +enum DeepLinkType { firebase, native } diff --git a/lib/utils/.DS_Store b/lib/utils/.DS_Store new file mode 100644 index 0000000..0ebceed Binary files /dev/null and b/lib/utils/.DS_Store differ diff --git a/lib/utils/AdMob/bannerAdLoadWidget.dart b/lib/utils/AdMob/bannerAdLoadWidget.dart new file mode 100644 index 0000000..9b543ef --- /dev/null +++ b/lib/utils/AdMob/bannerAdLoadWidget.dart @@ -0,0 +1,73 @@ +import 'dart:io'; + +import 'package:ebroker/exports/main_export.dart'; +import 'package:flutter/material.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; + +class BannerAdWidget extends StatefulWidget { + final AdSize bannerSize; + + const BannerAdWidget({Key? key, this.bannerSize = AdSize.largeBanner}) + : super(key: key); + + @override + State createState() => _BannerAdWidgetState(); +} + +class _BannerAdWidgetState extends State { + BannerAd? _bannerAd; + late String adUnitId; + + /// Loads a banner ad. + void loadAd() { + if (Constant.isAdmobAdsEnabled == false) { + return; + } + _bannerAd = BannerAd( + adUnitId: adUnitId, + request: const AdRequest(), + size: widget.bannerSize, + listener: BannerAdListener( + onAdLoaded: (ad) { + debugPrint('$ad loaded.'); + setState(() {}); + }, + // Called when an ad request failed. + onAdFailedToLoad: (ad, err) { + _bannerAd = null; + setState(() {}); + + ad.dispose(); + }, + ), + )..load(); + } + + @override + void initState() { + adUnitId = Platform.isAndroid + ? Constant.admobBannerAndroid + : Constant.admobBannerIos; + loadAd(); + super.initState(); + } + + @override + void dispose() { + if (_bannerAd != null) { + _bannerAd!.dispose(); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return (_bannerAd != null) + ? SizedBox( + width: _bannerAd!.size.width.toDouble(), + height: _bannerAd!.size.height.toDouble(), + child: AdWidget(ad: _bannerAd!), + ) + : const SizedBox.shrink(); + } +} diff --git a/lib/utils/AdMob/interstitialAdManager.dart b/lib/utils/AdMob/interstitialAdManager.dart new file mode 100644 index 0000000..d31e3ee --- /dev/null +++ b/lib/utils/AdMob/interstitialAdManager.dart @@ -0,0 +1,111 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +// class InterstitialAdManager { +// InterstitialAd? _interstitialAd; +// +// void load({VoidCallback? onAdLoad}) { +// InterstitialAd.load( +// adUnitId: "", +// request: const AdRequest(), +// adLoadCallback: InterstitialAdLoadCallback( +// // Called when an ad is successfully received. +// onAdLoaded: (InterstitialAd ad) { +// log('$ad loaded.'); +// onAdLoad?.call(); +// // Keep a reference to the ad so you can show it later. +// _interstitialAd = ad; +// }, +// // Called when an ad request failed. +// onAdFailedToLoad: (LoadAdError error) { +// log('InterstitialAd failed to load: $error'); +// }, +// )); +// } +// +// Future show() async { +// if (_interstitialAd != null) { +// await _interstitialAd!.show(); +// } +// } +// } +// import 'package:flutter/foundation.dart'; + +// class InterstitialAdManager { +// InterstitialAd? _interstitialAd; +// int _adCount = 0; +// +// void load({VoidCallback? onAdLoad}) { +// InterstitialAd.load( +// adUnitId: "", +// request: const AdRequest(), +// adLoadCallback: InterstitialAdLoadCallback( +// onAdLoaded: (InterstitialAd ad) { +// print('$ad loaded.'); +// onAdLoad?.call(); +// _interstitialAd = ad; +// }, +// onAdFailedToLoad: (LoadAdError error) { +// print('InterstitialAd failed to load: $error'); +// }, +// ), +// ); +// } +// +// Future show() async { +// if (_interstitialAd != null) { +// log("SHOWING HEHE"); +// await _interstitialAd!.show(); +// _adCount++; +// if (_adCount == 4) { +// _adCount = 0; // Reset the count after showing the ad +// } +// } +// } +// } +import 'package:google_mobile_ads/google_mobile_ads.dart'; + +import '../constant.dart'; + +class InterstitialAdManager { + static int _adCount = 0; + static InterstitialAd? _interstitialAd; + + void load({VoidCallback? onAdLoad}) { + if (Constant.isAdmobAdsEnabled == false) { + return; + } + + InterstitialAd.load( + adUnitId: (Platform.isAndroid + ? Constant.admobInterstitialAndroid + : Constant.admobInterstitialIos), + request: const AdRequest(), + adLoadCallback: InterstitialAdLoadCallback( + onAdLoaded: (InterstitialAd ad) { + print('$ad loaded.'); + onAdLoad?.call(); + _interstitialAd = ad; + }, + onAdFailedToLoad: (LoadAdError error) { + print('InterstitialAd failed to load: $error'); + }, + ), + ); + } + + Future show() async { + if (Constant.isAdmobAdsEnabled == false) { + return; + } + if (_interstitialAd != null) { + _adCount++; + if (_adCount == 4) { + await _interstitialAd!.show(); + + _adCount = 0; // Reset the count after showing the ad + } + } + } +} diff --git a/lib/utils/AppIcon.dart b/lib/utils/AppIcon.dart new file mode 100644 index 0000000..5a5a8d1 --- /dev/null +++ b/lib/utils/AppIcon.dart @@ -0,0 +1,99 @@ +// ignore_for_file: non_constant_identifier_names, file_names + +class AppIcons { + static String placeHolder = ""; + + static var ads = ""; + + static var propertyLimites = ""; + + AppIcons._(); + // + static const String _basePath = "assets/svg/"; + //** */ + static String magic = _svgPath("magic"); + static String bin = _svgPath("bin"); + static String chat = _svgPath("inactive_chat"); + static String update = _svgPath("update"); + static String companyLogo = _svgPath("Logo/company_logo"); + static String home = _svgPath("home"); + static String profile = _svgPath("profile"); + static String search = _svgPath("search"); + static String properties = _svgPath("properties"); + static String iconArrowLeft = _svgPath("icon_arrow_left"); + static String filter = _svgPath("filter"); + static String location = _svgPath("location"); + static String downArrow = _svgPath("down_arrow"); + static String arrowRight = _svgPath("arrow_right"); + static String like = _svgPath("like"); + static String like_fill = _svgPath("like_fill"); + static String notification = _svgPath("notification"); + static String language = _svgPath("language"); + static String darkTheme = _svgPath("dark_theme"); + static String subscription = _svgPath("subscription"); + static String articles = _svgPath("article"); + static String favorites = _svgPath("like_fill"); + static String shareApp = _svgPath("share"); + static String areaConvertor = _svgPath("area_convertor"); + static String rateUs = _svgPath("rate_us"); + static String contactUs = _svgPath("contact_us"); + static String aboutUs = _svgPath("about_us"); + static String terms = _svgPath("t_c"); + static String privacy = _svgPath("privacypolicy"); + static String delete = _svgPath("delete_account"); + static String logout = _svgPath("logout"); + static String edit = _svgPath("edit"); + static String call = _svgPath("call"); + static String message = _svgPath("message"); + static String defaultPersonLogo = _svgPath("defaultProfileIcon"); + static String arrowLeft = _svgPath("arrow_left"); + static String warning = _svgPath("warning"); + static String promoted = _svgPath("promoted"); + static String headerCurve = _svgPath("header_curve"); + static String v360Degree = _svgPath("v360"); + static String deleteGirlSvg = _svgPath("delete"); + static String forRent = _svgPath("for_rent"); + static String forSale = _svgPath("for_sale"); + static String propertyMap = _svgPath("propertymap"); + static String calender = _svgPath("calender"); + static String interested = _svgPath("interested"); + static String whatsapp = _svgPath("whatsapp"); + static String somethingwentwrong = + _svgPath("MultiColorSvg/something_went_wrong"); + + static String paystack = _svgPath("paystack"); + static String razorpay = _svgPath("razorpay"); + static String paypal = _svgPath("paypal"); + static String transaction = _svgPath("transaction"); + static String reportDark = _svgPath("report_dark"); + static String report = _svgPath("report"); + static String propertySubmittedc = _svgPath("MultiColorSvg/propertysubmited"); + static String no_chat_found = _svgPath("MultiColorSvg/no_chat_found"); + static String no_data_found = + _svgPath("MultiColorSvg/no_data_found_illustrator"); + static String days = _svgPath("days"); + + /// + static String propertiesIcon = _svgPath("properties_icon"); + static String upcomingProject = _svgPath("upcoming_projects_icon"); + + ///Fallback icons + static String fallbackSplashLogo = _svgPath("Fallback/splash"); + static String fallbackPlaceholderLogo = _svgPath("Fallback/placeholder"); + static String fallbackHomeLogo = _svgPath("Fallback/homeLogo"); + + static String no_internet = _svgPath("MultiColorSvg/no_internet_illustrator"); + static String deleteIcon = _svgPath("MultiColorSvg/delete_illustrator"); + static String logoutIcon = _svgPath("MultiColorSvg/logout_illustrator"); + + //on boardings + static String onBoardingsOne = _svgPath("onbo_a"); + static String onBoardingsTwo = _svgPath("onbo_b"); + static String onBoardingsThree = _svgPath("onbo_c"); + + /// + /// + static String _svgPath(String name) { + return "$_basePath$name.svg"; + } +} diff --git a/lib/utils/CloudState/cloud_state.dart b/lib/utils/CloudState/cloud_state.dart new file mode 100644 index 0000000..8ae7dac --- /dev/null +++ b/lib/utils/CloudState/cloud_state.dart @@ -0,0 +1,185 @@ +import 'package:flutter/material.dart'; + +// Abstract class for managing cloud data state +abstract class CloudState extends State { + // Static map to store cloud data shared across instances + static Map cloudData = {}; + + // Method to get all cloud data + Map getCloudDataAll() { + return cloudData; + } + + // Global single listener for item addition + static void Function(String key, dynamic value)? onItemAdd; + static List? _listeners = []; + + // Method to add a listener for a specific key + listenOn(String key, Function(dynamic value) callBack) { + _listeners?.add((String addedKey, dynamic addedValue) { + if (key == addedKey) { + callBack.call(addedValue); + } else if (key == "*") { + callBack.call({addedKey: addedValue}); + } + }); + } + + // Method to notify all listeners about changes + void notify(String key, dynamic value) { + _listeners?.forEach((element) { + element.call(key, value); + }); + } + + // Method to get cloud data for a specific key + getCloudData(String key) { + return cloudData[key]; + } + + // Method to add cloud data and notify listeners + void addCloudData(String key, dynamic value) { + cloudData.addAll(Map.from({key: value})); + notify(key, value); + } + + void insertCloudData(String key, dynamic value) { + if (!cloudData.containsKey(key)) { + cloudData[key] = {}; + } + if (cloudData[key] is Map) { + cloudData[key].addAll(Map.from({key: value})); + } + + notify(key, value); + } + + // Method to add screen-specific data and notify listeners + void addScreenValue(String key, dynamic value) { + cloudData.addAll({key: value}); + notify(key, value); + } + + // Method to set cloud data for a specific key and notify listeners + void setCloudData(String key, dynamic value) { + cloudData[key] = value; + notify(key, value); + } + + // Method to append a value to a list in cloud data and notify listeners + void appendToList(String key, T value, {bool? disableClone}) { + if (!cloudData.containsKey(key)) { + cloudData[key] = [value]; + } + if (cloudData[key] is List) { + if (disableClone == true) { + if (!(cloudData[key] as List).contains(value)) { + (cloudData[key] as List).add(value); + notify(key, value); + } + } else { + (cloudData[key] as List).add(value); + notify(key, value); + } + } + } + + void appendToListWhere({ + required String listKey, + required String whereKey, + required T equals, + required Map add, + bool? disableClone, + }) { + cloudData.putIfAbsent(listKey, () => [add]); + + if (cloudData[listKey] is List>) { + var list = cloudData[listKey] as List>; + + if (disableClone != true || + !list.any((item) => item[whereKey] == equals)) { + int indexWhere = + list.indexWhere((element) => element[whereKey] == equals); + if (indexWhere >= 0) { + list[indexWhere] = add; + } else { + list.add(add); + notify(listKey, add); + } + } + } + } + + void removeFromListWhere({ + required String listKey, + required String whereKey, + required T equals, + }) { + print("list key ${whereKey} ex $equals"); + + if (cloudData.containsKey(listKey) && + cloudData[listKey] is List>) { + var list = cloudData[listKey] as List>; + int indexWhere = + list.indexWhere((element) => element[whereKey] == equals); + print("list key ${list} $indexWhere"); + + if (indexWhere >= 0) { + var removedItem = list.removeAt(indexWhere); + notify(listKey, removedItem); + } + } + } + + CloudState toGroup(String groupName, dynamic key, dynamic value) { + // Check if the group exists in cloudData + if (!cloudData.containsKey(groupName)) { + cloudData[groupName] = {}; // Initialize as an empty map if not present + } + + // Now you can safely access and update the group's key-value pair + cloudData[groupName][key] = value; + return this; + } + + void removeFromGroup(String groupName, dynamic key) { + if (cloudData.containsKey(groupName)) { + if (cloudData[groupName].containsKey(key)) { + cloudData[groupName].remove(key); + } + } + } + + void clearGroup(String name) { + if (cloudData.containsKey(name)) { + cloudData.remove(name); + } + } + + dynamic fromGroup(String groupName, dynamic key) { + return cloudData[groupName][key]; + } + + Map? group(String groupName) { + return cloudData[groupName] as Map?; + } + + // Method to add screen-specific data in the cloud data + void screenData(String key, dynamic value) { + if (cloudData.containsKey(runtimeType)) { + (cloudData[runtimeType] as Map).addAll({key: value}); + } else { + cloudData[runtimeType] = {}; + (cloudData[runtimeType] as Map).addAll({key: value}); + } + } + + // Method to get screen-specific data from the cloud data + dynamic getScreenData(State screen, String key) { + return cloudData[screen][key] ?? {}; + } + + // Override build method, as it's an abstract class + @override + Widget build(BuildContext context); +} diff --git a/lib/utils/CurrencyConvertor/currentyConvertor.dart b/lib/utils/CurrencyConvertor/currentyConvertor.dart new file mode 100644 index 0000000..2b039e8 --- /dev/null +++ b/lib/utils/CurrencyConvertor/currentyConvertor.dart @@ -0,0 +1,36 @@ +// import 'dart:async'; +// +// abstract class CurrencyConvertorService { +// abstract String baseCurrency; +// +// FutureOr> execute(); +// } +// +// class Currency { +// final String name; +// final double rate; +// +// Currency(this.name, this.rate); +// } +// +// class ExchangeRatesApi implements CurrencyConvertorService { +// @override +// late String baseCurrency; +// @override +// FutureOr> execute() async {} +// } +// +// class CurrencyRateProvider { +// final CurrencyConvertorService service; +// +// CurrencyRateProvider(this.service, {required this.baseCurrency}); +// +// final String baseCurrency; +// +// Future> getExchangeRates() async { +// service.baseCurrency = this.baseCurrency; +// List respnse = await service.execute(); +// +// return respnse; +// } +// } diff --git a/lib/utils/DeepLink/blueprint.dart b/lib/utils/DeepLink/blueprint.dart new file mode 100644 index 0000000..b1389b4 --- /dev/null +++ b/lib/utils/DeepLink/blueprint.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +import 'nativeDeepLinkManager.dart'; + +abstract class NativeDeepLinkUtility { + void handle(Uri uri, ProcessResult? result); + Future handleLink(String url) async { + Uri parse = Uri.parse(url); + + NativeDeepLinkManager nativeDeepLinkManager = NativeDeepLinkManager(); + ProcessResult? processResult = await nativeDeepLinkManager.process(parse); + nativeDeepLinkManager.handle(parse, processResult); + } + + MaterialPageRoute build(RouteSettings settings) { + return MaterialPageRoute( + builder: (context) { + return NativeLinkWidget( + settings: settings, + ); + }, + ); + } + + Future process(Uri uri); +} + +class ProcessResult { + final T result; + ProcessResult(this.result); +} diff --git a/lib/utils/DeepLink/nativeDeepLinkManager.dart b/lib/utils/DeepLink/nativeDeepLinkManager.dart new file mode 100644 index 0000000..861926a --- /dev/null +++ b/lib/utils/DeepLink/nativeDeepLinkManager.dart @@ -0,0 +1,105 @@ +import 'package:ebroker/data/Repositories/articles_repository.dart'; +import 'package:ebroker/data/Repositories/property_repository.dart'; +import 'package:ebroker/data/model/article_model.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:ebroker/utils/DeepLink/blueprint.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; +import 'package:flutter/material.dart'; + +import '../../Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart'; +import '../../app/routes.dart'; +import '../constant.dart'; + +class NativeDeepLinkManager extends NativeDeepLinkUtility { + @override + void handle(Uri uri, ProcessResult? result) { + if (uri.toString().startsWith("http") || + uri.toString().startsWith("https")) { + if (result?.result is PropertyModel) { + Navigator.pushReplacementNamed( + Constant.navigatorKey.currentContext!, Routes.propertyDetails, + arguments: { + 'propertyData': result?.result as PropertyModel, + 'propertiesList': [] + }); + } + + if (result?.result is ArticleModel) { + Navigator.pushReplacementNamed( + Constant.navigatorKey.currentContext!, + Routes.articleDetailsScreenRoute, + arguments: { + "model": result?.result, + }, + ); + } + } + } + + @override + Future process(Uri uri) async { + if (uri.pathSegments.contains("properties-details")) { + String slug = uri.pathSegments[1]; + PropertyModel propertyModel = + await PropertyRepository().fetchBySlug(slug); + return ProcessResult(propertyModel); + } + if (uri.pathSegments.contains("article-details")) { + String slug = uri.pathSegments[1]; + ArticleModel articleModel = + await ArticlesRepository().fetchArticlesBySlugId(slug); + + return ProcessResult((articleModel)); + } + + return null; + } +} + +class NativeLinkWidget extends StatefulWidget { + final RouteSettings settings; + const NativeLinkWidget({super.key, required this.settings}); + static BlurredRouter render(RouteSettings settings) { + return BlurredRouter( + builder: (context) { + return Scaffold( + body: NativeLinkWidget( + settings: settings, + ), + ); + }, + ); + } + + @override + State createState() => _NativeLinkWidgetState(); +} + +class _NativeLinkWidgetState extends State { + @override + void initState() { + super.initState(); + + NativeDeepLinkManager().handleLink(widget.settings.name ?? ""); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator( + color: Theme.of(context).colorScheme.tertiaryColor, + ), + const SizedBox( + height: 15, + ), + const Text("Please Wait...") + ], + ), + ), + ); + } +} diff --git a/lib/utils/Encryption/rsa.dart b/lib/utils/Encryption/rsa.dart new file mode 100644 index 0000000..b20f219 --- /dev/null +++ b/lib/utils/Encryption/rsa.dart @@ -0,0 +1,38 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:encrypt/encrypt.dart'; +import 'package:pointycastle/asymmetric/api.dart' as d; + +class RSAEncryption { + final parser = RSAKeyParser(); + //encrypted data must be in base 64 + String decrypt({required String privateKey, required String encryptedData}) { + try { + Uint8List encryptedData_ = + Uint8List.fromList(base64Decode(encryptedData)); + // Parse the private key from the PEM format + final __privateKey__ = parser.parse(privateKey) as d.RSAPrivateKey; + + // Create an RSA decrypter with the private key + final decrypter = Encrypter(RSA(privateKey: __privateKey__)); + + // Decrypt the data + final decryptedData = decrypter.decryptBytes(Encrypted(encryptedData_)); + + // Convert the decrypted data to a string + String decryptedText = utf8.decode(decryptedData); + + return decryptedText; + } catch (e) { + throw ("Decrypt failed:$e"); + } + } + + String encrypt({required String data, required String publicKey}) { + d.RSAPublicKey publicKey_ = parser.parse(publicKey) as d.RSAPublicKey; + final encrypter = Encrypter(RSA(publicKey: publicKey_)); + final encrypted = encrypter.encrypt(data); + return encrypted.base64; + } +} diff --git a/lib/utils/Extensions/extensions.dart b/lib/utils/Extensions/extensions.dart new file mode 100644 index 0000000..8af8331 --- /dev/null +++ b/lib/utils/Extensions/extensions.dart @@ -0,0 +1,6 @@ +export 'lib/build_context.dart'; +export 'lib/color.dart'; +export 'lib/date.dart'; +export 'lib/string.dart'; +export 'lib/textWidgetExtention.dart'; +export 'lib/translate.dart'; diff --git a/lib/utils/Extensions/lib/adaptive_type.dart b/lib/utils/Extensions/lib/adaptive_type.dart new file mode 100644 index 0000000..2568fe3 --- /dev/null +++ b/lib/utils/Extensions/lib/adaptive_type.dart @@ -0,0 +1,38 @@ +class Adapter { + ///String to int + static int? forceInt(dynamic value) { + if (value == null) { + return null; + } + if (value == "") { + return 0; + } + if (value is int) { + return value; + } else { + try { + return int.tryParse(value as String); + } catch (e) { + throw "$value is not valid parsable int"; + } + } + } + + double? forceDouble(dynamic value) { + if (value == null) { + return null; + } + if (value == "") { + return 0.0; + } + if (value is double) { + return value; + } else { + try { + return double.tryParse(value as String); + } catch (e) { + throw "$value is not valid parsable double"; + } + } + } +} diff --git a/lib/utils/Extensions/lib/build_context.dart b/lib/utils/Extensions/lib/build_context.dart new file mode 100644 index 0000000..54c0239 --- /dev/null +++ b/lib/utils/Extensions/lib/build_context.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +import '../../../Ui/Theme/theme.dart'; + +export '../../../Ui/Theme/theme.dart'; + +extension CustomContext on BuildContext { + double get screenWidth => MediaQuery.of(this).size.width; + double get screenHeight => MediaQuery.of(this).size.height; + + //This one for colorScheme shortcut + ColorScheme get color => Theme.of(this).colorScheme; + +//This one for fontSize + ///I created different Font class to limit textTheme values, let's assume if some one is using context.font and he is getting too may options related to text theme so how will he know which one is for use?? + ///So in theme.dart file i have created Font class which will give limited numbers of getters + Font get font => Theme.of(this).textTheme.font; +} diff --git a/lib/utils/Extensions/lib/color.dart b/lib/utils/Extensions/lib/color.dart new file mode 100644 index 0000000..d0ddbc6 --- /dev/null +++ b/lib/utils/Extensions/lib/color.dart @@ -0,0 +1,25 @@ +import 'dart:ui'; + +extension ColorExt on Color { + Color brighten(int value) { + Color color0 = this; + + int red = color0.red + value; + int green = color0.green + value; + int blue = color0.blue + value; + + return Color.fromARGB(color0.alpha, red.clamp(0, 255), green.clamp(0, 255), + blue.clamp(0, 255)); + } + + Color darken(int value) { + Color color0 = this; + + int red = color0.red - value; + int green = color0.green - value; + int blue = color0.blue - value; + + return Color.fromARGB(color0.alpha, red.clamp(0, 255), green.clamp(0, 255), + blue.clamp(0, 255)); + } +} diff --git a/lib/utils/Extensions/lib/date.dart b/lib/utils/Extensions/lib/date.dart new file mode 100644 index 0000000..0af2f16 --- /dev/null +++ b/lib/utils/Extensions/lib/date.dart @@ -0,0 +1,13 @@ +extension D on String { + DateTime parseAsDate() { + return DateTime.parse(this); + } +} + +extension DT on DateTime { + bool isSameDate(DateTime date2) { + return this.year == date2.year && + this.month == date2.month && + this.day == date2.day; + } +} diff --git a/lib/utils/Extensions/lib/iterable.dart b/lib/utils/Extensions/lib/iterable.dart new file mode 100644 index 0000000..23cbe76 --- /dev/null +++ b/lib/utils/Extensions/lib/iterable.dart @@ -0,0 +1,10 @@ +extension MapIndexed on Iterable { + Iterable mapIndexed(U Function(T e, int i) f) { + int i = 0; + return map((it) { + final t = i; + i++; + return f(it, t); + }); + } +} diff --git a/lib/utils/Extensions/lib/list.dart b/lib/utils/Extensions/lib/list.dart new file mode 100644 index 0000000..8a297ca --- /dev/null +++ b/lib/utils/Extensions/lib/list.dart @@ -0,0 +1,65 @@ +import 'package:ebroker/utils/Extensions/lib/adaptive_type.dart'; + +extension ListExt on List { + void addOrRemove(T element) { + if (contains(element)) { + remove(element); + } else { + add(element); + } + } + + void clearAndAddAll(List elements) { + clear(); + addAll(elements); + } + + void clearAndAdd(T elements) { + clear(); + add(elements); + } + + bool containesAll(List element) { + int _has = 0; + + for (var i = 0; i < element.length; i++) { + if (contains(element[i])) { + _has++; + } + } + + return _has == element.length; + } + + bool isFirst(T element) { + return first == element; + } + + bool isSingleElementAndIs(T element) { + if (length == 1) { + return first == element; + } + return false; + } + + List forceInt() { + return map((e) => Adapter.forceInt(e)!).toList() ?? []; + } + + List forceDouble() { + return map((e) => Adapter().forceDouble(e)!).toList() ?? []; + } + + dynamic sum(num Function(T e) fn) { + dynamic joinner; + for (var value in this) { + dynamic valFromFn = fn.call(value); + if (joinner == null) { + joinner = valFromFn; + } else { + joinner += valFromFn; + } + } + return joinner; + } +} diff --git a/lib/utils/Extensions/lib/map.dart b/lib/utils/Extensions/lib/map.dart new file mode 100644 index 0000000..806fff0 --- /dev/null +++ b/lib/utils/Extensions/lib/map.dart @@ -0,0 +1,71 @@ +extension MapExt on Map { + void removeEmptyKeys() { + removeWhere((key, value) => value.isEmpty || value == "" || value == null); + } + + dynamic get(dynamic key) { + return this[key]; + } +} + +extension ListExt on List { + Map findByKey(dynamic key, {dynamic equals}) { + return where((element) { + return element[key] == equals; + }).first; + } + + List query(String q) { + String compareVersion = ""; + String comparableKey = ""; + String returnableKey = ""; + String comparableWithKey = ""; + List? keySplit; + // "GET `name` where `id` = `0`"; + + List split = q.split(" "); + if (q.startsWith('GET')) { + if (split[1].startsWith("`") && split[1].endsWith("`")) { + returnableKey = split[1].replaceAll("`", ""); + + if (split[2] == "where") { + if (split[3].startsWith("`") && split[3].endsWith("`")) { + comparableKey = split[3].replaceAll("`", ""); + if (comparableKey.contains(".")) { + keySplit = comparableKey.split("."); + } + } + if (split[4] == "=") { + compareVersion = "eq"; + } + if (split[4] == ">") { + compareVersion = "gt"; + } + if (split[5].startsWith("`") && split[5].endsWith("`")) { + comparableWithKey = split[5].replaceAll("`", ""); + } + if (compareVersion == "eq") { + return where((element) { + element['obj']['isPrivate']; + + return element[comparableKey] == comparableWithKey; + }).toList().map((e) => e[returnableKey]).toList(); + } + if (compareVersion == "gt") { + if (returnableKey == "*") { + var list = where((element) { + return element[comparableKey] > int.parse(comparableWithKey); + }).toList(); + return list; + } + + return where((element) { + return element[comparableKey] > int.parse(comparableWithKey); + }).toList().map((e) => e[returnableKey]).toList(); + } + } + } + } + return []; + } +} diff --git a/lib/utils/Extensions/lib/num_extention.dart b/lib/utils/Extensions/lib/num_extention.dart new file mode 100644 index 0000000..d8c8440 --- /dev/null +++ b/lib/utils/Extensions/lib/num_extention.dart @@ -0,0 +1,19 @@ +import 'package:intl/intl.dart'; + +extension NUMEXT on num { + String numPriceFormate({bool? disabled}) { + String formattedNumber = NumberFormat.compactCurrency( + decimalDigits: 2, + symbol: + '', // if you want to add currency symbol then pass that in this else leave it empty. + ).format(this); + + if (disabled == true) { + return toString(); + } + + return formattedNumber; + } + + // clamp(lowerLimit, upperLimit) +} diff --git a/lib/utils/Extensions/lib/string.dart b/lib/utils/Extensions/lib/string.dart new file mode 100644 index 0000000..d979404 --- /dev/null +++ b/lib/utils/Extensions/lib/string.dart @@ -0,0 +1,39 @@ +import 'dart:developer' as d; + +import 'package:ebroker/exports/main_export.dart'; + +extension S on String { + void get logg { + if (Constant.terminalLogMode == "debug") { + d.log(this); + } else { + print(this); + } + } + + void log([String? name]) { + if (Constant.terminalLogMode == "debug") { + d.log(this, name: name ?? ""); + } else { + print("[${name ?? "log"}]: $this"); + } + } +} + +extension OB on Object { + void get logg { + if (Constant.terminalLogMode == "debug") { + d.log(toString()); + } else { + print("$this"); + } + } + + void mlog([String? name]) { + if (Constant.terminalLogMode == "debug") { + d.log(toString(), name: name ?? ""); + } else { + print("[${name ?? "log"}]: $this"); + } + } +} diff --git a/lib/utils/Extensions/lib/textWidgetExtention.dart b/lib/utils/Extensions/lib/textWidgetExtention.dart new file mode 100644 index 0000000..9071537 --- /dev/null +++ b/lib/utils/Extensions/lib/textWidgetExtention.dart @@ -0,0 +1,108 @@ +// An extention for use with text widget to avoid re-write boilarplate code + +// ignore_for_file: file_names + +import 'package:flutter/material.dart'; + +extension StyledText on T { + Text copyWith({ + String? data, + InlineSpan? textSpan, + TextStyle? style, + StrutStyle? strutStyle, + TextAlign? textAlign, + TextDirection? textDirection, + bool? softWrap, + TextOverflow? overflow, + double? textScaleFactor, + Locale? locale, + int? maxLines, + String? semanticsLabel, + TextWidthBasis? textWidthBasis, + TextHeightBehavior? textHeightBehavior, + }) { + return Text( + data ?? this.data ?? "", + style: style ?? this.style, + locale: locale ?? this.locale, + maxLines: maxLines ?? this.maxLines, + overflow: overflow ?? this.overflow, + semanticsLabel: semanticsLabel ?? this.semanticsLabel, + softWrap: softWrap ?? this.softWrap, + strutStyle: strutStyle ?? this.strutStyle, + textAlign: textAlign ?? this.textAlign, + textDirection: textDirection ?? this.textDirection, + textHeightBehavior: textHeightBehavior ?? this.textHeightBehavior, + textScaleFactor: textScaleFactor ?? this.textScaleFactor, + textWidthBasis: textWidthBasis ?? this.textWidthBasis, + ); + } + + T bold({FontWeight? weight}) => copyWith( + style: (style ?? const TextStyle()).copyWith( + fontWeight: weight ?? FontWeight.bold, + ), + ) as T; + + ///Text overflow is inclueded + T setMaxLines({required int lines}) { + return copyWith( + maxLines: lines, overflow: TextOverflow.ellipsis, softWrap: true) as T; + } + + T italic() { + return copyWith( + style: (style ?? const TextStyle()).copyWith(fontStyle: FontStyle.italic), + ) as T; + } + + T size(double size) { + return copyWith( + style: (style ?? const TextStyle()).copyWith(fontSize: size)) as T; + } + + T color(Color color) { + return copyWith(style: (style ?? const TextStyle()).copyWith(color: color)) + as T; + } + + T underline() => copyWith( + style: (style ?? const TextStyle()) + .copyWith(decoration: TextDecoration.underline), + ) as T; + + T centerAlign() => copyWith(textAlign: TextAlign.center) as T; + + T firstUpperCaseWidget() { + String upperCase = ""; + var suffix = ""; + if (data?.isNotEmpty ?? true) { + upperCase = data?[0].toUpperCase() ?? ""; + suffix = data!.substring(1, data?.length); + } + return copyWith(data: upperCase + suffix) as T; + } + + // randomize() { + // String? text = data; + // Set indexSet = {}; + // List.generate(text?.length ?? 0, (index) { + // int? value = _randomGen(text, indexSet); + // if (value != null) { + // indexSet.add(value); + // } + // }); + + // print(indexSet.toList().toString()); + // } + + // int? _randomGen(String? text, Set indexSet) { + // int random = Random().nextInt(text?.length ?? 0); + // if (indexSet.contains(random)) { + // _randomGen(text, indexSet); + // } else { + // return random; + // } + // return null; + // } +} diff --git a/lib/utils/Extensions/lib/translate.dart b/lib/utils/Extensions/lib/translate.dart new file mode 100644 index 0000000..d88cf3a --- /dev/null +++ b/lib/utils/Extensions/lib/translate.dart @@ -0,0 +1,9 @@ +import 'package:flutter/cupertino.dart'; + +import '../../ui_utils.dart'; + +extension TranslateString on String { + String translate(BuildContext context) { + return UiUtils.translate(context, this); + } +} diff --git a/lib/utils/Geocoding/geocoding.dart b/lib/utils/Geocoding/geocoding.dart new file mode 100644 index 0000000..6fc4f5f --- /dev/null +++ b/lib/utils/Geocoding/geocoding.dart @@ -0,0 +1 @@ +abstract class GeocodingProvider {} diff --git a/lib/utils/LiquidIndicator/.DS_Store b/lib/utils/LiquidIndicator/.DS_Store new file mode 100644 index 0000000..6eeedb5 Binary files /dev/null and b/lib/utils/LiquidIndicator/.DS_Store differ diff --git a/lib/utils/LiquidIndicator/liquid_progress_indicator.dart b/lib/utils/LiquidIndicator/liquid_progress_indicator.dart new file mode 100644 index 0000000..6b52d73 --- /dev/null +++ b/lib/utils/LiquidIndicator/liquid_progress_indicator.dart @@ -0,0 +1,5 @@ +library liquid_progress_indicator; + +export 'src/liquid_circular_progress_indicator.dart'; +export 'src/liquid_linear_progress_indicator.dart'; +export 'src/liquid_custom_progress_indicator.dart'; diff --git a/lib/utils/LiquidIndicator/src/liquid_circular_progress_indicator.dart b/lib/utils/LiquidIndicator/src/liquid_circular_progress_indicator.dart new file mode 100644 index 0000000..2a9f39e --- /dev/null +++ b/lib/utils/LiquidIndicator/src/liquid_circular_progress_indicator.dart @@ -0,0 +1,134 @@ +import 'dart:math' as math; + +import 'package:ebroker/utils/LiquidIndicator/src/wave.dart'; +import 'package:flutter/material.dart'; + +const double _twoPi = math.pi * 2.0; +const double _epsilon = .001; +const double _sweep = _twoPi - _epsilon; + +class LiquidCircularProgressIndicator extends ProgressIndicator { + ///The width of the border, if this is set [borderColor] must also be set. + final double? borderWidth; + + ///The color of the border, if this is set [borderWidth] must also be set. + final Color? borderColor; + + ///The widget to show in the center of the progress indicator. + final Widget? center; + + ///The direction the liquid travels. + final Axis direction; + + LiquidCircularProgressIndicator({ + Key? key, + double value = 0.5, + Color? backgroundColor, + Animation? valueColor, + this.borderWidth, + this.borderColor, + this.center, + this.direction = Axis.vertical, + }) : super( + key: key, + value: value, + backgroundColor: backgroundColor, + valueColor: valueColor, + ) { + if (borderWidth != null && borderColor == null || + borderColor != null && borderWidth == null) { + throw ArgumentError("borderWidth and borderColor should both be set."); + } + } + + Color _getBackgroundColor(BuildContext context) => + backgroundColor ?? Theme.of(context).colorScheme.background; + + Color _getValueColor(BuildContext context) => + valueColor?.value ?? Theme.of(context).colorScheme.secondary; + + @override + State createState() => + _LiquidCircularProgressIndicatorState(); +} + +class _LiquidCircularProgressIndicatorState + extends State { + @override + Widget build(BuildContext context) { + return ClipPath( + clipper: _CircleClipper(), + child: CustomPaint( + painter: _CirclePainter( + color: widget._getBackgroundColor(context), + ), + foregroundPainter: _CircleBorderPainter( + color: widget.borderColor, + width: widget.borderWidth, + ), + child: Stack( + children: [ + Wave( + value: widget.value, + color: widget._getValueColor(context), + direction: widget.direction, + ), + if (widget.center != null) Center(child: widget.center), + ], + ), + ), + ); + } +} + +class _CirclePainter extends CustomPainter { + final Color color; + + _CirclePainter({required this.color}); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint()..color = color; + canvas.drawArc(Offset.zero & size, 0, _sweep, false, paint); + } + + @override + bool shouldRepaint(_CirclePainter oldDelegate) => color != oldDelegate.color; +} + +class _CircleBorderPainter extends CustomPainter { + final Color? color; + final double? width; + + _CircleBorderPainter({this.color, this.width}); + + @override + void paint(Canvas canvas, Size size) { + if (color == null || width == null) { + return; + } + + final borderPaint = Paint() + ..color = color! + ..style = PaintingStyle.stroke + ..strokeWidth = width!; + final newSize = Size(size.width - width!, size.height - width!); + canvas.drawArc(Offset(width! / 2, width! / 2) & newSize, 0, _sweep, false, + borderPaint); + } + + @override + bool shouldRepaint(_CircleBorderPainter oldDelegate) => + color != oldDelegate.color || width != oldDelegate.width; +} + +class _CircleClipper extends CustomClipper { + @override + Path getClip(Size size) { + final path = Path()..addArc(Offset.zero & size, 0, _sweep); + return path; + } + + @override + bool shouldReclip(CustomClipper oldClipper) => false; +} diff --git a/lib/utils/LiquidIndicator/src/liquid_custom_progress_indicator.dart b/lib/utils/LiquidIndicator/src/liquid_custom_progress_indicator.dart new file mode 100644 index 0000000..a168500 --- /dev/null +++ b/lib/utils/LiquidIndicator/src/liquid_custom_progress_indicator.dart @@ -0,0 +1,105 @@ +import 'package:ebroker/utils/LiquidIndicator/src/wave.dart'; +import 'package:flutter/material.dart'; + +class LiquidCustomProgressIndicator extends ProgressIndicator { + ///The widget to show in the center of the progress indicator. + final Widget? center; + + ///The direction the liquid travels. + final Axis direction; + + ///The path used to draw the shape of the progress indicator. The size of the progress indicator is controlled by the bounds of this path. + final Path shapePath; + + const LiquidCustomProgressIndicator({ + Key? key, + double value = 0.5, + Color? backgroundColor, + Animation? valueColor, + this.center, + required this.direction, + required this.shapePath, + }) : super( + key: key, + value: value, + backgroundColor: backgroundColor, + valueColor: valueColor, + ); + + Color _getBackgroundColor(BuildContext context) => + backgroundColor ?? Theme.of(context).colorScheme.background; + + Color _getValueColor(BuildContext context) => + valueColor?.value ?? Theme.of(context).colorScheme.secondary; + + @override + State createState() => _LiquidCustomProgressIndicatorState(); +} + +class _LiquidCustomProgressIndicatorState + extends State { + @override + Widget build(BuildContext context) { + final pathBounds = widget.shapePath.getBounds(); + return SizedBox( + width: pathBounds.width + pathBounds.left, + height: pathBounds.height + pathBounds.top, + child: ClipPath( + clipper: _CustomPathClipper( + path: widget.shapePath, + ), + child: CustomPaint( + painter: _CustomPathPainter( + color: widget._getBackgroundColor(context), + path: widget.shapePath, + ), + child: Stack( + children: [ + Positioned.fill( + left: pathBounds.left, + top: pathBounds.top, + child: Wave( + value: widget.value, + color: widget._getValueColor(context), + direction: widget.direction, + ), + ), + if (widget.center != null) Center(child: widget.center), + ], + ), + ), + ), + ); + } +} + +class _CustomPathPainter extends CustomPainter { + final Color color; + final Path path; + + _CustomPathPainter({required this.color, required this.path}); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint()..color = color; + canvas.drawPath(path, paint); + } + + @override + bool shouldRepaint(_CustomPathPainter oldDelegate) => + color != oldDelegate.color || path != oldDelegate.path; +} + +class _CustomPathClipper extends CustomClipper { + final Path path; + + _CustomPathClipper({required this.path}); + + @override + Path getClip(Size size) { + return path; + } + + @override + bool shouldReclip(CustomClipper oldClipper) => false; +} diff --git a/lib/utils/LiquidIndicator/src/liquid_linear_progress_indicator.dart b/lib/utils/LiquidIndicator/src/liquid_linear_progress_indicator.dart new file mode 100644 index 0000000..83019eb --- /dev/null +++ b/lib/utils/LiquidIndicator/src/liquid_linear_progress_indicator.dart @@ -0,0 +1,159 @@ +import 'package:ebroker/utils/LiquidIndicator/src/wave.dart'; +import 'package:flutter/material.dart'; + +class LiquidLinearProgressIndicator extends ProgressIndicator { + ///The width of the border, if this is set [borderColor] must also be set. + final double? borderWidth; + + ///The color of the border, if this is set [borderWidth] must also be set. + final Color? borderColor; + + ///The radius of the border. + final double? borderRadius; + + ///The widget to show in the center of the progress indicator. + final Widget? center; + + ///The direction the liquid travels. + final Axis direction; + + LiquidLinearProgressIndicator({ + Key? key, + double value = 0.5, + Color? backgroundColor, + Animation? valueColor, + this.borderWidth, + this.borderColor, + this.borderRadius, + this.center, + this.direction = Axis.horizontal, + }) : super( + key: key, + value: value, + backgroundColor: backgroundColor, + valueColor: valueColor, + ) { + if (borderWidth != null && borderColor == null || + borderColor != null && borderWidth == null) { + throw ArgumentError("borderWidth and borderColor should both be set."); + } + } + + Color _getBackgroundColor(BuildContext context) => + backgroundColor ?? Theme.of(context).colorScheme.background; + + Color _getValueColor(BuildContext context) => + valueColor?.value ?? Theme.of(context).colorScheme.secondary; + + @override + State createState() => _LiquidLinearProgressIndicatorState(); +} + +class _LiquidLinearProgressIndicatorState + extends State { + @override + Widget build(BuildContext context) { + return ClipPath( + clipper: _LinearClipper( + radius: widget.borderRadius, + ), + child: CustomPaint( + painter: _LinearPainter( + color: widget._getBackgroundColor(context), + radius: widget.borderRadius ?? 0, + ), + foregroundPainter: _LinearBorderPainter( + color: widget.borderColor ?? Colors.black, + width: widget.borderWidth ?? 0, + radius: widget.borderRadius ?? 0, + ), + child: Stack( + children: [ + Wave( + value: widget.value, + color: widget._getValueColor(context), + direction: widget.direction, + ), + if (widget.center != null) Center(child: widget.center), + ], + ), + ), + ); + } +} + +class _LinearPainter extends CustomPainter { + final Color color; + final double radius; + + _LinearPainter({required this.color, required this.radius}); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint()..color = color; + canvas.drawRRect( + RRect.fromRectAndRadius( + Rect.fromLTWH(0, 0, size.width, size.height), + Radius.circular(radius), + ), + paint); + } + + @override + bool shouldRepaint(_LinearPainter oldDelegate) => color != oldDelegate.color; +} + +class _LinearBorderPainter extends CustomPainter { + final Color color; + final double width; + final double radius; + + _LinearBorderPainter({ + required this.color, + required this.width, + required this.radius, + }); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = color + ..style = PaintingStyle.stroke + ..strokeWidth = width; + final alteredRadius = radius; + canvas.drawRRect( + RRect.fromRectAndRadius( + Rect.fromLTWH( + width / 2, width / 2, size.width - width, size.height - width), + Radius.circular(alteredRadius - width), + ), + paint); + } + + @override + bool shouldRepaint(_LinearBorderPainter oldDelegate) => + color != oldDelegate.color || + width != oldDelegate.width || + radius != oldDelegate.radius; +} + +class _LinearClipper extends CustomClipper { + final double? radius; + + _LinearClipper({required this.radius}); + + @override + Path getClip(Size size) { + final path = Path() + ..addRRect( + RRect.fromRectAndRadius( + Rect.fromLTWH(0, 0, size.width, size.height), + Radius.circular(radius ?? 0), + ), + ); + return path; + } + + @override + bool shouldReclip(CustomClipper oldClipper) => false; +} diff --git a/lib/utils/LiquidIndicator/src/wave.dart b/lib/utils/LiquidIndicator/src/wave.dart new file mode 100644 index 0000000..cc84758 --- /dev/null +++ b/lib/utils/LiquidIndicator/src/wave.dart @@ -0,0 +1,120 @@ +import 'dart:math' as math; + +import 'package:flutter/material.dart'; + +class Wave extends StatefulWidget { + final double? value; + final Color color; + final Axis direction; + + const Wave({ + Key? key, + required this.value, + required this.color, + required this.direction, + }) : super(key: key); + + @override + _WaveState createState() => _WaveState(); +} + +class _WaveState extends State with SingleTickerProviderStateMixin { + late AnimationController _animationController; + + @override + void initState() { + super.initState(); + + _animationController = AnimationController( + vsync: this, + duration: Duration(seconds: 2), + ); + _animationController.repeat(); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: CurvedAnimation( + parent: _animationController, + curve: Curves.easeInOut, + ), + builder: (context, child) => ClipPath( + child: Container( + color: widget.color, + ), + clipper: _WaveClipper( + + animationValue: _animationController.value, + value: widget.value, + direction: widget.direction, + ), + ), + ); + } +} + +class _WaveClipper extends CustomClipper { + final double animationValue; + final double? value; + final Axis direction; + + _WaveClipper({ + required this.animationValue, + required this.value, + required this.direction, + }); + + @override + Path getClip(Size size) { + if (direction == Axis.horizontal) { + Path path = Path() + ..addPolygon(_generateHorizontalWavePath(size), false) + ..lineTo(0.0, size.height) + ..lineTo(0.0, 0.0) + ..close(); + return path; + } + + Path path = Path() + ..addPolygon(_generateVerticalWavePath(size), false) + ..lineTo(size.width, size.height) + ..lineTo(0.0, size.height) + ..close(); + return path; + } + + List _generateHorizontalWavePath(Size size) { + final waveList = []; + for (int i = -2; i <= size.height.toInt() + 2; i++) { + final waveHeight = (size.width / 20); + final dx = math.sin((animationValue * 360 - i) % 360 * (math.pi / 180)) * + waveHeight + + (size.width * value!); + waveList.add(Offset(dx, i.toDouble())); + } + return waveList; + } + + List _generateVerticalWavePath(Size size) { + final waveList = []; + for (int i = -2; i <= size.width.toInt() + 2; i++) { + final waveHeight = (size.height / 20); + final dy = math.sin((animationValue * 360 - i) % 360 * (math.pi / 180)) * + waveHeight + + (size.height - (size.height * value!)); + waveList.add(Offset(i.toDouble(), dy)); + } + return waveList; + } + + @override + bool shouldReclip(_WaveClipper oldClipper) => + animationValue != oldClipper.animationValue; +} diff --git a/lib/utils/Login/AppleLogin/apple_login.dart b/lib/utils/Login/AppleLogin/apple_login.dart new file mode 100644 index 0000000..09e7777 --- /dev/null +++ b/lib/utils/Login/AppleLogin/apple_login.dart @@ -0,0 +1,37 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:sign_in_with_apple/sign_in_with_apple.dart'; + +import '../lib/login_status.dart'; +import '../lib/login_system.dart'; + +class AppleLogin extends LoginSystem { + OAuthCredential? credential; + @override + void init() async { + final AuthorizationCredentialAppleID appleIdCredential = + await SignInWithApple.getAppleIDCredential( + scopes: [ + AppleIDAuthorizationScopes.email, + AppleIDAuthorizationScopes.fullName, + ], + ); + final OAuthProvider oAuthProvider = OAuthProvider('apple.com'); + credential = oAuthProvider.credential( + idToken: appleIdCredential.identityToken, + accessToken: appleIdCredential.authorizationCode, + ); + } + + Future login() async { + if (credential != null) { + final userCredential = + await firebaseAuth.signInWithCredential(credential!); + + return userCredential; + } + return null; + } + + @override + void onEvent(MLoginState state) {} +} diff --git a/lib/utils/Login/Disclaimer.txt b/lib/utils/Login/Disclaimer.txt new file mode 100644 index 0000000..7baa77c --- /dev/null +++ b/lib/utils/Login/Disclaimer.txt @@ -0,0 +1,5 @@ +Note: + +This "Login" folder is for future use. Currently it is not being used. + +Thank you! \ No newline at end of file diff --git a/lib/utils/Login/EmailLogin/email_login.dart b/lib/utils/Login/EmailLogin/email_login.dart new file mode 100644 index 0000000..fb60481 --- /dev/null +++ b/lib/utils/Login/EmailLogin/email_login.dart @@ -0,0 +1,35 @@ +import 'dart:developer'; + +import 'package:firebase_auth/firebase_auth.dart'; + +import '../lib/login_status.dart'; +import '../lib/login_system.dart'; +import '../lib/payloads.dart'; + +class EmailLogin extends LoginSystem { + @override + Future login() async { + UserCredential? userCredential; + if (payload is EmailLoginPayload) { + var _payload = (payload as EmailLoginPayload); + if (_payload.type == EmailLoginType.signup) { + userCredential = + await FirebaseAuth.instance.createUserWithEmailAndPassword( + email: _payload.email, + password: _payload.password, + ); + } else { + userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword( + email: _payload.email, + password: _payload.password, + ); + } + } + return userCredential; + } + + @override + void onEvent(MLoginState state) { + log("MLOGIN STATE IS $state"); + } +} diff --git a/lib/utils/Login/GoogleLogin/google_login.dart b/lib/utils/Login/GoogleLogin/google_login.dart new file mode 100644 index 0000000..5780210 --- /dev/null +++ b/lib/utils/Login/GoogleLogin/google_login.dart @@ -0,0 +1,56 @@ +import 'dart:developer'; + +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; +import 'package:google_sign_in/google_sign_in.dart'; + +import '../lib/login_status.dart'; +import '../lib/login_system.dart'; + +class GoogleLogin extends LoginSystem { + GoogleSignIn? _googleSignIn; + + @override + void init() async { + _googleSignIn = GoogleSignIn( + scopes: [ + 'email', + 'https://www.googleapis.com/auth/contacts.readonly', + ], + ); + } + + @override + Future login() async { + try { + emit(MProgress()); + GoogleSignInAccount? googleSignIn = await _googleSignIn?.signIn(); + if (googleSignIn == null) { + throw ErrorDescription("google-terminated"); + } + GoogleSignInAuthentication? googleAuth = + await googleSignIn?.authentication; + + AuthCredential authCredential = GoogleAuthProvider.credential( + accessToken: googleAuth?.accessToken, + idToken: googleAuth?.idToken, + ); + + UserCredential userCredential = + await firebaseAuth.signInWithCredential(authCredential); + emit(MSuccess()); + + return userCredential; + } catch (e) { + log("FAILLL ISS $e"); + + emit(MFail(e.toString())); + throw e; + } + } + + @override + void onEvent(MLoginState state) { + // TODO: implement onEvent + } +} diff --git a/lib/utils/Login/PhoneLogin/phone_login.dart b/lib/utils/Login/PhoneLogin/phone_login.dart new file mode 100644 index 0000000..ef4b378 --- /dev/null +++ b/lib/utils/Login/PhoneLogin/phone_login.dart @@ -0,0 +1,54 @@ +import 'package:firebase_auth/firebase_auth.dart'; + +import '../../../utils/constant.dart'; +import '../lib/login_status.dart'; +import '../lib/login_system.dart'; +import '../lib/payloads.dart'; + +class PhoneLogin extends LoginSystem { + String? verificationId; + + @override + Future login() async { + try { + emit(MProgress()); + // (state); + PhoneAuthCredential credential = PhoneAuthProvider.credential( + verificationId: verificationId ?? "", + smsCode: (payload as PhoneLoginPayload).getOTP()!); + + UserCredential userCredential = + await firebaseAuth.signInWithCredential(credential); + emit(MSuccess()); + + return userCredential; + } catch (e) { + emit(MFail(e)); + } + } + + @override + Future requestVerification() async { + await FirebaseAuth.instance.verifyPhoneNumber( + timeout: Duration( + seconds: Constant.otpTimeOutSecond, + ), + phoneNumber: + "+${(payload as PhoneLoginPayload).countryCode}${(payload as PhoneLoginPayload).phoneNumber}", + verificationCompleted: (PhoneAuthCredential credential) {}, + verificationFailed: (FirebaseAuthException e) { + emit(MFail(e)); + }, + codeSent: (String verificationId, int? resendToken) { + super.requestVerification(); + PhoneLoginPayload.forceResendingtoken = resendToken; + this.verificationId = verificationId; + }, + codeAutoRetrievalTimeout: (String verificationId) {}, + forceResendingToken: PhoneLoginPayload.forceResendingtoken, + ); + } + + @override + void onEvent(MLoginState state) {} +} diff --git a/lib/utils/Login/lib/login_status.dart b/lib/utils/Login/lib/login_status.dart new file mode 100644 index 0000000..d42f428 --- /dev/null +++ b/lib/utils/Login/lib/login_status.dart @@ -0,0 +1,17 @@ +abstract class MLoginState {} + +class MProgress extends MLoginState {} + +class MVerificationPending extends MLoginState { + // final String target; + + MVerificationPending(); +} + +class MSuccess extends MLoginState {} + +class MFail extends MLoginState { + final dynamic error; + + MFail(this.error); +} diff --git a/lib/utils/Login/lib/login_system.dart b/lib/utils/Login/lib/login_system.dart new file mode 100644 index 0000000..15e2f60 --- /dev/null +++ b/lib/utils/Login/lib/login_system.dart @@ -0,0 +1,130 @@ +import 'package:ebroker/utils/Login/lib/payloads.dart'; +import 'package:firebase_auth/firebase_auth.dart'; + +import 'login_status.dart'; + +abstract class LoginSystem { + List listeners = []; + FirebaseAuth firebaseAuth = FirebaseAuth.instance; + + //This is abstract method it will be called when state of login change it means when emit method will be called it will called + void onEvent(MLoginState state); + + ///This emit method will change state of login and notify all listeners and call onEvent method + void emit(MLoginState state) { + ///Loop through all listeners and call them + for (Function(MLoginState fn) i in listeners) { + i.call(state); + } + onEvent(state); + } + + Future requestVerification() async { + emit(MVerificationPending()); + } + + LoginPayload? payload; + + //This will set login payload it means it will set necessary data while login like its email , password or anything else + void setPayload(LoginPayload payload) { + this.payload = payload; + } + + ///This method will be called when initialize this + void init() {} + + ///Here will be implementation of the main login method, it will return usercredentials + Future login(); +} + +///From this we will be able to use this login . [this is for single authentication . if you use this you must have to create instance of every login system individually] +class MAuthentication { + LoginPayload? payload; + final LoginSystem system; + MAuthentication(this.system, {this.payload}); + + //This will call login system's init method + void init() { + system.init(); + } + + ///this login call will execute login method of login system which is being assigned + Future? login() async { + //assign payload to system from constructor + system.payload = payload; + + UserCredential? credential = await system.login(); + //Return its response + return credential; + } +} + +///This is used for multiple authentication system like you do not have to create all system's instance again and again +class MMultiAuthentication { + MultiLoginPayload? payload; + Map systems; + String? _selectedLoginSystem; + + MMultiAuthentication( + this.systems, { + this.payload, + }); + + ///This init will call all login system's init method by loop + void init() { + for (LoginSystem loginSystem in systems.values) { + loginSystem.init(); + } + } + + requestVerification() { + systems.forEach((String key, LoginSystem value) async { + //like assign the particular payload if key is matching to selected login system + LoginSystem? selectedSystem; + if (_selectedLoginSystem == key) { + selectedSystem = systems[key]; + selectedSystem?.payload = payload?.payloads[key]; + selectedSystem?.requestVerification(); + } + }); + } + + ///This method ensures which login system is active + void setActive(String key) { + _selectedLoginSystem = key; + } + + ///This will listen changes in state + void listen(Function(MLoginState state) fn) { + systems.forEach((String key, LoginSystem value) async { + // if (_selectedLoginSystem == key) { + // systems[key]?.payload = payload?.payloads[key]; + systems[key]?.listeners.add(fn); + // } + }); + } + + ///This method will called for login + Future? login() async { + if (_selectedLoginSystem == "" || _selectedLoginSystem == null) { + throw "Please select login system using setActive method"; + } + LoginSystem? selectedSystem; + + //assign payload and login system + systems.forEach((String key, LoginSystem value) async { + //like assign the particular payload if key is matching to selected login system + if (_selectedLoginSystem == key) { + systems[key]?.payload = payload?.payloads[key]; + selectedSystem = systems[key]; + } + }); + + UserCredential? credential; + if (selectedSystem != null) { + credential = await selectedSystem?.login(); + } + + return credential; + } +} diff --git a/lib/utils/Login/lib/payloads.dart b/lib/utils/Login/lib/payloads.dart new file mode 100644 index 0000000..4ada10f --- /dev/null +++ b/lib/utils/Login/lib/payloads.dart @@ -0,0 +1,37 @@ +abstract class LoginPayload {} + +class MultiLoginPayload { + final Map payloads; + + MultiLoginPayload(this.payloads); +} + +enum EmailLoginType { login, signup } + +class EmailLoginPayload extends LoginPayload { + final String email; + final String password; + final EmailLoginType type; + + EmailLoginPayload( + {required this.email, required this.password, required this.type}); +} + +class GoogleLoginPayload extends LoginPayload { + GoogleLoginPayload(); +} + +class PhoneLoginPayload extends LoginPayload { + final String phoneNumber; + final String countryCode; + static int? forceResendingtoken; + String? otp; + PhoneLoginPayload(this.phoneNumber, this.countryCode); + void setOTP(String value) { + this.otp = value; + } + + String? getOTP() { + return otp; + } +} diff --git a/lib/utils/Lottie/lottieEditor.dart b/lib/utils/Lottie/lottieEditor.dart new file mode 100644 index 0000000..19d263a --- /dev/null +++ b/lib/utils/Lottie/lottieEditor.dart @@ -0,0 +1,258 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show rootBundle; + +class LottieEditor { + final ValueNotifier?> _lottieNotifier = + ValueNotifier>({}); + Map? get lottieJson => _lottieNotifier.value; + ValueNotifier?> get listener => _lottieNotifier; + LottieEditor() {} + + ///This is to load lottie file put here asset file path + ///CALL THIS FIRST-> + Future openAndLoad(String path) async { + try { + final String data = await rootBundle.loadString(path); + final Map lottieJson = json.decode(data); + _updateLottieJson(lottieJson); + } catch (e) { + _handleError("Error opening and loading Lottie file", e); + } + } + + ////This function is used to modify all colors of lottie with their opacity + ////USE THIS--> + void changeWholeLottieFileColor(Color targetColor) { + if (lottieJson != null) { + final Map modifiedJson = + modifyColorsRecursive(lottieJson!, targetColor); + _updateLottieJson(modifiedJson); + } else {} + } + + ////This file is to modify colors by their shape name; [Useful for multiple color lottie] + void changeColorsOfShapeNames(List shapeNames, Color targetColor) { + if (lottieJson != null) { + final Map modifiedJson = + modifyColorsByShapeNames(lottieJson!, shapeNames, targetColor); + _updateLottieJson(modifiedJson); + } else {} + } + + ///This will convert json to UINT8 + ///USE THIS TO DISPLAY LOTTIE + + Uint8List convertToUint8List() { + if (lottieJson != null) { + return Uint8List.fromList(utf8.encode(json.encode(lottieJson))); + } else { + print("Lottie file not loaded. Call openAndLoad() first."); + return Uint8List(0); // Return an empty list or handle as needed + } + } + + // Private method to modify colors recursively + Map modifyColorsRecursive( + Map json, Color targetColor) { + final List layers = json['layers'] ?? []; + + for (dynamic layer in layers) { + _modifyLayerColors(layer, targetColor); + } + return json; + } + + // Private method to modify colors by shape names + Map modifyColorsByShapeNames( + Map json, List shapeNames, Color targetColor) { + final List layers = json['layers'] ?? []; + for (final dynamic layer in layers) { + _modifyLayerColorsByShapeNames(layer, shapeNames, targetColor); + } + return json; + } + + // Private method to modify colors within a layer + void _modifyLayerColors(dynamic layer, Color targetColor) { + final List shapes = layer['shapes'] ?? []; + for (final dynamic shape in shapes) { + _loopShapes(shape, targetColor); + // log("SHAPE NAMEEE $shape"); + // if (shape['ty'] == 'fl' || shape['ty'] == 'st') { + // log("HEHE COLOR ISS $shape"); + // shape['c'] = _flutterColorToLottie(targetColor); + // } else if (shape['ty'] == 'gr') { + // log("COLORS GG"); + // _modifyLayerColors(shape['it'], targetColor); + // } + } + } + + void _loopShapes(Map shape, targetColor) { + List shapes = shape['it'] ?? []; + for (var element in shapes) { + if (element['ty'] == "fl") { + element['c'] = _flutterColorToLottie(targetColor); + } else if (element['ty'] == "gr") { + _loopShapes(element, targetColor); + } else if (element['ty'] == "st") { + element['c'] = _flutterColorToLottie(targetColor); + } + } + } + + // Private method to modify colors within a layer based on shape names + void _modifyLayerColorsByShapeNames( + dynamic layer, List shapeNames, Color targetColor) { + final List shapes = layer['shapes'] ?? []; + for (final dynamic shape in shapes) { + if (shape['ty'] == 'fl' || shape['ty'] == 'st') { + final String shapeName = shape['nm']; + + if (shapeNames.contains(shapeName)) { + shape['c'] = _flutterColorToLottie(targetColor); + } + } else if (shape['ty'] == 'gr') { + _modifyLayerColorsByShapeNames(shape, shapeNames, targetColor); + } + } + } + + // Private method to handle errors + void _handleError(String message, dynamic error) { + print("$message: $error"); + // You can choose to throw an exception, log the error, or handle it differently. + } + + // Private method to update the Lottie JSON and notify listeners + void _updateLottieJson(Map modifiedJson) { + _lottieNotifier.value = modifiedJson; + } + + // Private method to convert Flutter color to Lottie color format + Map _flutterColorToLottie(Color flutterColor) { + final double red = flutterColor.red / 255.0; + final double green = flutterColor.green / 255.0; + final double blue = flutterColor.blue / 255.0; + final double alpha = flutterColor.opacity; + + return { + 'a': 0, + 'k': [red, green, blue, alpha], + 'ix': 4, + }; + } + + // Dispose method to release resources + void dispose() { + _lottieNotifier.dispose(); + } +} + +/*** + * + * //THIS IS EXPERIMENTAL + Future extractLayersInfo(BuildContext context) async { + try { + final ByteData data = await rootBundle.load("assets/lottie/onbo_a.json"); + final LottieComposition composition = + await LottieComposition.fromByteData(data); + recurs(composition.layers); + log({"layers": composition.layers, "": composition.name}.toString()); + } catch (e) { + print('Error loading Lottie file: $e'); + } + } + + recurs(List layers) { + for (Layer layer in layers) { + log("LAYER : ${layer.name} ${layer.id}"); + shapeGroupRecurs(layer.shapes); + } + } + + shapeGroupRecurs(List shapes) { + for (ContentModel element in shapes) { + log("ELEMENT ISS $element"); + + if (element is ShapeGroup) { + shapeGroupRecurs(element.items); + + log(" SHAPE GROUP: ${element.name}"); + } + + if (element is ShapePath) { + log("SHAPE PATH: ${element.name} ${element}"); + } + + if (element is ShapeFill) { + shapes.remove(element); + log("SHAPE FILL: ${element.name} ${element.color}"); + } + } + } + + Future modifyLottieColors() async { + try { + final String data = + await rootBundle.loadString("assets/lottie/onbo_a.json"); + var lottieJson = json.decode(data); + + modifyColorsRecursive(lottieJson); + + // Log the modified JSON + log("Modified Lottie JSON: $lottieJson"); + Uint8List uint8List = + Uint8List.fromList(utf8.encode(json.encode(lottieJson))); + setState(() { + lottiesss = uint8List; + }); + } catch (e, st) { + log("Error modifying Lottie colors: $e, $st"); + } + } + + void modifyColorsRecursive(Map json) { + List layers = json['layers']; + for (var element in layers) { + loopLayers(element); + } + } + + void loopLayers(Map layer) { + List shapes = layer['shapes'] ?? []; + for (var element in shapes) { + loopShapes(element); + } + } + + void loopShapes(Map shape) { + List shapes = shape['it'] ?? []; + for (var element in shapes) { + if (element['ty'] == "fl") { + log("FILLLLLL $element"); + element['c'] = flutterColorToLottie(context.color.teritoryColor); + } else if (element['ty'] == "gr") { + loopShapes(element); + } else if (element['ty'] == "st") { + element['c'] = flutterColorToLottie(context.color.teritoryColor); + } + } + } + + Map flutterColorToLottie(Color flutterColor) { + double red = flutterColor.red / 255.0; + double green = flutterColor.green / 255.0; + double blue = flutterColor.blue / 255.0; + double alpha = flutterColor.opacity; + + return { + 'a': 0, + 'k': [red, green, blue, alpha], + 'ix': 4, + }; + } + */ diff --git a/lib/utils/Network/.DS_Store b/lib/utils/Network/.DS_Store new file mode 100644 index 0000000..0d48c14 Binary files /dev/null and b/lib/utils/Network/.DS_Store differ diff --git a/lib/utils/Network/Interseptors/network_request_interseptor.dart b/lib/utils/Network/Interseptors/network_request_interseptor.dart new file mode 100644 index 0000000..205bfed --- /dev/null +++ b/lib/utils/Network/Interseptors/network_request_interseptor.dart @@ -0,0 +1,69 @@ +import 'dart:convert'; + +import 'package:dio/dio.dart'; +import 'package:ebroker/utils/Extensions/extensions.dart'; + +class NetworkRequestInterseptor extends Interceptor { + int totalAPICallTimes = 0; + + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + Map map = {}; + + if (options.data != null) { + map = (Map.fromEntries((options.data ?? {} as FormData).fields) + ..addEntries(Iterable.castFrom((options.data as FormData).files))); + } + + // totalAPICallTimes++; + // ({ + // "URL": options.path, + // "Parameters": options.method == "POST" ? map : options.queryParameters, + // "Method": options.method, + // "_total_api_calls": totalAPICallTimes + // }).mlog("Request-API"); + // try { + // String prettyJsonEncode = _prettyJsonEncode(options.data); + // print("ENCODED ${json.encode(options.data)}"); + // print(prettyJsonEncode); + // } catch (e) { + // print("INTERSEPTOR ERROR $e"); + // } + + handler.next(options); + } + + @override + void onError(DioError err, ErrorInterceptorHandler handler) { + ({ + "URL": err.response?.requestOptions.path ?? "", + "Type": err.type, + "Error": err.error, + "Message": err.message, + }).mlog("API-Error"); + + handler.next(err); + } + + @override + void onResponse(Response response, ResponseInterceptorHandler handler) { + ({ + "URL": response.requestOptions.path, + "Method": response.requestOptions.method, + "status": response.statusCode, + "statusMessage": response.statusMessage, + "response": response.data, + }).mlog("Response-API"); + handler.next(response); + } +} + +String _prettyJsonEncode(dynamic data) { + try { + const encoder = JsonEncoder.withIndent(' '); + final jsonString = encoder.convert(data); + return jsonString; + } catch (e) { + return data.toString(); + } +} diff --git a/lib/utils/Network/Interseptors/throttelIntercepter.dart b/lib/utils/Network/Interseptors/throttelIntercepter.dart new file mode 100644 index 0000000..8d1e378 --- /dev/null +++ b/lib/utils/Network/Interseptors/throttelIntercepter.dart @@ -0,0 +1,49 @@ +import 'package:dio/dio.dart'; + +///In case there is rapidly API call request.. this will stop them +class ThrottleInterceptor extends Interceptor { + // Map to store the last request timestamp for each API endpoint + final Map _lastRequestTimestamps = {}; + + // Minimum time interval between requests (in milliseconds) + final int minInterval; + + ThrottleInterceptor({required this.minInterval}); + + @override + Future onRequest( + RequestOptions options, RequestInterceptorHandler handler) async { + // Get the key for the current API endpoint (you can customize this based on your needs) + String apiEndpointKey = options.path; + + // Check if the last request for this API endpoint was made within the specified interval + if (_isRequestThrottled(apiEndpointKey)) { + // Do not proceed with the request + handler.reject(DioError( + requestOptions: options, + error: 'Request throttled. Please wait before making another request.', + )); + return; + } + + // Store the current timestamp for this API endpoint + _lastRequestTimestamps[apiEndpointKey] = DateTime.now(); + + // Proceed with the request + handler.next(options); + } + + bool _isRequestThrottled(String apiEndpointKey) { + if (_lastRequestTimestamps.containsKey(apiEndpointKey)) { + DateTime lastRequestTime = _lastRequestTimestamps[apiEndpointKey]!; + DateTime currentTime = DateTime.now(); + int elapsedTime = currentTime.difference(lastRequestTime).inMilliseconds; + + // Check if the elapsed time is less than the minimum interval + return elapsedTime < minInterval; + } + + // No previous request for this API endpoint + return false; + } +} diff --git a/lib/utils/Network/apiCallTrigger.dart b/lib/utils/Network/apiCallTrigger.dart new file mode 100644 index 0000000..6d73eb6 --- /dev/null +++ b/lib/utils/Network/apiCallTrigger.dart @@ -0,0 +1,15 @@ +import 'dart:ui'; + +class APICallTrigger { + APICallTrigger._(); + static List _triggerableList = []; + static void onTrigger(VoidCallback fn) { + _triggerableList.add(fn); + } + + static void trigger() { + for (VoidCallback i in _triggerableList) { + i.call(); + } + } +} diff --git a/lib/utils/Network/networkAvailability.dart b/lib/utils/Network/networkAvailability.dart new file mode 100644 index 0000000..1cfec22 --- /dev/null +++ b/lib/utils/Network/networkAvailability.dart @@ -0,0 +1,16 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; + +class CheckInternet { + CheckInternet(); + static Connectivity connectivity = Connectivity(); + static Future check( + {required Function() onInternet, Function()? onNoInternet}) async { + ConnectivityResult connectivityResult = + await connectivity.checkConnectivity(); + if (connectivityResult == ConnectivityResult.none) { + onNoInternet?.call(); + } else { + onInternet.call(); + } + } +} diff --git a/lib/utils/Notification/awsomeNotification.dart b/lib/utils/Notification/awsomeNotification.dart new file mode 100644 index 0000000..0ed839f --- /dev/null +++ b/lib/utils/Notification/awsomeNotification.dart @@ -0,0 +1,213 @@ +// ignore_for_file: file_names + +import 'dart:async'; +import 'dart:math'; + +import 'package:awesome_notifications/awesome_notifications.dart'; +import 'package:ebroker/Ui/screens/chat/chat_screen.dart'; +import 'package:ebroker/app/routes.dart'; +import 'package:ebroker/data/Repositories/property_repository.dart'; +import 'package:ebroker/data/cubits/chatCubits/delete_message_cubit.dart'; +import 'package:ebroker/data/model/data_output.dart'; +import 'package:ebroker/data/model/property_model.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../data/cubits/chatCubits/load_chat_messages.dart'; +import '../constant.dart'; +import '../helper_utils.dart'; + +class LocalAwsomeNotification { + AwesomeNotifications notification = AwesomeNotifications(); + void init(BuildContext context) { + requestPermission(); + + notification.initialize( + null, + [ + NotificationChannel( + channelKey: Constant.notificationChannel, + channelName: 'Basic notifications', + channelDescription: 'Notification channel', + importance: NotificationImportance.Max, + ledColor: Colors.grey), + NotificationChannel( + channelKey: "Chat Notification", + channelName: 'Chat Notifications', + channelDescription: 'Chat Notifications', + importance: NotificationImportance.Max, + ledColor: Colors.grey) + ], + channelGroups: [], + ); + listenTap(context); + } + + void listenTap(BuildContext context) { + AwesomeNotifications().setListeners( + onNotificationCreatedMethod: + NotificationController.onNotificationCreatedMethod, + onDismissActionReceivedMethod: + NotificationController.onDismissActionReceivedMethod, + onNotificationDisplayedMethod: + NotificationController.onNotificationDisplayedMethod, + onActionReceivedMethod: NotificationController.onActionReceivedMethod, + ); + } + + createNotification( + {required RemoteMessage notificationData, required bool isLocked}) async { + try { + bool isChat = notificationData.data["type"] == "chat"; + + int chatId = int.parse(notificationData.data['sender_id']) + + int.parse(notificationData.data['property_id']); + + await notification.createNotification( + content: NotificationContent( + id: isChat ? chatId : Random().nextInt(5000), + title: notificationData.data["title"], + + // icon: AppIcons.aboutUs, + hideLargeIconOnExpand: true, + summary: notificationData.data["type"] == "chat" + ? "${notificationData.data['username']}" + : null, + locked: isLocked, + payload: Map.from(notificationData.data), + autoDismissible: true, + + body: notificationData.data["body"], + wakeUpScreen: true, + + notificationLayout: notificationData.data["type"] == "chat" + ? NotificationLayout.MessagingGroup + : NotificationLayout.Default, + groupKey: notificationData.data["id"], + channelKey: notificationData.data["type"] == "chat" + ? "Chat Notification" + : Constant.notificationChannel, + ), + ); + } catch (e) { + rethrow; + } + } + + Future requestPermission() async { + NotificationSettings notificationSettings = + await FirebaseMessaging.instance.getNotificationSettings(); + + if (notificationSettings.authorizationStatus == + AuthorizationStatus.notDetermined) { + await notification.requestPermissionToSendNotifications( + channelKey: Constant.notificationChannel, + permissions: [ + NotificationPermission.Alert, + NotificationPermission.Sound, + NotificationPermission.Badge, + NotificationPermission.Vibration, + NotificationPermission.Light + ], + ); + await notification.requestPermissionToSendNotifications( + channelKey: "Chat Notification", + permissions: [ + NotificationPermission.Alert, + NotificationPermission.Sound, + NotificationPermission.Badge, + NotificationPermission.Vibration, + NotificationPermission.Light + ], + ); + if (notificationSettings.authorizationStatus == + AuthorizationStatus.authorized || + notificationSettings.authorizationStatus == + AuthorizationStatus.provisional) {} + } else if (notificationSettings.authorizationStatus == + AuthorizationStatus.denied) { + return; + } + } +} + +class NotificationController { + /// Use this method to detect when a new notification or a schedule is created + @pragma("vm:entry-point") + static Future onNotificationCreatedMethod( + ReceivedNotification receivedNotification) async {} + + /// Use this method to detect every time that a new notification is displayed + @pragma("vm:entry-point") + static Future onNotificationDisplayedMethod( + ReceivedNotification receivedNotification) async {} + + /// Use this method to detect if the user dismissed a notification + @pragma("vm:entry-point") + static Future onDismissActionReceivedMethod( + ReceivedAction receivedAction) async {} + + /// Use this method to detect when the user taps on a notification or action button + @pragma("vm:entry-point") + static Future onActionReceivedMethod( + ReceivedAction receivedAction) async { + Map? payload = receivedAction.payload; + + if (payload?['type'] == "chat") { + var username = payload?['username']; + var propertyTitleImage = payload?['property_title_image']; + var propertyTitle = payload?['title']; + var userProfile = payload?['user_profile']; + var senderId = payload?['sender_id']; + var propertyId = payload?['property_id']; + Future.delayed( + Duration.zero, + () { + Navigator.push(Constant.navigatorKey.currentContext!, + MaterialPageRoute( + builder: (context) { + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => LoadChatMessagesCubit(), + ), + BlocProvider( + create: (context) => DeleteMessageCubit(), + ), + ], + child: Builder(builder: (context) { + return ChatScreen( + profilePicture: userProfile!, + userName: username ?? "", + propertyImage: propertyTitleImage ?? "", + proeprtyTitle: propertyTitle ?? "", + userId: senderId ?? "", + propertyId: propertyId ?? ""); + }), + ); + }, + )); + }, + ); + } else { + String id = receivedAction.payload?["id"] ?? ""; + + DataOutput property = + await PropertyRepository().fetchPropertyFromPropertyId(id); + + Future.delayed( + Duration.zero, + () { + HelperUtils.goToNextPage(Routes.propertyDetails, + Constant.navigatorKey.currentContext!, false, + args: { + 'propertyData': property.modelList[0], + 'propertiesList': property.modelList, + 'fromMyProperty': false, + }); + }, + ); + } + } +} diff --git a/lib/utils/Notification/chat_message_handler.dart b/lib/utils/Notification/chat_message_handler.dart new file mode 100644 index 0000000..7d5c81f --- /dev/null +++ b/lib/utils/Notification/chat_message_handler.dart @@ -0,0 +1,146 @@ +import 'dart:async'; +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../Ui/screens/chat/chatAudio/widgets/chat_widget.dart'; +import '../Extensions/extensions.dart'; +import '../ui_utils.dart'; + +int sentMessages = 0; + +class ChatMessageHandlerOLD { + static List messages = []; + static final List _chat = []; + static final StreamController> _chatMessageStream = + StreamController>.broadcast(); + + static void add(chat) { + List msgs = (messages); + + _chat.insert(0, chat); + + ///don't change this line + msgs = [..._chat, ...msgs]; + _chatMessageStream.sink.add(msgs); + } + + static void loadMessages(List chats, BuildContext context) { + List messagesWithDate = []; + String previousDate = ""; + // Get the current date and time + DateTime now = DateTime.now(); + DateTime today = DateTime(now.year, now.month, now.day); + DateTime yesterday = today.subtract(const Duration(days: 1)); + + for (int i = chats.length - 1; i >= 0; i--) { + DateTime date = DateTime.parse((chats[i] as ChatMessage).time).toLocal(); + String formattedDate; + + if (date.isAfter(today)) { + formattedDate = UiUtils.translate(context, "today"); + } else if (date.isAfter(yesterday)) { + formattedDate = UiUtils.translate(context, "yesterday"); + } else { + formattedDate = (date.toString()).formatDate(); + } + + // Add date widget if date has changed + if (formattedDate != previousDate) { + messagesWithDate.insert(0, messageDateChip(context, formattedDate)); + previousDate = formattedDate; + } + + // Add message widget + messagesWithDate.insert(0, chats[i]); + } + + // Update the messages list and sink the new messages to the stream + messages = messagesWithDate; + // messages = chats; //uncomment and comment above code if problem in chat + _chatMessageStream.sink.add(messages); + } + + static Widget messageDateChip(BuildContext context, String formattedDate) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Center( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(7), + color: context.color.tertiaryColor.withOpacity(0.3)), + child: Padding( + padding: const EdgeInsets.all(5.0), + child: Text(formattedDate), + ), + )), + ); + } + + static void flushMessages() { + messages.clear(); + _chat.clear(); + } + + static Stream getChatStream() { + return _chatMessageStream.stream; + } + + static attachListener(void Function(dynamic)? onData) { + _chatMessageStream.stream.listen(onData); + } + + static removeMessage(int id) { + List msgs = (messages); + msgs.removeWhere((element) { + if (element is! Padding) { + return ((element as ChatMessage).key as ValueKey).value == id; + } + return false; + }); + + _chatMessageStream.sink.add(msgs); + } + + ///This will replace message's key with server key so we will be able to delete message if we want + static updateMessageId(String identifier, int id) { + try { + List msgs = _chat; + for (var i = 0; i < _chat.length; i++) { + //We will only need to change its key when it is bloc provider because we added it locally and its key was also locally so we have to replace it with server key when message send complete + if (msgs[i] is BlocProvider) { + ///Extracting chate message from bloc provider + Widget? bloc = (msgs[i] as BlocProvider).child; + ChatMessage chat = (bloc as ChatMessage); + + ///Extracting its key [which we were added locally] + String chatKey = (chat.key as ValueKey).value; + + ///This identifier will come from ChatMessage's key when message send success. + ///this identifier must be same as chatKey because we want exact element to change + if (identifier == chatKey) { + ///Converting chat class to map and replace its key and again convert it to ChatMessage class + var map = chat.toMap(); + map['key'] = ValueKey(id); + + try { + ChatMessage chatMessage = ChatMessage.fromMap(map); + + ///Replace it with old one + _chat[i] = chatMessage; + } catch (e) { + log("THIS IS ERRRORR $e"); + } + + ///This will add chats in first and old messages in last... + msgs = [..._chat, ...messages]; + _chatMessageStream.sink.add(msgs); + } + } + } + } catch (e) { + log("ERROS IS AT $e"); + } + } +} diff --git a/lib/utils/Notification/notification_service.dart b/lib/utils/Notification/notification_service.dart new file mode 100644 index 0000000..116af2c --- /dev/null +++ b/lib/utils/Notification/notification_service.dart @@ -0,0 +1,217 @@ +// ignore_for_file: file_names + +import 'dart:async'; +import 'dart:developer'; + +import 'package:ebroker/Ui/screens/ChatNew/MessageTypes/registerar.dart'; +import 'package:ebroker/data/model/chat/chated_user_model.dart'; +import 'package:ebroker/utils/Notification/chat_message_handler.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../Ui/screens/ChatNew/model.dart'; +import '../../Ui/screens/chat/chat_screen.dart'; +import '../../app/routes.dart'; +import '../../data/Repositories/property_repository.dart'; +import '../../data/cubits/chatCubits/get_chat_users.dart'; +import '../../data/cubits/chatCubits/load_chat_messages.dart'; +import '../../data/model/data_output.dart'; +import '../../data/model/property_model.dart'; +import '../constant.dart'; +import '../helper_utils.dart'; +import 'awsomeNotification.dart'; + +String currentlyChatingWith = ""; +String currentlyChatPropertyId = ""; + +class NotificationService { + static FirebaseMessaging messagingInstance = FirebaseMessaging.instance; + + static LocalAwsomeNotification localNotification = LocalAwsomeNotification(); + + static late StreamSubscription foregroundStream; + static late StreamSubscription onMessageOpen; + static requestPermission() async {} + + void updateFCM() async { + await FirebaseMessaging.instance.getToken(); + // await Api.post( + // // url: Api.updateFCMId, + // parameter: {Api.fcmId: token}, + // useAuthToken: true); + } + + static handleNotification(RemoteMessage? message, [BuildContext? context]) { + var notificationType = message?.data['type'] ?? ""; + + log("@notificaiton data is ${message?.data}"); + + if (notificationType == "chat") { + var senderId = message?.data['sender_id'] ?? ""; + var chatMessage = message?.data['message'] ?? ""; + var attachment = message?.data['file'] ?? ""; + var audioMessage = message?.data['audio'] ?? ""; + var time = message?.data['date'] ?? ""; + var username = message!.data['username']; + var propertyTitleImage = message.data['property_title_image']; + var propertyTitle = message.data['title']; + var userProfile = message.data['user_profile']; + var propertyId = message.data['property_id']; + + (context as BuildContext).read().addNewChat(ChatedUser( + fcmId: "", + firebaseId: "", + name: username, + profile: userProfile, + propertyId: + (propertyId is int) ? propertyId : int.parse(propertyId), + title: propertyTitle, + userId: (senderId is int) ? senderId : int.parse(senderId), + titleImage: propertyTitleImage, + )); + + ///Checking if this is user we are chatiing with + if (senderId == currentlyChatingWith && + propertyId == currentlyChatPropertyId) { + ChatMessageModel chatMessageModel = + ChatMessageModel.fromJson(message.data); + chatMessageModel.setIsSentByMe(false); + chatMessageModel.setIsSentNow(false); + ChatMessageHandler.add(chatMessageModel); + // ChatMessageHandlerOLD.add( + // Builder(builder: (context) { + // return ChatMessage( + // key: ValueKey(DateTime.now().toString().toString()), + // message: chatMessage, + // isSentByMe: false, + // propertyId: "", + // reciverId: "", + // isChatAudio: audioMessage != null && audioMessage != "", + // audioFile: audioMessage, + // attachment: attachment, + // hasAttachment: attachment != "" && attachment != null, + // senderId: senderId, + // time: time, + // ); + // }), + // ); + totalMessageCount++; + } else { + localNotification.createNotification( + isLocked: false, + notificationData: message, + ); + } + } else if (notificationType == "delete_message") { + ChatMessageHandlerOLD.removeMessage( + int.parse(message!.data['message_id'])); + } else { + localNotification.createNotification( + isLocked: false, notificationData: message!); + } + } + + static void init(context) { + requestPermission(); + registerListeners(context); + } + + static Future onBackgroundMessageHandler(RemoteMessage message) async { + if (message.notification == null) { + handleNotification( + message, + ); + } + } + + static forgroundNotificationHandler(BuildContext context) async { + foregroundStream = + FirebaseMessaging.onMessage.listen((RemoteMessage message) { + handleNotification(message, context); + }); + } + + static terminatedStateNotificationHandler(BuildContext context) { + FirebaseMessaging.instance.getInitialMessage().then( + (RemoteMessage? message) { + if (message == null) { + return; + } + if (message.notification == null) { + handleNotification(message, context); + } + }, + ); + } + + static void onTapNotificationHandler(context) { + onMessageOpen = FirebaseMessaging.onMessageOpenedApp + .listen((RemoteMessage message) async { + if (message.data['type'] == "chat") { + var username = message.data['title']; + var propertyTitleImage = message.data['property_title_image']; + var propertyTitle = message.data['property_title']; + var userProfile = message.data['user_profile']; + var senderId = message.data['sender_id']; + var propertyId = message.data['property_id']; + Future.delayed( + Duration.zero, + () { + Navigator.push(Constant.navigatorKey.currentContext!, + MaterialPageRoute( + builder: (context) { + return BlocProvider( + create: (context) { + return LoadChatMessagesCubit(); + }, + child: Builder(builder: (context) { + return ChatScreen( + profilePicture: userProfile ?? "", + userName: username ?? "", + propertyImage: propertyTitleImage ?? "", + proeprtyTitle: propertyTitle ?? "", + userId: senderId ?? "", + propertyId: propertyId ?? "", + ); + }), + ); + }, + )); + }, + ); + } else { + String id = message.data["id"] ?? ""; + DataOutput property = + await PropertyRepository().fetchPropertyFromPropertyId(id); + Future.delayed(Duration.zero, () { + HelperUtils.goToNextPage(Routes.propertyDetails, + Constant.navigatorKey.currentContext!, false, + args: { + 'propertyData': property.modelList[0], + 'propertiesList': property.modelList, + 'fromMyProperty': false, + }); + }); + } + } + // if (message.data["screen"] == "profile") { + // Navigator.pushNamed(context, profileRoute); + // } + + ); + } + + static Future registerListeners(context) async { + FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( + alert: true, badge: true, sound: true); + await forgroundNotificationHandler(context); + await terminatedStateNotificationHandler(context); + onTapNotificationHandler(context); + } + + static void disposeListeners() { + onMessageOpen.cancel(); + foregroundStream.cancel(); + } +} diff --git a/lib/utils/VideoPlayer/video_player_widget.dart b/lib/utils/VideoPlayer/video_player_widget.dart new file mode 100644 index 0000000..6e38172 --- /dev/null +++ b/lib/utils/VideoPlayer/video_player_widget.dart @@ -0,0 +1,88 @@ +import 'package:ebroker/utils/VideoPlayer/ytb.dart'; +import 'package:ebroker/utils/helper_utils.dart'; +import 'package:flick_video_player/flick_video_player.dart'; +import 'package:flutter/material.dart'; +import 'package:video_player/video_player.dart'; +import 'package:youtube_explode_dart/youtube_explode_dart.dart'; + +class VideoPlayerWideget extends StatefulWidget { + final String url; + final EdgeInsetsGeometry? padding; + const VideoPlayerWideget({super.key, required this.url, this.padding}); + + @override + State createState() => _VideoPlayerWidegetState(); +} + +class _VideoPlayerWidegetState extends State { + YoutubeExplode youtubeExplode = YoutubeExplode(); + String? url; + + getYoutubeVideo(url) async { + try { + Video video = await youtubeExplode.videos.get(url); + StreamManifest manifest = + await youtubeExplode.videos.streams.getManifest(video.id.value); + + MuxedStreamInfo videoQuality = + manifest.muxed.sortByVideoQuality().bestQuality; + print("QUALITY IS ${videoQuality.url}"); + url = videoQuality.url.toString(); + _flickmanager = FlickManager( + autoPlay: false, + videoPlayerController: + VideoPlayerController.networkUrl(videoQuality.url)); + setState(() {}); + } catch (e) { + print("ISSU IS $e"); + } + } + + Future getYoutubeVideoQualityUrls() async { + Video? video = + await YtbRepo().getVideoMetadata("https://youtu.be/lSf5ThEETPk"); + + List list = await YtbRepo().getMuxed(video.id.value); + print("List map is $list"); + } + + bool isYoutube(String url) { + return HelperUtils.isYoutubeVideo(url); + } + + FlickManager? _flickmanager; + @override + void initState() { + if (isYoutube(widget.url)) { + getYoutubeVideo(widget.url); + } else { + _flickmanager = FlickManager( + autoPlay: false, + videoPlayerController: + VideoPlayerController.networkUrl(Uri.parse(widget.url))); + setState(() {}); + } + super.initState(); + } + + @override + void dispose() { + _flickmanager?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (_flickmanager != null) { + return Padding( + padding: widget.padding ?? EdgeInsets.zero, + child: FlickVideoPlayer( + flickManager: _flickmanager!, + ), + ); + } + return Container( + height: 0, + ); + } +} diff --git a/lib/utils/VideoPlayer/ytb.dart b/lib/utils/VideoPlayer/ytb.dart new file mode 100644 index 0000000..a0cb7fb --- /dev/null +++ b/lib/utils/VideoPlayer/ytb.dart @@ -0,0 +1,44 @@ +import 'package:youtube_explode_dart/youtube_explode_dart.dart'; + +class YtbRepo { + var yt = YoutubeExplode(); + Future