diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index 3dedfb3..d20b5c8 100644 --- a/.flutter-plugins-dependencies +++ b/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"file_selector_ios","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_ios-0.5.3+2\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_pdfview","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_pdfview-1.4.3\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_ios","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_ios-0.8.13\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-9.0.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pusher_channels_flutter","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\pusher_channels_flutter-2.5.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_ios","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_ios-6.3.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"video_player_avfoundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\video_player_avfoundation-2.8.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"wakelock_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_plus-1.4.0\\\\","native_build":true,"dependencies":["package_info_plus"],"dev_dependency":false}],"android":[{"name":"file_selector_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_android-0.5.1+17\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_pdfview","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_pdfview-1.4.3\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_plugin_android_lifecycle","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_plugin_android_lifecycle-2.0.31\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_android-0.8.13+1\\\\","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"package_info_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-9.0.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.2.19\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pusher_channels_flutter","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\pusher_channels_flutter-2.5.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_android-2.4.13\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_android-6.3.20\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"video_player_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\video_player_android-2.8.15\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"wakelock_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_plus-1.4.0\\\\","native_build":true,"dependencies":["package_info_plus"],"dev_dependency":false}],"macos":[{"name":"file_selector_macos","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_macos-0.9.4+4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_macos","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_macos-0.2.2\\\\","native_build":false,"dependencies":["file_selector_macos"],"dev_dependency":false},{"name":"package_info_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-9.0.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_macos","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_macos-3.2.3\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"video_player_avfoundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\video_player_avfoundation-2.8.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"wakelock_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_plus-1.4.0\\\\","native_build":true,"dependencies":["package_info_plus"],"dev_dependency":false}],"linux":[{"name":"file_selector_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_linux-0.9.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_linux-0.2.2\\\\","native_build":false,"dependencies":["file_selector_linux"],"dev_dependency":false},{"name":"package_info_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-9.0.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.2.1\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":false,"dependencies":["url_launcher_linux"],"dev_dependency":false},{"name":"shared_preferences_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_linux-2.4.1\\\\","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"url_launcher_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_linux-3.2.2\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"wakelock_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_plus-1.4.0\\\\","native_build":false,"dependencies":["package_info_plus"],"dev_dependency":false}],"windows":[{"name":"file_selector_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_windows-0.9.3+5\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_windows-0.2.2\\\\","native_build":false,"dependencies":["file_selector_windows"],"dev_dependency":false},{"name":"package_info_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-9.0.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.3.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":["url_launcher_windows"],"dev_dependency":false},{"name":"shared_preferences_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_windows-2.4.1\\\\","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"url_launcher_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_windows-3.1.5\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"wakelock_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_plus-1.4.0\\\\","native_build":false,"dependencies":["package_info_plus"],"dev_dependency":false}],"web":[{"name":"file_selector_web","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_web-0.9.4+2\\\\","dependencies":[],"dev_dependency":false},{"name":"image_picker_for_web","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_for_web-3.1.1\\\\","dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-9.0.0\\\\","dependencies":[],"dev_dependency":false},{"name":"pusher_channels_flutter","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\pusher_channels_flutter-2.5.0\\\\","dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","dependencies":["url_launcher_web"],"dev_dependency":false},{"name":"shared_preferences_web","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_web-2.4.3\\\\","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_web-2.4.1\\\\","dependencies":[],"dev_dependency":false},{"name":"video_player_web","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\video_player_web-2.4.0\\\\","dependencies":[],"dev_dependency":false},{"name":"wakelock_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_plus-1.4.0\\\\","dependencies":["package_info_plus"],"dev_dependency":false}]},"dependencyGraph":[{"name":"file_selector","dependencies":["file_selector_android","file_selector_ios","file_selector_linux","file_selector_macos","file_selector_web","file_selector_windows"]},{"name":"file_selector_android","dependencies":[]},{"name":"file_selector_ios","dependencies":[]},{"name":"file_selector_linux","dependencies":[]},{"name":"file_selector_macos","dependencies":[]},{"name":"file_selector_web","dependencies":[]},{"name":"file_selector_windows","dependencies":[]},{"name":"flutter_pdfview","dependencies":[]},{"name":"flutter_plugin_android_lifecycle","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":"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":"pusher_channels_flutter","dependencies":[]},{"name":"share_plus","dependencies":["url_launcher_web","url_launcher_windows","url_launcher_linux"]},{"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":"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_plus","dependencies":["package_info_plus"]}],"date_created":"2025-12-16 10:21:13.675480","version":"3.32.2","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"file_selector_ios","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_ios-0.5.3+2\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_pdfview","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_pdfview-1.4.3\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_ios","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_ios-0.8.13\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-9.0.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pusher_channels_flutter","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\pusher_channels_flutter-2.5.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_ios","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_ios-6.3.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"video_player_avfoundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\video_player_avfoundation-2.8.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"wakelock_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_plus-1.4.0\\\\","native_build":true,"dependencies":["package_info_plus"],"dev_dependency":false}],"android":[{"name":"file_selector_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_android-0.5.1+17\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_pdfview","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_pdfview-1.4.3\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_plugin_android_lifecycle","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_plugin_android_lifecycle-2.0.31\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_android-0.8.13+1\\\\","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"package_info_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-9.0.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.2.19\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pusher_channels_flutter","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\pusher_channels_flutter-2.5.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_android-2.4.13\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_android-6.3.20\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"video_player_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\video_player_android-2.8.15\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"wakelock_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_plus-1.4.0\\\\","native_build":true,"dependencies":["package_info_plus"],"dev_dependency":false}],"macos":[{"name":"file_selector_macos","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_macos-0.9.4+4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_macos","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_macos-0.2.2\\\\","native_build":false,"dependencies":["file_selector_macos"],"dev_dependency":false},{"name":"package_info_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-9.0.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_macos","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_macos-3.2.3\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"video_player_avfoundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\video_player_avfoundation-2.8.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"wakelock_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_plus-1.4.0\\\\","native_build":true,"dependencies":["package_info_plus"],"dev_dependency":false}],"linux":[{"name":"file_selector_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_linux-0.9.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_linux-0.2.2\\\\","native_build":false,"dependencies":["file_selector_linux"],"dev_dependency":false},{"name":"package_info_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-9.0.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.2.1\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":false,"dependencies":["url_launcher_linux"],"dev_dependency":false},{"name":"shared_preferences_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_linux-2.4.1\\\\","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"url_launcher_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_linux-3.2.2\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"wakelock_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_plus-1.4.0\\\\","native_build":false,"dependencies":["package_info_plus"],"dev_dependency":false}],"windows":[{"name":"file_selector_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_windows-0.9.3+5\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_windows-0.2.2\\\\","native_build":false,"dependencies":["file_selector_windows"],"dev_dependency":false},{"name":"package_info_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-9.0.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.3.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":["url_launcher_windows"],"dev_dependency":false},{"name":"shared_preferences_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_windows-2.4.1\\\\","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"url_launcher_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_windows-3.1.5\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"wakelock_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_plus-1.4.0\\\\","native_build":false,"dependencies":["package_info_plus"],"dev_dependency":false}],"web":[{"name":"file_selector_web","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_web-0.9.4+2\\\\","dependencies":[],"dev_dependency":false},{"name":"image_picker_for_web","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_for_web-3.1.1\\\\","dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\package_info_plus-9.0.0\\\\","dependencies":[],"dev_dependency":false},{"name":"pusher_channels_flutter","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\pusher_channels_flutter-2.5.0\\\\","dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","dependencies":["url_launcher_web"],"dev_dependency":false},{"name":"shared_preferences_web","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_web-2.4.3\\\\","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_web-2.4.1\\\\","dependencies":[],"dev_dependency":false},{"name":"video_player_web","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\video_player_web-2.4.0\\\\","dependencies":[],"dev_dependency":false},{"name":"wakelock_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\wakelock_plus-1.4.0\\\\","dependencies":["package_info_plus"],"dev_dependency":false}]},"dependencyGraph":[{"name":"file_selector","dependencies":["file_selector_android","file_selector_ios","file_selector_linux","file_selector_macos","file_selector_web","file_selector_windows"]},{"name":"file_selector_android","dependencies":[]},{"name":"file_selector_ios","dependencies":[]},{"name":"file_selector_linux","dependencies":[]},{"name":"file_selector_macos","dependencies":[]},{"name":"file_selector_web","dependencies":[]},{"name":"file_selector_windows","dependencies":[]},{"name":"flutter_pdfview","dependencies":[]},{"name":"flutter_plugin_android_lifecycle","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":"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":"pusher_channels_flutter","dependencies":[]},{"name":"share_plus","dependencies":["url_launcher_web","url_launcher_windows","url_launcher_linux"]},{"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":"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_plus","dependencies":["package_info_plus"]}],"date_created":"2025-12-17 19:20:30.438103","version":"3.32.2","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 41e02bd..ca1dfc7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -5,6 +5,10 @@ + + + + if (call.method == "scanFile") { + val path = call.argument("path") + + if (path != null) { + MediaScannerConnection.scanFile( + applicationContext, + arrayOf(path), + null, + null + ) + } + + result.success(null) + } else { + result.notImplemented() + } + } + } +} diff --git a/lib/config/api_config.dart b/lib/config/api_config.dart index b132eff..9caab88 100644 --- a/lib/config/api_config.dart +++ b/lib/config/api_config.dart @@ -1,3 +1,3 @@ class ApiConfig { - static const String baseUrl = "http://10.11.236.74:8000/api"; + static const String baseUrl = "http://10.119.0.74:8000/api"; } diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index c0b252c..0b526d4 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -6,7 +6,7 @@ class AppConfig { static const String logoUrlEmulator = "http://10.0.2.2:8000/images/kent_logo2.png"; // For Physical Device (Replace with your actual PC local IP) - static const String logoUrlDevice = "http://10.11.236.74:8000/images/kent_logo2.png"; + static const String logoUrlDevice = "http://10.119.0.74:8000/images/kent_logo2.png"; // Which one to use? static const String logoUrl = logoUrlDevice; // CHANGE THIS WHEN TESTING ON REAL DEVICE diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart index dfd2a51..88c52d2 100644 --- a/lib/screens/chat_screen.dart +++ b/lib/screens/chat_screen.dart @@ -24,6 +24,7 @@ class ChatScreen extends StatefulWidget { class _ChatScreenState extends State { final TextEditingController _messageCtrl = TextEditingController(); final ScrollController _scrollCtrl = ScrollController(); + Map? uploadingMessage; late ChatService _chatService; final ReverbSocketService _socket = ReverbSocketService(); @@ -48,18 +49,54 @@ class _ChatScreenState extends State { _initChat(); } + String _guessMimeType(String path) { + final lower = path.toLowerCase(); + if (lower.endsWith('.jpg') || lower.endsWith('.png')) return 'image/*'; + if (lower.endsWith('.mp4')) return 'video/*'; + if (lower.endsWith('.pdf')) return 'application/pdf'; + return 'application/octet-stream'; + } Future _pickAndSendFile() async { - final XFile? file = await openFile(); + final XFile? picked = await openFile(); + if (picked == null || ticketId == null) return; - if (file == null || ticketId == null) return; + final file = File(picked.path); - final dartFile = File(file.path); + // 1️⃣ Show uploading UI + setState(() { + uploadingMessage = { + 'local_file': file, + 'file_type': _guessMimeType(file.path), + 'progress': 0.0, + }; + }); - await _chatService.sendFile(ticketId!, dartFile); + // 2️⃣ Upload (NO adding message) + await _chatService.sendFile( + ticketId!, + file, + onProgress: (progress) { + if (!mounted) return; + setState(() { + uploadingMessage!['progress'] = progress; + }); + }, + ); + + // 3️⃣ Remove sending bubble ONLY + if (!mounted) return; + setState(() { + uploadingMessage = null; + }); + + // 🚫 DO NOT add message here + // WebSocket will handle it } + + // ============================ // INIT CHAT // ============================ @@ -77,10 +114,23 @@ class _ChatScreenState extends State { context: context, ticketId: ticketId!, onMessage: (msg) { - if (!mounted) return; - setState(() => messages.add(msg)); + final incomingClientId = msg['client_id']; + + setState(() { + // 🧹 Remove local temp message with same client_id + messages.removeWhere( + (m) => m['client_id'] != null && + m['client_id'] == incomingClientId, + ); + + // ✅ Add confirmed socket message + messages.add(msg); + }); + _scrollToBottom(); }, + + onAdminMessage: () { if (!mounted) { context.read().increment(); @@ -112,16 +162,35 @@ class _ChatScreenState extends State { // ============================ Future _sendMessage() async { final text = _messageCtrl.text.trim(); - if (text.isEmpty) return; + if (text.isEmpty || ticketId == null) return; _messageCtrl.clear(); + final clientId = DateTime.now().millisecondsSinceEpoch.toString(); + + // 1️⃣ ADD LOCAL MESSAGE IMMEDIATELY + setState(() { + messages.add({ + 'client_id': clientId, + 'sender_type': 'App\\Models\\User', + 'message': text, + 'file_path': null, + 'file_type': null, + 'sending': true, + }); + }); + + _scrollToBottom(); + + // 2️⃣ SEND TO SERVER await _chatService.sendMessage( ticketId!, message: text, + clientId: clientId, ); } + // ============================ // DISPOSE // ============================ @@ -226,47 +295,83 @@ class _ChatScreenState extends State { Widget _buildMessages() { - return ListView.builder( + return ListView( controller: _scrollCtrl, padding: const EdgeInsets.all(12), - itemCount: messages.length, - itemBuilder: (_, index) { - final msg = messages[index]; - final isUser = msg['sender_type'] == 'App\\Models\\User'; + children: [ + // EXISTING MESSAGES + ...messages.map((msg) { + final isUser = msg['sender_type'] == 'App\\Models\\User'; - final String? filePath = msg['file_path']; - final String? fileType = msg['file_type']; - final String? message = msg['message']; - - return Align( - alignment: isUser ? Alignment.centerRight : Alignment.centerLeft, - child: Container( - margin: const EdgeInsets.symmetric(vertical: 6), - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: isUser ? Colors.blue : Colors.grey.shade300, - borderRadius: BorderRadius.circular(12), - ), - - // 🔽 ONLY THIS PART CHANGED - child: filePath == null - ? Text( - message ?? '', - style: TextStyle( - color: isUser ? Colors.white : Colors.black, + return Align( + alignment: isUser ? Alignment.centerRight : Alignment.centerLeft, + child: Container( + margin: const EdgeInsets.symmetric(vertical: 6), + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: isUser ? Colors.blue : Colors.grey.shade300, + borderRadius: BorderRadius.circular(12), + ), + child: msg['file_path'] == null + ? Text( + msg['message'] ?? '', + style: TextStyle( + color: isUser ? Colors.white : Colors.black, + ), + ) + : ChatFilePreview( + filePath: msg['file_path'], + fileType: msg['file_type'] ?? '', + isUser: isUser, + ), + ), + ); + }), + + // ⏳ UPLOADING MESSAGE + if (uploadingMessage != null) + Align( + alignment: Alignment.centerRight, + child: Container( + margin: const EdgeInsets.symmetric(vertical: 6), + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.circular(12), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ChatFilePreview( + filePath: uploadingMessage!['local_file'].path, + fileType: uploadingMessage!['file_type'], + isUser: true, + isLocal: true, + ), + const SizedBox(height: 8), + LinearProgressIndicator( + value: uploadingMessage!['progress'], + backgroundColor: Colors.white24, + valueColor: + const AlwaysStoppedAnimation(Colors.white), + ), + const SizedBox(height: 4), + const Text( + "Sending…", + style: TextStyle( + color: Colors.white, + fontSize: 12, + ), + ), + ], ), - ) - : ChatFilePreview( - filePath: filePath, - fileType: fileType ?? '', - isUser: isUser, ), ), - ); - }, + ], ); } + Widget _buildInput() { return SafeArea( child: Row( diff --git a/lib/services/chat_service.dart b/lib/services/chat_service.dart index dd08113..11452e6 100644 --- a/lib/services/chat_service.dart +++ b/lib/services/chat_service.dart @@ -24,27 +24,25 @@ class ChatService { Future sendMessage( int ticketId, { String? message, - String? filePath, + String? clientId, }) async { final form = FormData(); if (message != null) form.fields.add(MapEntry('message', message)); - if (filePath != null) { - form.files.add( - MapEntry( - 'file', - await MultipartFile.fromFile(filePath), - ), - ); - } + if (clientId != null) form.fields.add(MapEntry('client_id', clientId)); await dio.post('/user/chat/send/$ticketId', data: form); } + // --------------------------- // SEND FILE (image/video/pdf/excel) // --------------------------- - Future sendFile(int ticketId, File file) async { + Future> sendFile( + int ticketId, + File file, { + required Function(double) onProgress, + }) async { final formData = FormData.fromMap({ 'file': await MultipartFile.fromFile( file.path, @@ -52,13 +50,20 @@ class ChatService { ), }); - await dio.post( + final res = await dio.post( "/user/chat/send/$ticketId", - data: formData, options: Options( headers: {'Content-Type': 'multipart/form-data'}, ), + onSendProgress: (sent, total) { + if (total > 0) { + onProgress(sent / total); + } + }, ); + + return Map.from(res.data['message']); } + } diff --git a/lib/services/dio_client.dart b/lib/services/dio_client.dart index f683d49..672fcff 100644 --- a/lib/services/dio_client.dart +++ b/lib/services/dio_client.dart @@ -8,7 +8,7 @@ import 'token_interceptor.dart'; class DioClient { static Dio? _dio; - static const String baseUrl = "http://10.11.236.74:8000"; + static const String baseUrl = "http://10.119.0.74:8000"; static Dio getInstance(BuildContext context) { if (_dio == null) { diff --git a/lib/services/reverb_socket_service.dart b/lib/services/reverb_socket_service.dart index 188edad..dfc6bf6 100644 --- a/lib/services/reverb_socket_service.dart +++ b/lib/services/reverb_socket_service.dart @@ -36,7 +36,7 @@ class ReverbSocketService { _onAdminMessage = onAdminMessage; // 👈 SAVE final uri = Uri.parse( - 'ws://10.11.236.74:8080/app/q5fkk5rvcnatvbgadwvl' + 'ws://10.119.0.74:8080/app/q5fkk5rvcnatvbgadwvl' '?protocol=7&client=flutter&version=1.0', ); @@ -60,6 +60,7 @@ class ReverbSocketService { Future _handleMessage(dynamic raw) async { debugPrint("📥 RAW: $raw"); + final payload = jsonDecode(raw); final event = payload['event']?.toString() ?? ''; @@ -91,13 +92,15 @@ class ReverbSocketService { event == 'NewChatMessage' || event.endsWith('.NewChatMessage') || event.contains('NewChatMessage')) { + dynamic data = payload['data']; if (data is String) data = jsonDecode(data); final int msgId = data['id']; final String senderType = data['sender_type'] ?? ''; + final String? incomingClientId = data['client_id']; // ✅ HERE - // 🔁 Prevent duplicates + // 🔁 Prevent duplicates by DB id if (_receivedIds.contains(msgId)) { debugPrint("🔁 DUPLICATE MESSAGE IGNORED: $msgId"); return; @@ -106,20 +109,19 @@ class ReverbSocketService { debugPrint("📩 NEW MESSAGE"); debugPrint("🆔 id=$msgId"); + debugPrint("🧩 client_id=$incomingClientId"); debugPrint("👤 sender=$senderType"); debugPrint("💬 text=${data['message']}"); - // Always push message to UI + // ✅ Forward FULL payload (with client_id) to UI _onMessage(Map.from(data)); - // 🔔 Increment unread ONLY if ADMIN sent message - // 🔔 Increment unread ONLY if ADMIN sent message + // 🔔 Unread count only for admin messages if (senderType == 'App\\Models\\Admin') { debugPrint("🔔 ADMIN MESSAGE → UNREAD +1"); - _onAdminMessage(); // ✅ ACTUAL INCREMENT + _onAdminMessage(); } - return; } } diff --git a/lib/widgets/chat_file_actions.dart b/lib/widgets/chat_file_actions.dart new file mode 100644 index 0000000..8bf611a --- /dev/null +++ b/lib/widgets/chat_file_actions.dart @@ -0,0 +1,118 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/services.dart'; +import 'package:path_provider/path_provider.dart'; +import '../screens/chat_file_viewer.dart'; + +class ChatFileActions { + static void show( + BuildContext context, { + required String url, + required String fileType, + }) { + showModalBottomSheet( + context: context, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(16)), + ), + builder: (_) { + return SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _actionTile( + icon: Icons.open_in_new, + title: "Open", + onTap: () { + Navigator.pop(context); + ChatFileViewer.open( + context, + url: url, + fileType: fileType, + ); + }, + ), + _actionTile( + icon: Icons.download, + title: "Download", + onTap: () async { + Navigator.pop(context); + await _downloadFile(context, url, fileType); + + }, + ), + const SizedBox(height: 8), + ], + ), + ); + }, + ); + } + + static Widget _actionTile({ + required IconData icon, + required String title, + required VoidCallback onTap, + }) { + return ListTile( + leading: Icon(icon), + title: Text(title), + onTap: onTap, + ); + } + + + static Future _scanFile(String path) async { + const platform = MethodChannel('media_scanner'); + + try { + await platform.invokeMethod('scanFile', {'path': path}); + } catch (_) {} + } + + + // =========================== + // DOWNLOAD LOGIC + // =========================== + static Future _downloadFile( + BuildContext context, + String url, + String fileType, + ) async { + try { + final fileName = url.split('/').last; + + Directory baseDir; + + if (fileType.startsWith('image/')) { + baseDir = Directory('/storage/emulated/0/Pictures/KentChat'); + } else if (fileType.startsWith('video/')) { + baseDir = Directory('/storage/emulated/0/Movies/KentChat'); + } else { + baseDir = Directory('/storage/emulated/0/Download/KentChat'); + } + + if (!await baseDir.exists()) { + await baseDir.create(recursive: true); + } + + final filePath = "${baseDir.path}/$fileName"; + + await Dio().download(url, filePath); + + // 🔔 IMPORTANT: Tell Android to scan the file + await _scanFile(filePath); + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Saved to ${baseDir.path}")), + ); + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Download failed")), + ); + } + } + +} diff --git a/lib/widgets/chat_file_preview.dart b/lib/widgets/chat_file_preview.dart index 076a417..627453b 100644 --- a/lib/widgets/chat_file_preview.dart +++ b/lib/widgets/chat_file_preview.dart @@ -1,99 +1,118 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import '../services/dio_client.dart'; import '../screens/chat_file_viewer.dart'; +import '../widgets/chat_file_actions.dart'; class ChatFilePreview extends StatelessWidget { final String filePath; final String fileType; final bool isUser; + final bool isLocal; + const ChatFilePreview({ super.key, required this.filePath, required this.fileType, required this.isUser, + this.isLocal = false, }); + @override Widget build(BuildContext context) { final url = "${DioClient.baseUrl}/storage/$filePath"; final textColor = isUser ? Colors.white : Colors.black; - // IMAGE PREVIEW - if (fileType.startsWith('image/')) { - return GestureDetector( - onTap: () => ChatFileViewer.open( - context, - url: url, - fileType: fileType, - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(8), - child: Image.network( - url, - width: 180, - height: 120, - fit: BoxFit.cover, - ), - ), + // LOCAL IMAGE (uploading preview) + if (isLocal && fileType.startsWith('image/')) { + return Image.file( + File(filePath), + width: 180, + height: 120, + fit: BoxFit.cover, ); } - // VIDEO PREVIEW - if (fileType.startsWith('video/')) { - return GestureDetector( - onTap: () => ChatFileViewer.open( - context, - url: url, - fileType: fileType, - ), - child: Stack( - alignment: Alignment.center, - children: [ - Container( - width: 180, - height: 120, - decoration: BoxDecoration( - color: Colors.black26, - borderRadius: BorderRadius.circular(8), - ), - child: const Icon( - Icons.videocam, - size: 50, - color: Colors.white70, - ), - ), - const Icon( - Icons.play_circle_fill, - size: 56, - color: Colors.white, - ), - ], - ), - ); - } - - // PDF / FILE PREVIEW return GestureDetector( - onTap: () => ChatFileViewer.open( - context, - url: url, - fileType: fileType, - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(_fileIcon(fileType), color: textColor), - const SizedBox(width: 8), - Text( - _fileLabel(fileType), - style: TextStyle(color: textColor), - ), - ], - ), + onTap: () { + // ✅ TAP = OPEN + ChatFileViewer.open( + context, + url: url, + fileType: fileType, + ); + }, + onLongPress: () { + // ✅ LONG PRESS = OPEN / DOWNLOAD + ChatFileActions.show( + context, + url: url, + fileType: fileType, + ); + }, + child: _buildPreviewUI(textColor, url), ); } + Widget _buildPreviewUI(Color textColor, String url) { + // IMAGE + if (fileType.startsWith('image/')) { + return ClipRRect( + borderRadius: BorderRadius.circular(8), + child: Image.network( + url, + width: 180, + height: 120, + fit: BoxFit.cover, + ), + ); + } + + // VIDEO + if (fileType.startsWith('video/')) { + return Stack( + alignment: Alignment.center, + children: [ + Container( + width: 180, + height: 120, + decoration: BoxDecoration( + color: Colors.black26, + borderRadius: BorderRadius.circular(8), + ), + child: const Icon( + Icons.videocam, + size: 50, + color: Colors.white70, + ), + ), + const Icon( + Icons.play_circle_fill, + size: 56, + color: Colors.white, + ), + ], + ); + } + + // PDF / OTHER FILES + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(_fileIcon(fileType), color: textColor), + const SizedBox(width: 8), + Text( + _fileLabel(fileType), + style: TextStyle(color: textColor), + ), + ], + ); + } + + IconData _fileIcon(String type) { if (type == 'application/pdf') return Icons.picture_as_pdf; if (type.contains('excel')) return Icons.table_chart;