diff --git a/assets/icons/add_devices_icon.svg b/assets/icons/add_devices_icon.svg new file mode 100644 index 0000000..b7f0243 --- /dev/null +++ b/assets/icons/add_devices_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/add_scene_icon.svg b/assets/icons/add_scene_icon.svg new file mode 100644 index 0000000..0fb0b4a --- /dev/null +++ b/assets/icons/add_scene_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/add_switch_icon.svg b/assets/icons/add_switch_icon.svg new file mode 100644 index 0000000..7b39523 --- /dev/null +++ b/assets/icons/add_switch_icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/backlight_icon.svg b/assets/icons/backlight_icon.svg new file mode 100644 index 0000000..b4f5d91 --- /dev/null +++ b/assets/icons/backlight_icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/check_update_icon.svg b/assets/icons/check_update_icon.svg new file mode 100644 index 0000000..46527d3 --- /dev/null +++ b/assets/icons/check_update_icon.svg @@ -0,0 +1,22 @@ + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/create_group_icon.svg b/assets/icons/create_group_icon.svg new file mode 100644 index 0000000..00a8668 --- /dev/null +++ b/assets/icons/create_group_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/edit_name_setting.svg b/assets/icons/edit_name_setting.svg new file mode 100644 index 0000000..9011e82 --- /dev/null +++ b/assets/icons/edit_name_setting.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/edit_name_sos_icon.svg b/assets/icons/edit_name_sos_icon.svg new file mode 100644 index 0000000..c9e6a9e --- /dev/null +++ b/assets/icons/edit_name_sos_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/edit_sos_icon.svg b/assets/icons/edit_sos_icon.svg new file mode 100644 index 0000000..c9e6a9e --- /dev/null +++ b/assets/icons/edit_sos_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/empty_log.svg b/assets/icons/empty_log.svg new file mode 100644 index 0000000..8e847ac --- /dev/null +++ b/assets/icons/empty_log.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/icons/empty_update_icon.svg b/assets/icons/empty_update_icon.svg new file mode 100644 index 0000000..3cf26c9 --- /dev/null +++ b/assets/icons/empty_update_icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/faq_icon.svg b/assets/icons/faq_icon.svg new file mode 100644 index 0000000..394f26c --- /dev/null +++ b/assets/icons/faq_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/four_scene_home_icon.svg b/assets/icons/four_scene_home_icon.svg new file mode 100644 index 0000000..8251888 --- /dev/null +++ b/assets/icons/four_scene_home_icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/four_scene_icon.svg b/assets/icons/four_scene_icon.svg new file mode 100644 index 0000000..414d952 --- /dev/null +++ b/assets/icons/four_scene_icon.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/green_sos.svg b/assets/icons/green_sos.svg new file mode 100644 index 0000000..da225aa --- /dev/null +++ b/assets/icons/green_sos.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/info.svg b/assets/icons/info.svg new file mode 100644 index 0000000..8aafa02 --- /dev/null +++ b/assets/icons/info.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/minus_icon.svg b/assets/icons/minus_icon.svg new file mode 100644 index 0000000..5eb5323 --- /dev/null +++ b/assets/icons/minus_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/notification_icon.svg b/assets/icons/notification_icon.svg new file mode 100644 index 0000000..ed3d9d0 --- /dev/null +++ b/assets/icons/notification_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/red_sos.svg b/assets/icons/red_sos.svg new file mode 100644 index 0000000..c2f6966 --- /dev/null +++ b/assets/icons/red_sos.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/remove_scene_icon.svg b/assets/icons/remove_scene_icon.svg new file mode 100644 index 0000000..f190e01 --- /dev/null +++ b/assets/icons/remove_scene_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/share_icon.svg b/assets/icons/share_icon.svg new file mode 100644 index 0000000..a4b908f --- /dev/null +++ b/assets/icons/share_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/six_scene_home_icon.svg b/assets/icons/six_scene_home_icon.svg new file mode 100644 index 0000000..b4483f3 --- /dev/null +++ b/assets/icons/six_scene_home_icon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/icons/six_scene_icon.svg b/assets/icons/six_scene_icon.svg new file mode 100644 index 0000000..386e615 --- /dev/null +++ b/assets/icons/six_scene_icon.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/sos_home_icon.svg b/assets/icons/sos_home_icon.svg new file mode 100644 index 0000000..6f36bac --- /dev/null +++ b/assets/icons/sos_home_icon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/sos_profile_icon.svg b/assets/icons/sos_profile_icon.svg new file mode 100644 index 0000000..f99ec5b --- /dev/null +++ b/assets/icons/sos_profile_icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/switch_off.svg b/assets/icons/switch_off.svg new file mode 100644 index 0000000..0615df3 --- /dev/null +++ b/assets/icons/switch_off.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/switch_on.svg b/assets/icons/switch_on.svg new file mode 100644 index 0000000..d3b24ed --- /dev/null +++ b/assets/icons/switch_on.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/tap_run_icon.svg b/assets/icons/tap_run_icon.svg new file mode 100644 index 0000000..3518ace --- /dev/null +++ b/assets/icons/tap_run_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/thumb_down.svg b/assets/icons/thumb_down.svg new file mode 100644 index 0000000..4d22f00 --- /dev/null +++ b/assets/icons/thumb_down.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/thumb_up.svg b/assets/icons/thumb_up.svg new file mode 100644 index 0000000..5670b79 --- /dev/null +++ b/assets/icons/thumb_up.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/update_icon.svg b/assets/icons/update_icon.svg new file mode 100644 index 0000000..cf3e305 --- /dev/null +++ b/assets/icons/update_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 0e5c500..40f2b75 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -10,12 +10,12 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 611C662010675536F855E5CA /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 490AAF90B8FBFCC5BA996845 /* Pods_RunnerTests.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 964EC64D4BABF3375BEBF6DE /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB51EC18BE9E4FD7A688D262 /* Pods_RunnerTests.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - D31283674D2826D7EF8E56BC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25B37F5982CD6994FABA2CC1 /* Pods_Runner.framework */; }; + CE9CA504D8FF2965F977B16B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 274C82CD6955A1499B0B1ECC /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -42,24 +42,20 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 01BAAF935356ECBDD35AF0DB /* Pods-RunnerTests.debug-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug-prod.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug-prod.xcconfig"; sourceTree = ""; }; - 12AD49A621BEBB053FD06115 /* Pods-Runner.release-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-prod.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-prod.xcconfig"; sourceTree = ""; }; + 064BE0B8B723A6E30728B215 /* Pods-RunnerTests.release-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release-prod.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release-prod.xcconfig"; sourceTree = ""; }; + 0D60D6C4BBD804473BD9E4A0 /* Pods-RunnerTests.profile-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile-prod.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile-prod.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 1F99043C7AC9BDABD8A4D41A /* Pods-Runner.profile-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-dev.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-dev.xcconfig"; sourceTree = ""; }; - 210827A693936E5201C5E75C /* Pods-RunnerTests.profile-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile-dev.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile-dev.xcconfig"; sourceTree = ""; }; - 238CAAD9FFF9A0C9ED3CFAB2 /* Pods-RunnerTests.profile-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile-prod.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile-prod.xcconfig"; sourceTree = ""; }; - 25B37F5982CD6994FABA2CC1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2688A8D4C03F1C4585B3EFE2 /* Pods-Runner.release-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-dev.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-dev.xcconfig"; sourceTree = ""; }; + 274C82CD6955A1499B0B1ECC /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 444D77D28A8CDF32047CD0AF /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 490AAF90B8FBFCC5BA996845 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 5DE3E6D1EADE3D3859FC1B69 /* Pods-Runner.debug-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-dev.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-dev.xcconfig"; sourceTree = ""; }; + 4F6A2F89436864C7EE769652 /* Pods-RunnerTests.profile-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile-dev.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile-dev.xcconfig"; sourceTree = ""; }; + 54B9D4926B53AAFC49A54F19 /* Pods-Runner.release-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-prod.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-prod.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 949637473C534E1F68B19CC0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -67,16 +63,14 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9B82456986D7FA25420A224F /* Pods-Runner.debug-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-prod.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-prod.xcconfig"; sourceTree = ""; }; - A3D4DF5D9888DAC25E2380AA /* Pods-RunnerTests.release-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release-dev.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release-dev.xcconfig"; sourceTree = ""; }; - AAC9129FD50E64509AD1B9AF /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; - BFD4DDED98208034B60B5311 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - C41134CD2FDFC1A2BDF49283 /* Pods-Runner.release-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-dev.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-dev.xcconfig"; sourceTree = ""; }; - C5DBBF9417E4F8A9A08DFF02 /* Pods-RunnerTests.release-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release-prod.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release-prod.xcconfig"; sourceTree = ""; }; - D0F6245A5BF345FCC425515C /* Pods-Runner.profile-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-prod.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-prod.xcconfig"; sourceTree = ""; }; - DFB6BB492A265F2BF6FDC8C0 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - EE971EFEA60AEDFDB361D9A3 /* Pods-RunnerTests.debug-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug-dev.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug-dev.xcconfig"; sourceTree = ""; }; - F323D632CA976B68DDB0E669 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 9D1B204BC7CD29434FE9D537 /* Pods-Runner.profile-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-dev.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-dev.xcconfig"; sourceTree = ""; }; + C33EAABFBF3F560A8EFB0BC0 /* Pods-RunnerTests.release-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release-dev.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release-dev.xcconfig"; sourceTree = ""; }; + E0A467016A4C6B17ECA05534 /* Pods-Runner.profile-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-prod.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-prod.xcconfig"; sourceTree = ""; }; + E2512B7B8C737577EB9DB570 /* Pods-RunnerTests.debug-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug-dev.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug-dev.xcconfig"; sourceTree = ""; }; + F5E8A17F8AB7D50983179FD3 /* Pods-Runner.debug-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-dev.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-dev.xcconfig"; sourceTree = ""; }; + FA2359C69B2F150BE9833D5A /* Pods-RunnerTests.debug-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug-prod.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug-prod.xcconfig"; sourceTree = ""; }; + FB51EC18BE9E4FD7A688D262 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FC3EFA91747319965EF91609 /* Pods-Runner.debug-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-prod.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-prod.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -84,7 +78,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D31283674D2826D7EF8E56BC /* Pods_Runner.framework in Frameworks */, + CE9CA504D8FF2965F977B16B /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -92,7 +86,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 611C662010675536F855E5CA /* Pods_RunnerTests.framework in Frameworks */, + 964EC64D4BABF3375BEBF6DE /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -102,24 +96,18 @@ 2F70EB4341A83C900EB253DC /* Pods */ = { isa = PBXGroup; children = ( - BFD4DDED98208034B60B5311 /* Pods-Runner.debug.xcconfig */, - 949637473C534E1F68B19CC0 /* Pods-Runner.release.xcconfig */, - 444D77D28A8CDF32047CD0AF /* Pods-Runner.profile.xcconfig */, - DFB6BB492A265F2BF6FDC8C0 /* Pods-RunnerTests.debug.xcconfig */, - F323D632CA976B68DDB0E669 /* Pods-RunnerTests.release.xcconfig */, - AAC9129FD50E64509AD1B9AF /* Pods-RunnerTests.profile.xcconfig */, - 9B82456986D7FA25420A224F /* Pods-Runner.debug-prod.xcconfig */, - 5DE3E6D1EADE3D3859FC1B69 /* Pods-Runner.debug-dev.xcconfig */, - 12AD49A621BEBB053FD06115 /* Pods-Runner.release-prod.xcconfig */, - C41134CD2FDFC1A2BDF49283 /* Pods-Runner.release-dev.xcconfig */, - D0F6245A5BF345FCC425515C /* Pods-Runner.profile-prod.xcconfig */, - 1F99043C7AC9BDABD8A4D41A /* Pods-Runner.profile-dev.xcconfig */, - 01BAAF935356ECBDD35AF0DB /* Pods-RunnerTests.debug-prod.xcconfig */, - EE971EFEA60AEDFDB361D9A3 /* Pods-RunnerTests.debug-dev.xcconfig */, - C5DBBF9417E4F8A9A08DFF02 /* Pods-RunnerTests.release-prod.xcconfig */, - A3D4DF5D9888DAC25E2380AA /* Pods-RunnerTests.release-dev.xcconfig */, - 238CAAD9FFF9A0C9ED3CFAB2 /* Pods-RunnerTests.profile-prod.xcconfig */, - 210827A693936E5201C5E75C /* Pods-RunnerTests.profile-dev.xcconfig */, + FC3EFA91747319965EF91609 /* Pods-Runner.debug-prod.xcconfig */, + F5E8A17F8AB7D50983179FD3 /* Pods-Runner.debug-dev.xcconfig */, + 54B9D4926B53AAFC49A54F19 /* Pods-Runner.release-prod.xcconfig */, + 2688A8D4C03F1C4585B3EFE2 /* Pods-Runner.release-dev.xcconfig */, + E0A467016A4C6B17ECA05534 /* Pods-Runner.profile-prod.xcconfig */, + 9D1B204BC7CD29434FE9D537 /* Pods-Runner.profile-dev.xcconfig */, + FA2359C69B2F150BE9833D5A /* Pods-RunnerTests.debug-prod.xcconfig */, + E2512B7B8C737577EB9DB570 /* Pods-RunnerTests.debug-dev.xcconfig */, + 064BE0B8B723A6E30728B215 /* Pods-RunnerTests.release-prod.xcconfig */, + C33EAABFBF3F560A8EFB0BC0 /* Pods-RunnerTests.release-dev.xcconfig */, + 0D60D6C4BBD804473BD9E4A0 /* Pods-RunnerTests.profile-prod.xcconfig */, + 4F6A2F89436864C7EE769652 /* Pods-RunnerTests.profile-dev.xcconfig */, ); path = Pods; sourceTree = ""; @@ -132,11 +120,11 @@ path = RunnerTests; sourceTree = ""; }; - 876D3217A8BBDAF41961161F /* Frameworks */ = { + 61B46FA3FB0932D29E3C6E47 /* Frameworks */ = { isa = PBXGroup; children = ( - 25B37F5982CD6994FABA2CC1 /* Pods_Runner.framework */, - 490AAF90B8FBFCC5BA996845 /* Pods_RunnerTests.framework */, + 274C82CD6955A1499B0B1ECC /* Pods_Runner.framework */, + FB51EC18BE9E4FD7A688D262 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -160,7 +148,7 @@ 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, 2F70EB4341A83C900EB253DC /* Pods */, - 876D3217A8BBDAF41961161F /* Frameworks */, + 61B46FA3FB0932D29E3C6E47 /* Frameworks */, ); sourceTree = ""; }; @@ -195,7 +183,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - 3B971DE531245D7FD2921C30 /* [CP] Check Pods Manifest.lock */, + 73698F4EABFF3F9B7ADF4220 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, C2B33A7265AF659D80692473 /* Frameworks */, @@ -214,7 +202,7 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 3DC878D0674AA34AEC9695FB /* [CP] Check Pods Manifest.lock */, + BF71A0E0099ADA10FE9B580A /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, B07E4A152C9B8EA4001F6910 /* copy GoogleService.plist file to the correct location */, 97C146EA1CF9000F007C117D /* Sources */, @@ -222,9 +210,9 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 315A05630CF83C532DBBCBF2 /* [CP] Embed Pods Frameworks */, - 3724F7A126D8469D5B04D144 /* [CP] Copy Pods Resources */, 4768286A3BADB12BBB8C6996 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */, + 6CF71F42A45B39E9945C8410 /* [CP] Embed Pods Frameworks */, + 14DF593CA13D27D3781140F6 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -296,24 +284,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 315A05630CF83C532DBBCBF2 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 3724F7A126D8469D5B04D144 /* [CP] Copy Pods Resources */ = { + 14DF593CA13D27D3781140F6 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -346,7 +317,42 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 3B971DE531245D7FD2921C30 /* [CP] Check Pods Manifest.lock */ = { + 4768286A3BADB12BBB8C6996 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "FlutterFire: \"flutterfire upload-crashlytics-symbols\""; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\n#!/bin/bash\nPATH=${PATH}:$FLUTTER_ROOT/bin:$HOME/.pub-cache/bin\nflutterfire upload-crashlytics-symbols --upload-symbols-script-path=$PODS_ROOT/FirebaseCrashlytics/upload-symbols --platform=ios --apple-project-path=${SRCROOT} --env-platform-name=${PLATFORM_NAME} --env-configuration=${CONFIGURATION} --env-project-dir=${PROJECT_DIR} --env-built-products-dir=${BUILT_PRODUCTS_DIR} --env-dwarf-dsym-folder-path=${DWARF_DSYM_FOLDER_PATH} --env-dwarf-dsym-file-name=${DWARF_DSYM_FILE_NAME} --env-infoplist-path=${INFOPLIST_PATH} --default-config=default\n"; + }; + 6CF71F42A45B39E9945C8410 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 73698F4EABFF3F9B7ADF4220 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -368,46 +374,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 3DC878D0674AA34AEC9695FB /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 4768286A3BADB12BBB8C6996 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "FlutterFire: \"flutterfire upload-crashlytics-symbols\""; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\n#!/bin/bash\nPATH=${PATH}:$FLUTTER_ROOT/bin:$HOME/.pub-cache/bin\nflutterfire upload-crashlytics-symbols --upload-symbols-script-path=$PODS_ROOT/FirebaseCrashlytics/upload-symbols --platform=ios --apple-project-path=${SRCROOT} --env-platform-name=${PLATFORM_NAME} --env-configuration=${CONFIGURATION} --env-project-dir=${PROJECT_DIR} --env-built-products-dir=${BUILT_PRODUCTS_DIR} --env-dwarf-dsym-folder-path=${DWARF_DSYM_FOLDER_PATH} --env-dwarf-dsym-file-name=${DWARF_DSYM_FILE_NAME} --env-infoplist-path=${INFOPLIST_PATH} --default-config=default\n"; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -441,6 +407,28 @@ shellPath = /bin/sh; shellScript = "# Get a reference to the destination location for the GoogleService-Info.plist\n# This is the default location where Firebase init code expects to find GoogleServices-Info.plist file.\nPLIST_DESTINATION=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app\n# We have named our Build Configurations as Debug-dev, Debug-prod etc.\n# Here, dev and prod are the scheme names. This kind of naming is required by Flutter for flavors to work.\n# We are using the $CONFIGURATION variable available in the XCode build environment to get the build configuration.\nif [ \"${CONFIGURATION}\" == \"Debug-prod\" ] || [ \"${CONFIGURATION}\" == \"Release-prod\" ] || [ \"${CONFIGURATION}\" == \"Profile-prod\" ] || [ \"${CONFIGURATION}\" == \"Release\" ]; then\ncp \"${PROJECT_DIR}/config/prod/GoogleService-Info.plist\" \"${PLIST_DESTINATION}/GoogleService-Info.plist\"\necho \"Production plist copied\"\nelif [ \"${CONFIGURATION}\" == \"Debug-dev\" ] || [ \"${CONFIGURATION}\" == \"Release-dev\" ] || [ \"${CONFIGURATION}\" == \"Profile-dev\" ] || [ \"${CONFIGURATION}\" == \"Debug\" ]; then\ncp \"${PROJECT_DIR}/config/dev/GoogleService-Info.plist\" \"${PLIST_DESTINATION}/GoogleService-Info.plist\"\necho \"Development plist copied\"\nfi\n"; }; + BF71A0E0099ADA10FE9B580A /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -575,7 +563,7 @@ }; 331C8088294A63A400263BE5 /* Debug-prod */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 01BAAF935356ECBDD35AF0DB /* Pods-RunnerTests.debug-prod.xcconfig */; + baseConfigurationReference = FA2359C69B2F150BE9833D5A /* Pods-RunnerTests.debug-prod.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -593,7 +581,7 @@ }; 331C8089294A63A400263BE5 /* Release-prod */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C5DBBF9417E4F8A9A08DFF02 /* Pods-RunnerTests.release-prod.xcconfig */; + baseConfigurationReference = 064BE0B8B723A6E30728B215 /* Pods-RunnerTests.release-prod.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -609,7 +597,7 @@ }; 331C808A294A63A400263BE5 /* Profile-prod */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 238CAAD9FFF9A0C9ED3CFAB2 /* Pods-RunnerTests.profile-prod.xcconfig */; + baseConfigurationReference = 0D60D6C4BBD804473BD9E4A0 /* Pods-RunnerTests.profile-prod.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -885,7 +873,7 @@ }; B07E4A0E2C9B8C45001F6910 /* Debug-dev */ = { isa = XCBuildConfiguration; - baseConfigurationReference = EE971EFEA60AEDFDB361D9A3 /* Pods-RunnerTests.debug-dev.xcconfig */; + baseConfigurationReference = E2512B7B8C737577EB9DB570 /* Pods-RunnerTests.debug-dev.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -987,7 +975,7 @@ }; B07E4A112C9B8C52001F6910 /* Release-dev */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A3D4DF5D9888DAC25E2380AA /* Pods-RunnerTests.release-dev.xcconfig */; + baseConfigurationReference = C33EAABFBF3F560A8EFB0BC0 /* Pods-RunnerTests.release-dev.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -1085,7 +1073,7 @@ }; B07E4A142C9B8C5C001F6910 /* Profile-dev */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 210827A693936E5201C5E75C /* Pods-RunnerTests.profile-dev.xcconfig */; + baseConfigurationReference = 4F6A2F89436864C7EE769652 /* Pods-RunnerTests.profile-dev.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; diff --git a/lib/features/app_layout/bloc/home_cubit.dart b/lib/features/app_layout/bloc/home_cubit.dart index 625bab0..9e7b353 100644 --- a/lib/features/app_layout/bloc/home_cubit.dart +++ b/lib/features/app_layout/bloc/home_cubit.dart @@ -34,7 +34,7 @@ part 'home_state.dart'; class HomeCubit extends Cubit { HomeCubit._() : super(HomeInitial()) { - checkIfNotificationPermissionGranted(); + // checkIfNotificationPermissionGranted(); fetchUserInfo(); if (selectedSpace == null) { fetchUnitsByUserId(); @@ -55,7 +55,7 @@ class HomeCubit extends Cubit { Future fetchUserInfo() async { try { - var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); + var uuid=await const FlutterSecureStorage().read(key: UserModel.userUuidKey); user = await ProfileApi().fetchUserInfo(uuid); emit(HomeUserInfoLoaded(user!)); // Emit state after fetching user info } catch (e) { @@ -76,9 +76,12 @@ class HomeCubit extends Cubit { selectedSpace = null; selectedRoom = null; pageIndex = 0; - OneSignal.User.pushSubscription.removeObserver((stateChanges) => oneSignalSubscriptionObserver); - OneSignal.Notifications.removePermissionObserver((permission) => oneSignalPermissionObserver); - OneSignal.Notifications.removeClickListener((event) => oneSignalClickListenerObserver); + // OneSignal.User.pushSubscription + // .removeObserver((stateChanges) => oneSignalSubscriptionObserver); + // OneSignal.Notifications.removePermissionObserver( + // (permission) => oneSignalPermissionObserver); + // OneSignal.Notifications.removeClickListener( + // (event) => oneSignalClickListenerObserver); return super.close(); } @@ -96,9 +99,9 @@ class HomeCubit extends Cubit { var duration = const Duration(milliseconds: 300); - void oneSignalPermissionObserver; - void oneSignalSubscriptionObserver; - void oneSignalClickListenerObserver; + // void oneSignalPermissionObserver; + // void oneSignalSubscriptionObserver; + // void oneSignalClickListenerObserver; // selectSpace(SpaceModel space) async { // selectedSpace = space; @@ -120,31 +123,36 @@ class HomeCubit extends Cubit { return; } - var userUuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? ''; + var userUuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? + ''; if (userUuid.isNotEmpty) { await OneSignal.login(userUuid); } //Enable push notifications await OneSignal.User.pushSubscription.optIn(); - //this function will be called once a user is subscribed - oneSignalSubscriptionObserver = OneSignal.User.pushSubscription.addObserver((state) async { - if (state.current.optedIn) { - await _sendSubscriptionId(); - } - }); + // //this function will be called once a user is subscribed + // oneSignalSubscriptionObserver = + // OneSignal.User.pushSubscription.addObserver((state) async { + // if (state.current.optedIn) { + // await _sendSubscriptionId(); + // } + // }); - // Send the player id when a user allows notifications - oneSignalPermissionObserver = OneSignal.Notifications.addPermissionObserver((state) async { - await _sendSubscriptionId(); - }); + // // Send the player id when a user allows notifications + // oneSignalPermissionObserver = + // OneSignal.Notifications.addPermissionObserver((state) async { + // await _sendSubscriptionId(); + // }); - //check if the player id is sent, if not send it again - await _sendSubscriptionId(); + // //check if the player id is sent, if not send it again + // await _sendSubscriptionId(); - oneSignalClickListenerObserver = OneSignal.Notifications.addClickListener((event) async { - //Once the user clicks on the notification - }); + // oneSignalClickListenerObserver = + // OneSignal.Notifications.addClickListener((event) async { + // //Once the user clicks on the notification + // }); } catch (err) { debugPrint("******* Error"); debugPrint(err.toString()); @@ -213,7 +221,8 @@ class HomeCubit extends Cubit { //////////////////////////////////////// API //////////////////////////////////////// generateInvitation(SpaceModel unit) async { try { - final invitationCode = await SpacesAPI.generateInvitationCode(unit.id, unit.community.uuid); + final invitationCode = + await SpacesAPI.generateInvitationCode(unit.id, unit.community.uuid); if (invitationCode.isNotEmpty) { Share.share('The invitation code is $invitationCode'); CustomSnackBar.displaySnackBar( @@ -229,7 +238,9 @@ class HomeCubit extends Cubit { Future joinAUnit(String code) async { try { - var userUuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? ''; + var userUuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? + ''; Map body = {'inviteCode': code}; final success = await SpacesAPI.joinUnit(userUuid, body); @@ -265,7 +276,8 @@ class HomeCubit extends Cubit { fetchRoomsByUnitId(SpaceModel space) async { emitSafe(GetSpaceRoomsLoading()); try { - space.subspaces = await SpacesAPI.getSubSpaceBySpaceId(space.community.uuid, space.id); + space.subspaces = + await SpacesAPI.getSubSpaceBySpaceId(space.community.uuid, space.id); } catch (failure) { emitSafe(GetSpaceRoomsError(failure.toString())); return; @@ -347,7 +359,8 @@ class HomeCubit extends Cubit { size: 32, ), style: ButtonStyle( - foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor), + foregroundColor: + WidgetStateProperty.all(ColorsManager.textPrimaryColor), ), onPressed: () { Navigator.pushNamed( @@ -368,7 +381,8 @@ class HomeCubit extends Cubit { NavigationService.navigatorKey.currentContext! .read() .add(const SmartSceneClearEvent()); - BlocProvider.of(NavigationService.navigatorKey.currentState!.context) + BlocProvider.of( + NavigationService.navigatorKey.currentState!.context) .add(ResetEffectivePeriod()); NavigationService.navigatorKey.currentContext! .read() @@ -381,7 +395,8 @@ class HomeCubit extends Cubit { size: 28, ), style: ButtonStyle( - foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor), + foregroundColor: + WidgetStateProperty.all(ColorsManager.textPrimaryColor), ), onPressed: () {}, ), @@ -414,7 +429,8 @@ class HomeCubit extends Cubit { }; static var bottomNavItems = [ - defaultBottomNavBarItem(icon: Assets.assetsIconsDashboard, label: 'Dashboard'), + defaultBottomNavBarItem( + icon: Assets.assetsIconsDashboard, label: 'Dashboard'), // defaultBottomNavBarItem(icon: Assets.assetsIconslayout, label: 'Layout'), defaultBottomNavBarItem(icon: Assets.assetsIconsDevices, label: 'Devices'), defaultBottomNavBarItem(icon: Assets.assetsIconsRoutines, label: 'Routine'), @@ -440,7 +456,8 @@ class HomeCubit extends Cubit { void updateDevice(String deviceId) async { try { - final response = await DevicesAPI.firmwareDevice(deviceId: deviceId, firmwareVersion: '0'); + final response = await DevicesAPI.firmwareDevice( + deviceId: deviceId, firmwareVersion: '0'); if (response['success'] ?? false) { CustomSnackBar.displaySnackBar('No updates available'); } @@ -448,7 +465,8 @@ class HomeCubit extends Cubit { } } -BottomNavigationBarItem defaultBottomNavBarItem({required String icon, required String label}) { +BottomNavigationBarItem defaultBottomNavBarItem( + {required String icon, required String label}) { return BottomNavigationBarItem( icon: SvgPicture.asset(icon), activeIcon: SvgPicture.asset( diff --git a/lib/features/auth/view/widgets/login_user_agreement.dart b/lib/features/auth/view/widgets/login_user_agreement.dart index a4e7ee8..978ccad 100644 --- a/lib/features/auth/view/widgets/login_user_agreement.dart +++ b/lib/features/auth/view/widgets/login_user_agreement.dart @@ -2,6 +2,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/utils/context_extension.dart'; diff --git a/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_bloc.dart b/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_bloc.dart new file mode 100644 index 0000000..48fe85f --- /dev/null +++ b/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_bloc.dart @@ -0,0 +1,394 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; +import 'package:syncrow_app/features/devices/bloc/6_scene_switch_bloc/6_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/6_scene_switch_bloc/6_scene_state.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/device_report_model.dart'; +import 'package:syncrow_app/features/devices/model/device_info_model.dart'; +import 'package:syncrow_app/features/devices/model/six_scene_model.dart'; +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; +import 'package:syncrow_app/features/scene/model/scenes_model.dart'; +import 'package:syncrow_app/services/api/devices_api.dart'; +import 'package:syncrow_app/services/api/home_management_api.dart'; +import 'package:syncrow_app/services/api/scene_api.dart'; +import 'package:syncrow_app/services/api/spaces_api.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; + +class SixSceneBloc extends Bloc { + final String sixSceneId; + SixSceneBloc({ + required this.sixSceneId, + }) : super(const SixSceneState()) { + on(_fetchDeviceStatus); + on(changeSwitchStatus); + on(_onLoadScenes); + on(searchScene); + on(_onSaveSelection); + on(_onOptionSelected); + on(_fetchFourSceneSwitches); + on(assignScene); + on(getSceneByName); + on(_selectScene); + on(fetchDeviceInfo); + on(_assignDevice); + on(_fetchRoomsAndDevices); + on(deleteDevice); + on(_toggleLowBattery); + on(_unAssignScene); + } + + final TextEditingController nameController = + TextEditingController(text: deviceName); + bool isSaving = false; + bool editName = false; + final FocusNode focusNode = FocusNode(); + bool closingReminder = false; + bool waterAlarm = false; + static String deviceName = ''; + + SixSceneModel deviceStatus = SixSceneModel( + scene_1: '', + scene_2: '', + scene_3: '', + scene_4: '', + scene_5: '', + scene_6: '', + scene_id_group_id: '', + switch_backlight: false); + + DeviceInfoModel deviceInfo = DeviceInfoModel( + activeTime: 0, + category: "", + categoryName: "", + createTime: 0, + gatewayId: "", + icon: "", + ip: "", + lat: "", + localKey: "", + lon: "", + model: "", + name: "", + nodeId: "", + online: false, + ownerId: "", + productName: "", + sub: false, + timeZone: "", + updateTime: 0, + uuid: "", + productUuid: "", + productType: "", + permissionType: "", + macAddress: "", + subspace: Subspace( + uuid: "", + createdAt: "", + updatedAt: "", + subspaceName: "", + ), + ); + + bool enableAlarm = false; + + void _toggleLowBattery( + ToggleEnableAlarmEvent event, Emitter emit) async { + emit(LoadingNewSate(device: deviceStatus)); + try { + enableAlarm = event.isLowBatteryEnabled; + emit(UpdateState(device: deviceStatus)); + } catch (e) { + emit(SixSceneFailedState(errorMessage: e.toString())); + } + } + + List roomsList = []; + + void _fetchRoomsAndDevices( + FetchRoomsEvent event, Emitter emit) async { + try { + emit(SixSceneLoadingState()); + roomsList = await SpacesAPI.getSubSpaceBySpaceId( + event.unit.community.uuid, event.unit.id); + emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList)); + } catch (e) { + emit(SixSceneFailedState(errorMessage: e.toString())); + return; + } + } + + void _assignDevice(AssignRoomEvent event, Emitter emit) async { + try { + emit(SixSceneLoadingState()); + if (_hasSelectionChanged) { + await HomeManagementAPI.assignDeviceToRoom( + event.unit.community.uuid, event.unit.id, event.roomId, sixSceneId); + final devicesList = await DevicesAPI.getDevicesByRoomId( + communityUuid: event.unit.community.uuid, + spaceUuid: event.unit.id, + roomId: event.roomId); + List allDevicesIds = []; + allDevices.forEach((element) { + allDevicesIds.add(element.uuid!); + }); + await HomeCubit.getInstance().fetchUnitsByUserId(); + CustomSnackBar.displaySnackBar('Save Successfully'); + emit(SaveSelectionSuccessState()); + } + } catch (e) { + emit(SixSceneFailedState(errorMessage: e.toString())); + return; + } + } + + void _unAssignScene( + UnAssignSceneEvent event, Emitter emit) async { + try { + emit(SixSceneLoadingState()); + if (_hasSelectionChanged) { + var response = await DevicesAPI.unAssignScenesDevice( + deviceUuid: event.switchSceneUuid); + CustomSnackBar.displaySnackBar('Save Successfully'); + emit(SaveSelectionSuccessState()); + } + } catch (e) { + emit(SixSceneFailedState(errorMessage: e.toString())); + return; + } + } + + deleteDevice(DeleteDeviceEvent event, Emitter emit) async { + try { + emit(SixSceneLoadingState()); + var response = await DevicesAPI.resetDevise(devicesUuid: sixSceneId); + add(const SixSceneInitialInfo()); + CustomSnackBar.displaySnackBar('Reset Successfully'); + } catch (e) { + emit(SixSceneFailedState(errorMessage: e.toString())); + } + } + +//============================ fitch devise info and status ======================= +///////////////////////////////////////////////////////////////////////////////////// + + Future fetchDeviceInfo( + SixSceneInitialInfo event, Emitter emit) async { + try { + emit(SixSceneLoadingState()); + var response = await DevicesAPI.getDeviceInfo(sixSceneId); + deviceInfo = DeviceInfoModel.fromJson(response); + deviceName = deviceInfo.name; + emit(LoadingDeviceInfo(deviceInfo: deviceInfo)); + } catch (e) { + emit(SixSceneFailedState(errorMessage: e.toString())); + } + } + + void _fetchDeviceStatus( + SixSceneInitial event, Emitter emit) async { + emit(SixSceneLoadingState()); + try { + var response = await DevicesAPI.getDeviceStatus(sixSceneId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = SixSceneModel.fromJson( + statusModelList, + ); + add(const SexSceneSwitchInitial()); + emit(UpdateState(device: deviceStatus)); + } catch (e) { + emit(SixSceneFailedState(errorMessage: e.toString())); + return; + } + } + + //============================ assign Scene ======================= +///////////////////////////////////////////////////////////////////////////////////// + + String selectedFormApiSceneId = ''; + + void getSceneByName( + GetSceneBySwitchName event, Emitter emit) async { + try { + // emit(SixSceneLoadingState()); + + final response = await DevicesAPI.getSceneBySwitchName( + deviceId: sixSceneId, switchName: event.switchName); + selectedFormApiSceneId = response['scene']['uuid']; + emit(SuccessState()); + + emit(UpdateState(device: deviceStatus)); + } catch (e) { + emit(SixSceneFailedState(errorMessage: e.toString())); + } + } + + String selectedSceneId = ''; + + + _selectScene(SelectSceneEvent event, Emitter emit) { + emit(SixSceneLoadingState()); + if (event.isSelected == false) { + selectedSceneId = ''; + selectedFormApiSceneId = ''; + emit(SceneSelectionUpdatedState(selectedSceneId: selectedSceneId)); + } else { + selectedSceneId = event.selectedSceneId; + } + emit(SceneSelectionUpdatedState(selectedSceneId: selectedSceneId)); + } + + List fourScene = []; + + void _fetchFourSceneSwitches( + SexSceneSwitchInitial event, Emitter emit) async { + emit(SixSceneLoadingState()); + try { + var response = await DevicesAPI.getDeviceSceneInfo(sixSceneId); + + Map sceneTitles = { + "scene_1": '', + "scene_2": '', + "scene_3": '', + "scene_4": '', + "scene_5": '', + "scene_6": '', + }; + for (var item in response) { + if (item["switchName"] != null) { + sceneTitles[item["switchName"]] = item["scene"]["name"] ?? ''; + } + } + SixSceneModel deviceStatus = SixSceneModel( + scene_1: sceneTitles["scene_1"] ?? '', + scene_2: sceneTitles["scene_2"] ?? '', + scene_3: sceneTitles["scene_3"] ?? '', + scene_4: sceneTitles["scene_4"] ?? '', + scene_5: sceneTitles["scene_5"] ?? '', + scene_6: sceneTitles["scene_6"] ?? '', + scene_id_group_id: '', + switch_backlight: '', + ); + emit(UpdateState(device: deviceStatus)); + } catch (e) { + emit(SixSceneFailedState(errorMessage: e.toString())); + return; + } + } + + void assignScene(AssignDeviceScene event, Emitter emit) async { + emit(SixSceneLoadingState()); + try { + if (event.sceneUuid == '') { + final response = await DevicesAPI.unAssignScenesDevice( + deviceUuid: sixSceneId, switchName: event.switchName); + } else { + final response = await DevicesAPI.postDeviceSceneInfo( + deviceId: sixSceneId, + sceneUuid: event.sceneUuid, + spaceUuid: event.unit!.id, + switchName: event.switchName); + } + + emit(SaveSelectionSuccessState()); + CustomSnackBar.displaySnackBar('Save Successfully'); + } catch (e) { + emit(SixSceneFailedState(errorMessage: e.toString())); + } + } + + DeviceReport recordGroups = + DeviceReport(startTime: '0', endTime: '0', data: []); + + List allDevices = []; + + bool switchStatus = true; + Future changeSwitchStatus( + ChangeSwitchStatusEvent event, Emitter emit) async { + try { + emit(SixSceneLoadingState()); + switchStatus = deviceStatus.switch_backlight; + switchStatus = !switchStatus; + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: sixSceneId, + code: 'switch_backlight', + value: switchStatus), + sixSceneId); + deviceStatus.switch_backlight = switchStatus; + + Future.delayed(const Duration(milliseconds: 250), () { + add(const SexSceneSwitchInitial()); + }); + + Future.delayed(const Duration(milliseconds: 250), () { + emit(ChangeSwitchState(isEnable: switchStatus)); + emit(UpdateState(device: deviceStatus)); + }); + } catch (_) { + emit(const SixSceneFailedState(errorMessage: 'Something went wrong')); + } + } + + Future _onLoadScenes( + LoadScenes event, Emitter emit) async { + emit(SixSceneLoadingState()); + + try { + allScenes = await SceneApi.getScenesByUnitId( + event.unitId, event.unit.community.uuid, + showInDevice: event.showInDevice); + + filteredScenes = allScenes; + + emit(SceneLoaded(allScenes)); + emit(UpdateState(device: deviceStatus)); + } catch (e) { + emit(const SixSceneFailedState(errorMessage: 'Something went wrong')); + } + + emit(SuccessState()); + } + + bool selecedScene = false; + + List allScenes = []; + List filteredScenes = []; + + void searchScene(SearchScenesEvent event, Emitter emit) { + emit(SixSceneLoadingState()); + filteredScenes = event.query.isEmpty + ? allScenes + : allScenes.where((scene) { + final sceneName = scene.name.toLowerCase(); + return sceneName.contains(event.query.toLowerCase()); + }).toList(); + emit(SearchResultsState()); + } + + String _selectedOption = ''; + bool _hasSelectionChanged = false; + + void _onOptionSelected(SelectOptionEvent event, Emitter emit) { + emit(SixSceneLoadingState()); + _selectedOption = event.selectedOption; + _hasSelectionChanged = true; + emit(OptionSelectedState( + selectedOption: _selectedOption, + hasSelectionChanged: _hasSelectionChanged)); + } + + void _onSaveSelection(SaveSelectionEvent event, Emitter emit) { + if (_hasSelectionChanged) { + print('Save button clicked with selected option: $_selectedOption'); + _hasSelectionChanged = false; + emit(SaveSelectionSuccessState()); + } + } +} diff --git a/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_event.dart b/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_event.dart new file mode 100644 index 0000000..0cf3437 --- /dev/null +++ b/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_event.dart @@ -0,0 +1,177 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; +import 'package:syncrow_app/features/devices/model/group_devices_model.dart'; + +abstract class SixSceneEvent extends Equatable { + const SixSceneEvent(); + + @override + List get props => []; +} + +class GetSceneBySwitchName extends SixSceneEvent { + final String? switchName; + + const GetSceneBySwitchName({this.switchName}); +} + +class SixSceneInitial extends SixSceneEvent { + const SixSceneInitial(); +} + +class SaveNameEvent extends SixSceneEvent { + const SaveNameEvent(); +} + +class UnAssignSceneEvent extends SixSceneEvent { + final String switchSceneUuid; + const UnAssignSceneEvent({required this.switchSceneUuid}); +} + +class SixSceneInitialInfo extends SixSceneEvent { + const SixSceneInitialInfo(); +} + +class SexSceneSwitchInitial extends SixSceneEvent { + const SexSceneSwitchInitial(); +} + +class ToggleEnableAlarmEvent extends SixSceneEvent { + final bool isLowBatteryEnabled; + + const ToggleEnableAlarmEvent(this.isLowBatteryEnabled); + + @override + List get props => [isLowBatteryEnabled]; +} + +class ToggleNotificationEvent extends SixSceneEvent { + final bool isClosingEnabled; + + const ToggleNotificationEvent(this.isClosingEnabled); + + @override + List get props => [isClosingEnabled]; +} + +class DeleteDeviceEvent extends SixSceneEvent {} + +class ChangeNameEvent extends SixSceneEvent { + final bool? value; + const ChangeNameEvent({this.value}); +} + +class SearchFaqEvent extends SixSceneEvent { + final String query; + + const SearchFaqEvent(this.query); +} + +class SixSceneInitialQuestion extends SixSceneEvent { + const SixSceneInitialQuestion(); +} + +class ChangeSwitchStatusEvent extends SixSceneEvent {} + +class FetchRoomsEvent extends SixSceneEvent { + final SpaceModel unit; + + const FetchRoomsEvent({required this.unit}); + + @override + List get props => [unit]; +} + +class LoadScenes extends SixSceneEvent { + final String unitId; + final bool showInDevice; + final SpaceModel unit; + + const LoadScenes( + {required this.unitId, required this.unit, this.showInDevice = false}); + + @override + List get props => [unitId, showInDevice]; +} + +class SelectSceneEvent extends SixSceneEvent { + final String selectedSceneId; + final bool isSelected; + // final String unitId; + const SelectSceneEvent({ + // required this.unitId, + required this.selectedSceneId, + required this.isSelected, + }); +} + + +class SearchScenesEvent extends SixSceneEvent { + final String query; + const SearchScenesEvent({ + required this.query, + }); +} + +class SaveSelectionEvent extends SixSceneEvent {} + +class SelectOptionEvent extends SixSceneEvent { + dynamic selectedOption; + SelectOptionEvent({ + this.selectedOption, + }); +} + +class AddDeviceToGroup extends SixSceneEvent { + final GroupDevicesModel device; + final String icon; + const AddDeviceToGroup(this.device, this.icon); +} + +class RemoveDeviceFromGroup extends SixSceneEvent { + final GroupDevicesModel device; + final String icon; + + const RemoveDeviceFromGroup(this.device, this.icon); +} + +class AssignDeviceScene extends SixSceneEvent { + final String? sceneUuid; + final String? switchName; + final SpaceModel? unit; + const AssignDeviceScene({ + this.sceneUuid, + this.unit, + this.switchName, + }); +} + +class AssignRoomEvent extends SixSceneEvent { + final String roomId; + final SpaceModel unit; + final BuildContext context; + + const AssignRoomEvent({ + required this.roomId, + required this.unit, + required this.context, + }); + + @override + List get props => [ + roomId, + unit, + context, + ]; +} + +class ToggleUpdateEvent extends SixSceneEvent { + final bool? isUpdateEnabled; + const ToggleUpdateEvent({this.isUpdateEnabled}); +} + +class ToggleHelpfulEvent extends SixSceneEvent { + final bool? isHelpful; + const ToggleHelpfulEvent({this.isHelpful}); +} diff --git a/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_state.dart b/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_state.dart new file mode 100644 index 0000000..43982ef --- /dev/null +++ b/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_state.dart @@ -0,0 +1,125 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/group_devices_model.dart'; +import 'package:syncrow_app/features/devices/model/device_info_model.dart'; +import 'package:syncrow_app/features/devices/model/six_scene_question_model.dart'; +import 'package:syncrow_app/features/devices/model/six_scene_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; +import 'package:syncrow_app/features/scene/model/scenes_model.dart'; + +class SixSceneState extends Equatable { + const SixSceneState(); + + @override + List get props => []; +} + +class SixSceneLoadingState extends SixSceneState {} + +class SaveState extends SixSceneState {} + +class UpdateStateList extends SixSceneState { + final List groupDevices; + final List devices; + const UpdateStateList({required this.groupDevices, required this.devices}); +} + +class SixSceneFailedState extends SixSceneState { + final String errorMessage; + + const SixSceneFailedState({required this.errorMessage}); + + @override + List get props => [errorMessage]; +} + +class UpdateState extends SixSceneState { + final SixSceneModel device; + const UpdateState({required this.device}); + + @override + List get props => [device]; +} + +class LoadingNewSate extends SixSceneState { + final SixSceneModel device; + const LoadingNewSate({required this.device}); + + @override + List get props => [device]; +} + +class NameEditingState extends SixSceneState { + final bool editName; + + const NameEditingState({required this.editName}); +} + +class FaqLoadedState extends SixSceneState { + final List filteredFaqQuestions; + + const FaqLoadedState({this.filteredFaqQuestions = const []}); +} + +class FaqSearchState extends SixSceneState { + final List filteredFaqQuestions; + + const FaqSearchState({this.filteredFaqQuestions = const []}); +} + +class FetchRoomsState extends SixSceneState { + final List roomsList; + final List devicesList; + + const FetchRoomsState({required this.devicesList, required this.roomsList}); + + @override + List get props => [devicesList]; +} + +class ChangeSwitchState extends SixSceneState { + final bool isEnable; + + const ChangeSwitchState({required this.isEnable}); +} + +class SceneLoaded extends SixSceneState { + final List scenes; + final String? loadingSceneId; + final Map loadingStates; + + const SceneLoaded(this.scenes, + {this.loadingSceneId, this.loadingStates = const {}}); +} + +class SearchResultsState extends SixSceneState {} +class SuccessState extends SixSceneState {} + +class SaveSelectionSuccessState extends SixSceneState {} + +class OptionSelectedState extends SixSceneState { + final String selectedOption; + final bool hasSelectionChanged; + + const OptionSelectedState({ + required this.selectedOption, + required this.hasSelectionChanged, + }); + + @override + List get props => [selectedOption, hasSelectionChanged]; +} + +class SceneSelectionUpdatedState extends SixSceneState { + final String selectedSceneId; + + const SceneSelectionUpdatedState({required this.selectedSceneId}); +} + +class LoadingDeviceInfo extends SixSceneState { + final DeviceInfoModel deviceInfo; + const LoadingDeviceInfo({required this.deviceInfo}); + + @override + List get props => [deviceInfo]; +} diff --git a/lib/features/devices/bloc/device_settings_bloc/device_scene_bloc.dart b/lib/features/devices/bloc/device_settings_bloc/device_scene_bloc.dart new file mode 100644 index 0000000..4c91650 --- /dev/null +++ b/lib/features/devices/bloc/device_settings_bloc/device_scene_bloc.dart @@ -0,0 +1,483 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/device_report_model.dart'; +import 'package:syncrow_app/features/devices/model/four_scene_model.dart'; +import 'package:syncrow_app/features/devices/model/four_scene_switch_model.dart'; +import 'package:syncrow_app/features/devices/model/question_model.dart'; +import 'package:syncrow_app/features/devices/model/group_devices_model.dart'; +import 'package:syncrow_app/features/devices/model/device_info_model.dart'; +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; +import 'package:syncrow_app/features/scene/model/scenes_model.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/services/api/devices_api.dart'; +import 'package:syncrow_app/services/api/home_management_api.dart'; +import 'package:syncrow_app/services/api/spaces_api.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; + +class DeviceSettingBloc extends Bloc { + final String deviceId; + DeviceSettingBloc({ + required this.deviceId, + }) : super(const DeviceSettingState()) { + on(_fetchDeviceStatus); + on(fetchDeviceInfo); + on(saveName); + on(_toggleNotification); + on(_changeName); + on(_onSearchFaq); + on(_fetchRoomsAndDevices); + on(_onLoadSettings); + on(searchSetting); + on(_assignDevice); + on(_addDeviceToGroup); + on(_removeDeviceFromGroup); + on(_onDeviceSettingInitial); + on(_fetchDeviceSetting); + on(getSettingByName); + on(_selectSetting); + on(deleteDevice); + on(_toggleLowBattery); + on(_toggleUpdate); + on(_toggleHelpful); + on(_onOptionSelected); + } + + final TextEditingController nameController = + TextEditingController(text: deviceName); + bool isSaving = false; + bool editName = false; + final FocusNode focusNode = FocusNode(); + bool closingReminder = false; + bool waterAlarm = false; + static String deviceName = ''; + static String selectedRoomId = ''; + bool selecedSetting = false; + bool enableAlarm = false; + List fourSetting = []; + + String _selectedOption = ''; + bool _hasSelectionChanged = false; + + FourSceneModelState deviceStatus = FourSceneModelState( + scene_1: '', + scene_2: '', + scene_3: '', + scene_4: '', + scene_id_group_id: '', + switch_backlight: false); + + DeviceInfoModel deviceInfo = DeviceInfoModel( + activeTime: 0, + category: "", + categoryName: "", + createTime: 0, + gatewayId: "", + icon: "", + ip: "", + lat: "", + localKey: "", + lon: "", + model: "", + name: "", + nodeId: "", + online: false, + ownerId: "", + productName: "", + sub: false, + timeZone: "", + updateTime: 0, + uuid: "", + productUuid: "", + productType: "", + permissionType: "", + macAddress: "", + subspace: Subspace( + uuid: "", + createdAt: "", + updatedAt: "", + subspaceName: "", + ), + ); + +//============================ get Setting and assign scene ======================= + String selectedFormApiSettingId = ''; + String selectedSettingId = ''; + + _selectSetting(SelectSettingEvent event, Emitter emit) { + emit(DeviceSettingLoadingState()); + selectedSettingId = event.selectedSettingId; + emit(SettingSelectionUpdatedState(selectedSettingId: selectedSettingId)); + } + + void getSettingByName( + GetSettingBySwitchName event, Emitter emit) async { + // emit(DeviceSettingLoadingState()); + try { + final response = await DevicesAPI.getSceneBySwitchName( + deviceId: deviceId, switchName: event.switchName); + selectedFormApiSettingId = response['scene']['uuid']; + emit(UpdateState(device: deviceStatus)); + } catch (e) { + emit(DeviceSettingFailedState(errorMessage: e.toString())); + } + } + + void _fetchDeviceSetting( + FetchDeviceSetting event, Emitter emit) async { + emit(DeviceSettingLoadingState()); + try { + var response = await DevicesAPI.getDeviceStatus(deviceId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = FourSceneModelState.fromJson( + statusModelList, + ); + emit(UpdateState(device: deviceStatus)); + Future.delayed(const Duration(milliseconds: 500)); + } catch (e) { + emit(DeviceSettingFailedState(errorMessage: e.toString())); + return; + } + } + +//===================== fetch Device Status and info ======================= + + void _toggleLowBattery( + ToggleEnableAlarmEvent event, Emitter emit) async { + emit(LoadingNewSate(device: deviceStatus)); + try { + enableAlarm = event.isLowBatteryEnabled; + emit(UpdateState(device: deviceStatus)); + } catch (e) { + emit(DeviceSettingFailedState(errorMessage: e.toString())); + } + } + + void _fetchDeviceStatus( + DeviceSettingInitial event, Emitter emit) async { + emit(DeviceSettingLoadingState()); + try { + var response = await DevicesAPI.getDeviceStatus(deviceId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = FourSceneModelState.fromJson( + statusModelList, + ); + emit(SuccessState()); + // add(const DeviceSettingSwitchInitial()); + } catch (e) { + emit(DeviceSettingFailedState(errorMessage: e.toString())); + return; + } + } + + Future fetchDeviceInfo( + DeviceSettingInitialInfo event, Emitter emit) async { + try { + emit(DeviceSettingLoadingState()); + var response = await DevicesAPI.getDeviceInfo(deviceId); + deviceInfo = DeviceInfoModel.fromJson(response); + deviceName = deviceInfo.name; + emit(LoadingDeviceInfo(deviceInfo: deviceInfo)); + } catch (e) { + emit(DeviceSettingFailedState(errorMessage: e.toString())); + } + } + + void _onSearchFaq(SearchFaqEvent event, Emitter emit) { + emit(DeviceSettingLoadingState()); + List _faqQuestions = faqQuestions.where((question) { + return question.question + .toLowerCase() + .contains(event.query.toLowerCase()); + }).toList(); + emit(FaqSearchState(filteredFaqQuestions: _faqQuestions)); + } + +//============================ assign Device ================================== +////////////////////////////////////////////////////////////////////////////// + + void _toggleNotification( + ToggleNotificationEvent event, Emitter emit) async { + emit(LoadingNewSate(device: deviceStatus)); + try { + enableAlarm = event.isClosingEnabled; + emit(UpdateState(device: deviceStatus)); + } catch (e) { + emit(DeviceSettingFailedState(errorMessage: e.toString())); + } + } + + DeviceReport recordGroups = + DeviceReport(startTime: '0', endTime: '0', data: []); + + //========================= Question and faq ================================ + + final List faqQuestions = [ + QuestionModel( + id: 1, + question: + 'How does an 4 Setting Switch work? How long will an 4 Setting Switch persist?', + answer: + 'Yes. In scenes with high detection requirements, we recommend that you choose phone or message notification in Automation.', + ), + QuestionModel( + id: 2, + question: 'Does the 4 Setting Switch support sending notifications?', + answer: + 'The SOS alarm will persist until it is manually turned off or after a set time.', + ), + QuestionModel( + id: 3, + question: + 'Why does the data statistics in the device panel not show the correct data?', + answer: 'Try restarting the device. If it persists, contact support.', + ), + QuestionModel( + id: 4, + question: + 'How long will the App show offline after a device (low-power devices and normal devices) is powered...', + answer: + 'No, a network connection is required to send the alert to your contacts.', + ), + ]; + + bool? isHelpful; + + void _toggleHelpful( + ToggleHelpfulEvent event, Emitter emit) async { + try { + emit(DeviceSettingLoadingState()); + isHelpful = event.isHelpful!; + emit(SaveState()); + } catch (e) { + emit( + const DeviceSettingFailedState(errorMessage: 'Something went wrong')); + } + } + + Future _onDeviceSettingInitial(DeviceSettingInitialQuestion event, + Emitter emit) async { + emit(DeviceSettingLoadingState()); + emit(FaqLoadedState(filteredFaqQuestions: faqQuestions)); + } + + List allDevices = []; + List roomsList = []; + bool switchStatus = true; + + Future _onLoadSettings( + LoadSettings event, Emitter emit) async { + emit(DeviceSettingLoadingState()); + try { + if (event.unitId.isNotEmpty) { + // allSettings = await DevicesAPI.getDveByUnitId( + // event.unitId, event.unit.community.uuid, + // showInDevice: event.showInDevice); + + filteredSettings = allSettings; + emit(SettingLoaded(allSettings)); + } else { + emit(const DeviceSettingFailedState(errorMessage: 'Unit ID is empty')); + } + } catch (e) { + emit( + const DeviceSettingFailedState(errorMessage: 'Something went wrong')); + } + } + + List allSettings = []; + List filteredSettings = []; + + void searchSetting( + SearchSettingsEvent event, Emitter emit) { + emit(DeviceSettingLoadingState()); + filteredSettings = event.query.isEmpty + ? allSettings + : allSettings.where((scene) { + final sceneName = scene.name?.toLowerCase() ?? ''; + return sceneName.contains(event.query.toLowerCase()); + }).toList(); + emit(SearchResultsState()); + } + + List groupDevices = [ + GroupDevicesModel( + dec: 'Syncroom', icon: Assets.minusIcon, name: '6 Setting Switch') + ]; + + List devices = [ + GroupDevicesModel( + dec: 'Syncroom', icon: Assets.addDevicesIcon, name: '6 Setting Switch') + ]; + + // Handler for AddDeviceToGroup + void _addDeviceToGroup( + AddDeviceToGroup event, Emitter emit) { + devices.remove(event.device); + groupDevices.add(event.device); + for (var device in groupDevices) { + device.icon = event.icon; + } + emit(UpdateStateList(groupDevices: groupDevices, devices: devices)); + } + + // Handler for RemoveDeviceFromGroup + void _removeDeviceFromGroup( + RemoveDeviceFromGroup event, Emitter emit) { + groupDevices.remove(event.device); + devices.add(event.device); + for (var device in groupDevices) { + device.icon = event.icon; + } + emit(UpdateStateList(groupDevices: groupDevices, devices: devices)); + } + + //=========================== assign device to room ========================== + + void _assignDevice( + AssignRoomEvent event, Emitter emit) async { + try { + emit(DeviceSettingLoadingState()); + if (_hasSelectionChanged) { + await HomeManagementAPI.assignDeviceToRoom( + event.unit.community.uuid, event.unit.id, event.roomId, deviceId); + final devicesList = await DevicesAPI.getDevicesByRoomId( + communityUuid: event.unit.community.uuid, + spaceUuid: event.unit.id, + roomId: event.roomId); + List allDevicesIds = []; + allDevices.forEach((element) { + allDevicesIds.add(element.uuid!); + }); + await HomeCubit.getInstance().fetchUnitsByUserId(); + CustomSnackBar.displaySnackBar('Save Successfully'); + emit(SaveSelectionSuccessState()); + } + } catch (e) { + emit( + const DeviceSettingFailedState(errorMessage: 'Something went wrong')); + return; + } + } + + void _fetchRoomsAndDevices( + FetchRoomsEvent event, Emitter emit) async { + try { + emit(DeviceSettingLoadingState()); + roomsList = await SpacesAPI.getSubSpaceBySpaceId( + event.unit.community.uuid, event.unit.id); + emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList)); + } catch (e) { + emit( + const DeviceSettingFailedState(errorMessage: 'Something went wrong')); + return; + } + } + +//============================ setting name ================================== +////////////////////////////////////////////////////////////////////////////// + + void _changeName(ChangeNameEvent event, Emitter emit) { + emit(DeviceSettingLoadingState()); + editName = event.value!; + if (editName) { + Future.delayed(const Duration(milliseconds: 500), () { + focusNode.requestFocus(); + }); + } else { + focusNode.unfocus(); + } + emit(NameEditingState(editName: editName)); + } + + bool _validateInputs() { + final nameError = fullNameValidator(nameController.text); + if (nameError != null) { + CustomSnackBar.displaySnackBar(nameError); + return true; + } + return false; + } + + String? fullNameValidator(String? value) { + if (value == null) return 'name is required'; + final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim(); + if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) { + return 'name must be between 2 and 30 characters long'; + } + if (RegExp(r"/[^ a-zA-Z0-9-\']/").hasMatch(withoutExtraSpaces)) { + return 'Only alphanumeric characters, space, dash and single quote are allowed'; + } + return null; + } + + Future saveName( + SaveNameEvent event, Emitter emit) async { + if (_validateInputs()) return; + try { + add(const ChangeNameEvent(value: false)); + isSaving = true; + emit(DeviceSettingLoadingState()); + var response = await DevicesAPI.putDeviceName( + deviceId: deviceId, deviceName: nameController.text); + add(const DeviceSettingInitialInfo()); + await HomeCubit.getInstance().fetchUnitsByUserId(); + CustomSnackBar.displaySnackBar('Save Successfully'); + emit(SaveState()); + } catch (e) { + emit(DeviceSettingFailedState(errorMessage: e.toString())); + } finally { + isSaving = false; + } + } + +//====================== update device ============================== + + bool enableUpdate = false; + + void _toggleUpdate( + ToggleUpdateEvent event, Emitter emit) async { + try { + emit(DeviceSettingLoadingState()); + enableUpdate = event.isUpdateEnabled!; + emit(SaveState()); + } catch (e) { + emit( + const DeviceSettingFailedState(errorMessage: 'Something went wrong')); + } + } + + deleteDevice( + DeleteDeviceEvent event, Emitter emit) async { + try { + emit(DeviceSettingLoadingState()); + var response = await DevicesAPI.resetDevise(devicesUuid: deviceId); + CustomSnackBar.displaySnackBar('Reset Successfully'); + emit(UpdateState(device: deviceStatus)); + } catch (e) { + emit(DeviceSettingFailedState(errorMessage: e.toString())); + return; + } + } + + void _onOptionSelected( + SelectOptionEvent event, Emitter emit) { + emit(DeviceSettingLoadingState()); + _selectedOption = event.selectedOption; + _hasSelectionChanged = true; + emit(OptionSelectedState( + selectedOption: _selectedOption, + hasSelectionChanged: _hasSelectionChanged)); + } +} diff --git a/lib/features/devices/bloc/device_settings_bloc/device_scene_event.dart b/lib/features/devices/bloc/device_settings_bloc/device_scene_event.dart new file mode 100644 index 0000000..bca5b97 --- /dev/null +++ b/lib/features/devices/bloc/device_settings_bloc/device_scene_event.dart @@ -0,0 +1,171 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; +import 'package:syncrow_app/features/devices/model/group_devices_model.dart'; + +abstract class DeviceSettingEvent extends Equatable { + const DeviceSettingEvent(); + + @override + List get props => []; +} + +class DeviceSettingInitialInfo extends DeviceSettingEvent { + const DeviceSettingInitialInfo(); +} + +class DeviceSettingInitial extends DeviceSettingEvent { + const DeviceSettingInitial(); +} + +class SaveNameEvent extends DeviceSettingEvent { + final String? deviceName; + + const SaveNameEvent({this.deviceName}); +} + +class ToggleEnableAlarmEvent extends DeviceSettingEvent { + final bool isLowBatteryEnabled; + + const ToggleEnableAlarmEvent(this.isLowBatteryEnabled); + + @override + List get props => [isLowBatteryEnabled]; +} + +class ToggleNotificationEvent extends DeviceSettingEvent { + final bool isClosingEnabled; + + const ToggleNotificationEvent(this.isClosingEnabled); + + @override + List get props => [isClosingEnabled]; +} + +class DeleteDeviceEvent extends DeviceSettingEvent {} + +class ChangeNameEvent extends DeviceSettingEvent { + final bool? value; + const ChangeNameEvent({this.value}); +} + +class SearchFaqEvent extends DeviceSettingEvent { + final String query; + + const SearchFaqEvent(this.query); +} + +class DeviceSettingInitialQuestion extends DeviceSettingEvent { + const DeviceSettingInitialQuestion(); +} + +// class ChangeSwitchStatusEvent extends DeviceSettingEvent {} + +class FetchRoomsEvent extends DeviceSettingEvent { + final SpaceModel unit; + + const FetchRoomsEvent({required this.unit}); + + @override + List get props => [unit]; +} + +class LoadSettings extends DeviceSettingEvent { + final String unitId; + final bool showInDevice; + final SpaceModel unit; + + const LoadSettings( + {required this.unitId, required this.unit, this.showInDevice = false}); + + @override + List get props => [unitId, showInDevice]; +} + +class SelectSettingEvent extends DeviceSettingEvent { + final String selectedSettingId; + // final String unitId; + const SelectSettingEvent({ + // required this.unitId, + required this.selectedSettingId, + }); +} + +class SearchSettingsEvent extends DeviceSettingEvent { + final String query; + const SearchSettingsEvent({ + required this.query, + }); +} + +class SelectOptionEvent extends DeviceSettingEvent { + final dynamic selectedOption; + const SelectOptionEvent({ + this.selectedOption, + }); +} + +class AddDeviceToGroup extends DeviceSettingEvent { + final GroupDevicesModel device; + final String icon; + const AddDeviceToGroup(this.device, this.icon); +} + +class RemoveDeviceFromGroup extends DeviceSettingEvent { + final GroupDevicesModel device; + final String icon; + + const RemoveDeviceFromGroup(this.device, this.icon); +} + +class AssignRoomEvent extends DeviceSettingEvent { + final String roomId; + final SpaceModel unit; + final BuildContext context; + + const AssignRoomEvent({ + required this.roomId, + required this.unit, + required this.context, + }); + + @override + List get props => [ + roomId, + unit, + context, + ]; +} + +class FetchDeviceSetting extends DeviceSettingEvent {} + +// class DeviceSettingSwitchInitial extends DeviceSettingEvent { +// const DeviceSettingSwitchInitial(); +// } + +class AssignDeviceSetting extends DeviceSettingEvent { + final String? sceneUuid; + final String? switchName; + final SpaceModel? unit; + const AssignDeviceSetting({ + this.sceneUuid, + this.unit, + this.switchName, + }); +} + +class GetSettingBySwitchName extends DeviceSettingEvent { + final String? switchName; + + const GetSettingBySwitchName({this.switchName}); +} + +class ToggleUpdateEvent extends DeviceSettingEvent { + final bool? isUpdateEnabled; + const ToggleUpdateEvent({this.isUpdateEnabled}); +} + +class ToggleHelpfulEvent extends DeviceSettingEvent { + final bool? isHelpful; + const ToggleHelpfulEvent({this.isHelpful}); +} diff --git a/lib/features/devices/bloc/device_settings_bloc/device_scene_state.dart b/lib/features/devices/bloc/device_settings_bloc/device_scene_state.dart new file mode 100644 index 0000000..b3325af --- /dev/null +++ b/lib/features/devices/bloc/device_settings_bloc/device_scene_state.dart @@ -0,0 +1,125 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/four_scene_model.dart'; +import 'package:syncrow_app/features/devices/model/question_model.dart'; +import 'package:syncrow_app/features/devices/model/group_devices_model.dart'; +import 'package:syncrow_app/features/devices/model/device_info_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; +import 'package:syncrow_app/features/scene/model/scenes_model.dart'; + +class DeviceSettingState extends Equatable { + const DeviceSettingState(); + + @override + List get props => []; +} + +class DeviceSettingLoadingState extends DeviceSettingState {} + +class UpdateStateList extends DeviceSettingState { + final List groupDevices; + final List devices; + const UpdateStateList({required this.groupDevices, required this.devices}); +} + +class DeviceSettingFailedState extends DeviceSettingState { + final String errorMessage; + + const DeviceSettingFailedState({required this.errorMessage}); + + @override + List get props => [errorMessage]; +} + +class UpdateState extends DeviceSettingState { + final FourSceneModelState device; + const UpdateState({required this.device}); + + @override + List get props => [device]; +} + +class LoadingNewSate extends DeviceSettingState { + final FourSceneModelState device; + const LoadingNewSate({required this.device}); + + @override + List get props => [device]; +} + +class NameEditingState extends DeviceSettingState { + final bool editName; + + const NameEditingState({required this.editName}); +} + +class FaqLoadedState extends DeviceSettingState { + final List filteredFaqQuestions; + + const FaqLoadedState({this.filteredFaqQuestions = const []}); +} + +class FaqSearchState extends DeviceSettingState { + final List filteredFaqQuestions; + + const FaqSearchState({this.filteredFaqQuestions = const []}); +} + +class FetchRoomsState extends DeviceSettingState { + final List roomsList; + final List devicesList; + + const FetchRoomsState({required this.devicesList, required this.roomsList}); + + @override + List get props => [devicesList]; +} + +class ChangeSwitchState extends DeviceSettingState { + final bool isEnable; + + const ChangeSwitchState({required this.isEnable}); +} + +class SettingLoaded extends DeviceSettingState { + final List scenes; + final String? loadingSettingId; + final Map loadingStates; + + const SettingLoaded(this.scenes, + {this.loadingSettingId, this.loadingStates = const {}}); +} + +class SearchResultsState extends DeviceSettingState {} + +class SaveState extends DeviceSettingState {} +class SuccessState extends DeviceSettingState {} + +class SaveSelectionSuccessState extends DeviceSettingState {} + +class OptionSelectedState extends DeviceSettingState { + final String selectedOption; + final bool hasSelectionChanged; + + const OptionSelectedState({ + required this.selectedOption, + required this.hasSelectionChanged, + }); + + @override + List get props => [selectedOption, hasSelectionChanged]; +} + +class LoadingDeviceInfo extends DeviceSettingState { + final DeviceInfoModel deviceInfo; + const LoadingDeviceInfo({required this.deviceInfo}); + + @override + List get props => [deviceInfo]; +} + +class SettingSelectionUpdatedState extends DeviceSettingState { + final String selectedSettingId; + + const SettingSelectionUpdatedState({required this.selectedSettingId}); +} diff --git a/lib/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart b/lib/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart new file mode 100644 index 0000000..d3069f7 --- /dev/null +++ b/lib/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart @@ -0,0 +1,330 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_state.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/device_report_model.dart'; +import 'package:syncrow_app/features/devices/model/four_scene_model.dart'; +import 'package:syncrow_app/features/devices/model/four_scene_switch_model.dart'; +import 'package:syncrow_app/features/devices/model/device_info_model.dart'; +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; +import 'package:syncrow_app/features/scene/model/scenes_model.dart'; +import 'package:syncrow_app/services/api/devices_api.dart'; +import 'package:syncrow_app/services/api/scene_api.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; + +class FourSceneBloc extends Bloc { + final String fourSceneId; + FourSceneBloc({ + required this.fourSceneId, + }) : super(const FourSceneState()) { + on(_fetchDeviceStatus); + on(fetchDeviceInfo); + on(_toggleNotification); + on(changeSwitchStatus); + on(_onLoadScenes); + on(searchScene); + on(_onOptionSelected); + on(_fetchDeviceScene); + on(_fetchFourSceneSwitches); + on(assignScene); + on(getSceneByName); + on(_selectScene); + on(_toggleLowBattery); + } + + final TextEditingController nameController = + TextEditingController(text: deviceName); + bool isSaving = false; + bool editName = false; + final FocusNode focusNode = FocusNode(); + bool closingReminder = false; + bool waterAlarm = false; + static String deviceName = ''; + static String selectedRoomId = ''; + bool selecedScene = false; + bool enableAlarm = false; + List fourScene = []; + + String _selectedOption = ''; + bool _hasSelectionChanged = false; + + FourSceneModelState deviceStatus = FourSceneModelState( + scene_1: '', + scene_2: '', + scene_3: '', + scene_4: '', + scene_id_group_id: '', + switch_backlight: false); + + DeviceInfoModel deviceInfo = DeviceInfoModel( + activeTime: 0, + category: "", + categoryName: "", + createTime: 0, + gatewayId: "", + icon: "", + ip: "", + lat: "", + localKey: "", + lon: "", + model: "", + name: "", + nodeId: "", + online: false, + ownerId: "", + productName: "", + sub: false, + timeZone: "", + updateTime: 0, + uuid: "", + productUuid: "", + productType: "", + permissionType: "", + macAddress: "", + subspace: Subspace( + uuid: "", + createdAt: "", + updatedAt: "", + subspaceName: "", + ), + ); + +//============================ get Scene and assign scene ======================= + String selectedFormApiSceneId = ''; + String selectedSceneId = ''; + + _selectScene(SelectSceneEvent event, Emitter emit) { + emit(FourSceneLoadingState()); + if (event.isSelected == false) { + selectedSceneId = ''; + selectedFormApiSceneId = ''; + emit(SceneSelectionUpdatedState(selectedSceneId: selectedSceneId)); + } else { + selectedSceneId = event.selectedSceneId; + } + emit(SceneSelectionUpdatedState(selectedSceneId: selectedSceneId)); + } + + void getSceneByName( + GetSceneBySwitchName event, Emitter emit) async { + // emit(FourSceneLoadingState()); + try { + final response = await DevicesAPI.getSceneBySwitchName( + deviceId: fourSceneId, switchName: event.switchName); + selectedFormApiSceneId = response['scene']['uuid']; + emit(UpdateState(device: deviceStatus)); + } catch (e) { + emit(FourSceneFailedState(errorMessage: e.toString())); + } + } + + void _onOptionSelected( + SelectOptionEvent event, Emitter emit) { + emit(FourSceneLoadingState()); + _selectedOption = event.selectedOption; + _hasSelectionChanged = true; + emit(OptionSelectedState( + selectedOption: _selectedOption, + hasSelectionChanged: _hasSelectionChanged)); + } + + void _fetchFourSceneSwitches( + FourSceneSwitchInitial event, Emitter emit) async { + emit(FourSceneLoadingState()); + try { + var response = await DevicesAPI.getDeviceSceneInfo(fourSceneId); + Map sceneTitles = { + "scene_1": '', + "scene_2": '', + "scene_3": '', + "scene_4": '', + }; + for (var item in response) { + if (item["switchName"] != null) { + sceneTitles[item["switchName"]] = item["scene"]["name"] ?? ''; + } + } + FourSceneModelState deviceStatus = FourSceneModelState( + scene_1: sceneTitles["scene_1"] ?? '', + scene_2: sceneTitles["scene_2"] ?? '', + scene_3: sceneTitles["scene_3"] ?? '', + scene_4: sceneTitles["scene_4"] ?? '', + scene_id_group_id: '', + switch_backlight: '', + ); + emit(UpdateState(device: deviceStatus)); + } catch (e) { + emit(FourSceneFailedState(errorMessage: e.toString())); + return; + } + } + + void assignScene( + AssignDeviceScene event, Emitter emit) async { + emit(FourSceneLoadingState()); + try { + if (event.sceneUuid == '') { + final response = await DevicesAPI.unAssignScenesDevice( + deviceUuid: fourSceneId, switchName: event.switchName); + } else { + final response = await DevicesAPI.postDeviceSceneInfo( + deviceId: fourSceneId, + sceneUuid: event.sceneUuid, + spaceUuid: event.unit!.id, + switchName: event.switchName); + } + + emit(SaveSelectionSuccessState()); + CustomSnackBar.displaySnackBar('Save Successfully'); + } catch (e) { + emit(FourSceneFailedState(errorMessage: e.toString())); + } + } + + void _fetchDeviceScene( + FetchDeviceScene event, Emitter emit) async { + emit(FourSceneLoadingState()); + try { + var response = await DevicesAPI.getDeviceStatus(fourSceneId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = FourSceneModelState.fromJson( + statusModelList, + ); + emit(UpdateState(device: deviceStatus)); + Future.delayed(const Duration(milliseconds: 500)); + } catch (e) { + emit(FourSceneFailedState(errorMessage: e.toString())); + return; + } + } + +//===================== fetch Device Status and info ======================= + + void _toggleLowBattery( + ToggleEnableAlarmEvent event, Emitter emit) async { + emit(LoadingNewSate(device: deviceStatus)); + try { + enableAlarm = event.isLowBatteryEnabled; + emit(UpdateState(device: deviceStatus)); + } catch (e) { + emit(FourSceneFailedState(errorMessage: e.toString())); + } + } + + void _fetchDeviceStatus( + FourSceneInitial event, Emitter emit) async { + emit(FourSceneLoadingState()); + try { + var response = await DevicesAPI.getDeviceStatus(fourSceneId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = FourSceneModelState.fromJson( + statusModelList, + ); + add(const FourSceneSwitchInitial()); + } catch (e) { + emit(FourSceneFailedState(errorMessage: e.toString())); + return; + } + } + + Future fetchDeviceInfo( + FourSceneInitialInfo event, Emitter emit) async { + try { + emit(FourSceneLoadingState()); + var response = await DevicesAPI.getDeviceInfo(fourSceneId); + deviceInfo = DeviceInfoModel.fromJson(response); + deviceName = deviceInfo.name; + emit(LoadingDeviceInfo(deviceInfo: deviceInfo)); + } catch (e) { + emit(FourSceneFailedState(errorMessage: e.toString())); + } + } + +//============================ assign Device ================================== +////////////////////////////////////////////////////////////////////////////// + + void _toggleNotification( + ToggleNotificationEvent event, Emitter emit) async { + emit(LoadingNewSate(device: deviceStatus)); + try { + enableAlarm = event.isClosingEnabled; + emit(UpdateState(device: deviceStatus)); + } catch (e) { + emit(FourSceneFailedState(errorMessage: e.toString())); + } + } + + DeviceReport recordGroups = + DeviceReport(startTime: '0', endTime: '0', data: []); + + List allDevices = []; + List roomsList = []; + bool switchStatus = true; + Future changeSwitchStatus( + ChangeSwitchStatusEvent event, Emitter emit) async { + try { + emit(FourSceneLoadingState()); + switchStatus = deviceStatus.switch_backlight; + switchStatus = !switchStatus; + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: fourSceneId, + code: 'switch_backlight', + value: switchStatus), + fourSceneId); + deviceStatus.switch_backlight = switchStatus; + Future.delayed(const Duration(milliseconds: 200), () { + add(const FourSceneSwitchInitial()); + }); + Future.delayed(const Duration(milliseconds: 200), () { + emit(ChangeSwitchState(isEnable: switchStatus)); + emit(UpdateState(device: deviceStatus)); + }); + } catch (_) { + add(const FourSceneInitial()); + } + } + + Future _onLoadScenes( + LoadScenes event, Emitter emit) async { + emit(FourSceneLoadingState()); + try { + if (event.unitId.isNotEmpty) { + allScenes = await SceneApi.getScenesByUnitId( + event.unitId, event.unit.community.uuid, + showInDevice: event.showInDevice); + + filteredScenes = allScenes; + emit(SceneLoaded(allScenes)); + } else { + emit(const FourSceneFailedState(errorMessage: 'Unit ID is empty')); + } + } catch (e) { + emit(const FourSceneFailedState(errorMessage: 'Something went wrong')); + } + } + + List allScenes = []; + List filteredScenes = []; + + void searchScene(SearchScenesEvent event, Emitter emit) { + emit(FourSceneLoadingState()); + filteredScenes = event.query.isEmpty + ? allScenes + : allScenes.where((scene) { + final sceneName = scene.name?.toLowerCase() ?? ''; + return sceneName.contains(event.query.toLowerCase()); + }).toList(); + emit(SearchResultsState()); + } +} diff --git a/lib/features/devices/bloc/four_scene_bloc/four_scene_event.dart b/lib/features/devices/bloc/four_scene_bloc/four_scene_event.dart new file mode 100644 index 0000000..a543ac3 --- /dev/null +++ b/lib/features/devices/bloc/four_scene_bloc/four_scene_event.dart @@ -0,0 +1,180 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; +import 'package:syncrow_app/features/devices/model/group_devices_model.dart'; + +abstract class FourSceneEvent extends Equatable { + const FourSceneEvent(); + + @override + List get props => []; +} + +class FourSceneInitialInfo extends FourSceneEvent { + const FourSceneInitialInfo(); +} + +class FourSceneInitial extends FourSceneEvent { + const FourSceneInitial(); +} + +class SaveNameEvent extends FourSceneEvent { + final String? deviceName; + + const SaveNameEvent({this.deviceName}); +} + +class ToggleEnableAlarmEvent extends FourSceneEvent { + final bool isLowBatteryEnabled; + + const ToggleEnableAlarmEvent(this.isLowBatteryEnabled); + + @override + List get props => [isLowBatteryEnabled]; +} + +class ToggleNotificationEvent extends FourSceneEvent { + final bool isClosingEnabled; + + const ToggleNotificationEvent(this.isClosingEnabled); + + @override + List get props => [isClosingEnabled]; +} + +class DeleteDeviceEvent extends FourSceneEvent {} + +class ChangeNameEvent extends FourSceneEvent { + final bool? value; + const ChangeNameEvent({this.value}); +} + +class SearchFaqEvent extends FourSceneEvent { + final String query; + + const SearchFaqEvent(this.query); +} + +class FourSceneInitialQuestion extends FourSceneEvent { + const FourSceneInitialQuestion(); +} + +class ChangeSwitchStatusEvent extends FourSceneEvent {} + +class FetchRoomsEvent extends FourSceneEvent { + final SpaceModel unit; + + const FetchRoomsEvent({required this.unit}); + + @override + List get props => [unit]; +} + +class LoadScenes extends FourSceneEvent { + final String unitId; + final bool showInDevice; + final SpaceModel unit; + + const LoadScenes( + {required this.unitId, required this.unit, this.showInDevice = false}); + + @override + List get props => [unitId, showInDevice]; +} + +class SelectSceneEvent extends FourSceneEvent { + final String selectedSceneId; + final bool isSelected; + // final String unitId; + const SelectSceneEvent({ + // required this.unitId, + required this.selectedSceneId, + required this.isSelected, + }); +} + +class SearchScenesEvent extends FourSceneEvent { + final String query; + const SearchScenesEvent({ + required this.query, + }); +} + +class SelectOptionEvent extends FourSceneEvent { + final dynamic selectedOption; + const SelectOptionEvent({ + this.selectedOption, + }); +} + +class AddDeviceToGroup extends FourSceneEvent { + final GroupDevicesModel device; + final String icon; + const AddDeviceToGroup(this.device, this.icon); +} + +class RemoveDeviceFromGroup extends FourSceneEvent { + final GroupDevicesModel device; + final String icon; + + const RemoveDeviceFromGroup(this.device, this.icon); +} + +class AssignRoomEvent extends FourSceneEvent { + final String roomId; + final SpaceModel unit; + final BuildContext context; + + const AssignRoomEvent({ + required this.roomId, + required this.unit, + required this.context, + }); + + @override + List get props => [ + roomId, + unit, + context, + ]; +} + +class FetchDeviceScene extends FourSceneEvent {} + +class FourSceneSwitchInitial extends FourSceneEvent { + const FourSceneSwitchInitial(); +} + +class UnAssignSceneEvent extends FourSceneEvent { + final String deviceUuid; + final String switchName; + const UnAssignSceneEvent( + {required this.deviceUuid, required this.switchName}); +} + +class AssignDeviceScene extends FourSceneEvent { + final String? sceneUuid; + final String? switchName; + final SpaceModel? unit; + const AssignDeviceScene({ + this.sceneUuid, + this.unit, + this.switchName, + }); +} + +class GetSceneBySwitchName extends FourSceneEvent { + final String? switchName; + + const GetSceneBySwitchName({this.switchName}); +} + +class ToggleUpdateEvent extends FourSceneEvent { + final bool? isUpdateEnabled; + const ToggleUpdateEvent({this.isUpdateEnabled}); +} + +class ToggleHelpfulEvent extends FourSceneEvent { + final bool? isHelpful; + const ToggleHelpfulEvent({this.isHelpful}); +} diff --git a/lib/features/devices/bloc/four_scene_bloc/four_scene_state.dart b/lib/features/devices/bloc/four_scene_bloc/four_scene_state.dart new file mode 100644 index 0000000..e342989 --- /dev/null +++ b/lib/features/devices/bloc/four_scene_bloc/four_scene_state.dart @@ -0,0 +1,124 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/four_scene_model.dart'; +import 'package:syncrow_app/features/devices/model/question_model.dart'; +import 'package:syncrow_app/features/devices/model/group_devices_model.dart'; +import 'package:syncrow_app/features/devices/model/device_info_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; +import 'package:syncrow_app/features/scene/model/scenes_model.dart'; + +class FourSceneState extends Equatable { + const FourSceneState(); + + @override + List get props => []; +} + +class FourSceneLoadingState extends FourSceneState {} + +class UpdateStateList extends FourSceneState { + final List groupDevices; + final List devices; + const UpdateStateList({required this.groupDevices, required this.devices}); +} + +class FourSceneFailedState extends FourSceneState { + final String errorMessage; + + const FourSceneFailedState({required this.errorMessage}); + + @override + List get props => [errorMessage]; +} + +class UpdateState extends FourSceneState { + final FourSceneModelState device; + const UpdateState({required this.device}); + + @override + List get props => [device]; +} + +class LoadingNewSate extends FourSceneState { + final FourSceneModelState device; + const LoadingNewSate({required this.device}); + + @override + List get props => [device]; +} + +class NameEditingState extends FourSceneState { + final bool editName; + + const NameEditingState({required this.editName}); +} + +class FaqLoadedState extends FourSceneState { + final List filteredFaqQuestions; + + const FaqLoadedState({this.filteredFaqQuestions = const []}); +} + +class FaqSearchState extends FourSceneState { + final List filteredFaqQuestions; + + const FaqSearchState({this.filteredFaqQuestions = const []}); +} + +class FetchRoomsState extends FourSceneState { + final List roomsList; + final List devicesList; + + const FetchRoomsState({required this.devicesList, required this.roomsList}); + + @override + List get props => [devicesList]; +} + +class ChangeSwitchState extends FourSceneState { + final bool isEnable; + + const ChangeSwitchState({required this.isEnable}); +} + +class SceneLoaded extends FourSceneState { + final List scenes; + final String? loadingSceneId; + final Map loadingStates; + + const SceneLoaded(this.scenes, + {this.loadingSceneId, this.loadingStates = const {}}); +} + +class SearchResultsState extends FourSceneState {} + +class SaveState extends FourSceneState {} + +class SaveSelectionSuccessState extends FourSceneState {} + +class OptionSelectedState extends FourSceneState { + final String selectedOption; + final bool hasSelectionChanged; + + const OptionSelectedState({ + required this.selectedOption, + required this.hasSelectionChanged, + }); + + @override + List get props => [selectedOption, hasSelectionChanged]; +} + +class LoadingDeviceInfo extends FourSceneState { + final DeviceInfoModel deviceInfo; + const LoadingDeviceInfo({required this.deviceInfo}); + + @override + List get props => [deviceInfo]; +} + +class SceneSelectionUpdatedState extends FourSceneState { + final String selectedSceneId; + + const SceneSelectionUpdatedState({required this.selectedSceneId}); +} diff --git a/lib/features/devices/bloc/sos_bloc/sos_bloc.dart b/lib/features/devices/bloc/sos_bloc/sos_bloc.dart new file mode 100644 index 0000000..63082fa --- /dev/null +++ b/lib/features/devices/bloc/sos_bloc/sos_bloc.dart @@ -0,0 +1,422 @@ +import 'dart:async'; +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; +import 'package:syncrow_app/features/app_layout/model/community_model.dart'; +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; +import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart'; +import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; +import 'package:syncrow_app/features/devices/model/device_info_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/device_report_model.dart'; +import 'package:syncrow_app/features/devices/model/question_model.dart'; +import 'package:syncrow_app/features/devices/model/sos_model.dart'; +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; +import 'package:syncrow_app/navigation/routing_constants.dart'; +import 'package:syncrow_app/services/api/devices_api.dart'; +import 'package:syncrow_app/services/api/home_management_api.dart'; +import 'package:syncrow_app/services/api/spaces_api.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; + +class SosBloc extends Bloc { + final String sosId; + SosBloc({ + required this.sosId, + }) : super(const SosState()) { + on(_fetchStatus); + on(fetchLogsForLastMonth); + on(_toggleLowBattery); + on(_toggleClosingReminder); + on(_changeName); + on(_onSearchFaq); + on(_onSosInitial); + on(_fetchRoomsAndDevices); + on(_onOptionSelected); + on(_onSaveSelection); + on(_assignDevice); + on(fetchDeviceInfo); + on(saveName); + on(_toggleUpdate); + on(_toggleHelpful); + } + + final TextEditingController nameController = + TextEditingController(text: deviceName); + bool isSaving = false; + bool editName = false; + final FocusNode focusNode = FocusNode(); + bool enableAlarm = false; + bool closingReminder = false; + SosModel deviceStatus = + SosModel(sosContactState: 'sos', batteryPercentage: 0); + + DeviceInfoModel deviceInfo = DeviceInfoModel( + activeTime: 0, + category: "", + categoryName: "", + createTime: 0, + gatewayId: "", + icon: "", + ip: "", + lat: "", + localKey: "", + lon: "", + model: "", + name: "", + nodeId: "", + online: false, + ownerId: "", + productName: "", + sub: false, + timeZone: "", + updateTime: 0, + uuid: "", + productUuid: "", + productType: "", + permissionType: "", + macAddress: "", + subspace: Subspace( + uuid: "", + createdAt: "", + updatedAt: "", + subspaceName: "", + ), + ); + + void _toggleLowBattery( + ToggleEnableAlarmEvent event, Emitter emit) async { + emit(LoadingNewSate(sosSensor: deviceStatus)); + try { + enableAlarm = event.isLowBatteryEnabled; + emit(UpdateState(sensor: deviceStatus)); + await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: sosId, + code: 'low_battery', + value: enableAlarm, + ), + sosId, + ); + } catch (e) { + emit(SosFailedState(errorMessage: e.toString())); + } + } + + void _toggleClosingReminder( + ToggleClosingReminderEvent event, Emitter emit) async { + emit(LoadingNewSate(sosSensor: deviceStatus)); + try { + closingReminder = event.isClosingReminderEnabled; + emit(UpdateState(sensor: deviceStatus)); + } catch (e) { + emit(SosFailedState(errorMessage: e.toString())); + } + } + + DeviceReport recordGroups = + DeviceReport(startTime: '0', endTime: '0', data: []); + + Future fetchLogsForLastMonth( + ReportLogsInitial event, Emitter emit) async { + DateTime now = DateTime.now(); + DateTime lastMonth = DateTime(now.year, now.month - 1, now.day); + int startTime = lastMonth.millisecondsSinceEpoch; + int endTime = now.millisecondsSinceEpoch; + try { + emit(SosLoadingState()); + var response = await DevicesAPI.getReportLogs( + startTime: startTime.toString(), + endTime: endTime.toString(), + deviceUuid: sosId, + code: 'sos', + ); + recordGroups = response; + emit(UpdateState(sensor: deviceStatus)); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(SosFailedState(errorMessage: e.toString())); + } + } + +//========================= Device Info & Status ============================= + + static String deviceName = ''; + + fetchDeviceInfo(SosInitialDeviseInfo event, Emitter emit) async { + try { + emit(SosLoadingState()); + var response = await DevicesAPI.getDeviceInfo(sosId); + deviceInfo = DeviceInfoModel.fromJson(response); + deviceName = deviceInfo.name; + emit(LoadingSosDeviceInfo(deviceInfo: deviceInfo)); + } catch (e) { + emit(SosFailedState(errorMessage: e.toString())); + } + } + + void _fetchStatus(SosInitial event, Emitter emit) async { + emit(SosLoadingState()); + try { + var response = await DevicesAPI.getDeviceStatus(sosId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = SosModel.fromJson( + statusModelList, + ); + emit(UpdateState(sensor: deviceStatus)); + Future.delayed(const Duration(milliseconds: 500)); + } catch (e) { + emit(SosFailedState(errorMessage: e.toString())); + return; + } + } + +//========================= assign & unassign devise to room ============================= + List allDevices = []; + List roomsList = []; + void _fetchRoomsAndDevices( + FetchRoomsEvent event, Emitter emit) async { + try { + emit(SosLoadingState()); + roomsList = await SpacesAPI.getSubSpaceBySpaceId( + event.unit.community.uuid, event.unit.id); + emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList)); + } catch (e) { + emit(const SosFailedState(errorMessage: 'Something went wrong')); + return; + } + } + + String roomId = ''; + SpaceModel unit = + SpaceModel(id: '', name: '', community: Community(uuid: '', name: '')); + + String _selectedOption = ''; + bool _hasSelectionChanged = false; + + void _onOptionSelected(SelectOptionEvent event, Emitter emit) { + emit(SosLoadingState()); + _selectedOption = event.selectedOption; + _hasSelectionChanged = true; + emit(OptionSelectedState( + selectedOption: _selectedOption, + hasSelectionChanged: _hasSelectionChanged)); + } + + void _onSaveSelection(SaveSelectionEvent event, Emitter emit) { + if (_hasSelectionChanged) { + _hasSelectionChanged = false; + add(AssignRoomEvent(roomId: roomId, unit: unit, context: event.context)); + emit(SaveSelectionSuccessState()); + var cubit = HomeCubit.getInstance(); + cubit.updatePageIndex(1); + Navigator.pushReplacementNamed(event.context, Routes.homeRoute); + } + } + + void _assignDevice(AssignRoomEvent event, Emitter emit) async { + try { + emit(SosLoadingState()); + + await HomeManagementAPI.assignDeviceToRoom( + event.unit.community.uuid, event.unit.id, event.roomId, sosId); + final devicesList = await DevicesAPI.getDevicesByRoomId( + communityUuid: event.unit.community.uuid, + spaceUuid: event.unit.id, + roomId: event.roomId); + + List allDevicesIds = []; + + allDevices.forEach((element) { + allDevicesIds.add(element.uuid!); + }); + await HomeCubit.getInstance().fetchUnitsByUserId(); + CustomSnackBar.displaySnackBar('Save Successfully'); + + emit(SaveSelectionSuccessState()); + } catch (e) { + emit(const SosFailedState(errorMessage: 'Something went wrong')); + return; + } + } + + void _unassignDevice(UnassignRoomEvent event, Emitter emit) async { + try { + Map roomDevicesId = {}; + emit(SosLoadingState()); + await HomeManagementAPI.unAssignDeviceToRoom( + event.unit.community.uuid, event.unit.id, event.roomId, sosId); + final devicesList = await DevicesAPI.getDevicesByRoomId( + communityUuid: event.unit.community.uuid, + spaceUuid: event.unit.id, + roomId: event.roomId); + + List allDevicesIds = []; + + allDevices.forEach((element) { + allDevicesIds.add(element.uuid!); + }); + + devicesList.forEach((e) { + if (allDevicesIds.contains(e.uuid!)) { + roomDevicesId[e.uuid!] = true; + } else { + roomDevicesId[e.uuid!] = false; + } + }); + } catch (e) { + emit(const SosFailedState(errorMessage: 'Something went wrong')); + return; + } + } + + Map> devicesByRoom = {}; + +//======================= setting name ====================================== + + Future saveName(SaveNameEvent event, Emitter emit) async { + if (_validateInputs()) return; + try { + add(const ChangeNameEvent(value: false)); + isSaving = true; + emit(SosLoadingState()); + var response = await DevicesAPI.putDeviceName( + deviceId: sosId, deviceName: nameController.text); + add(SosInitialDeviseInfo()); + CustomSnackBar.displaySnackBar('Save Successfully'); + emit(SaveState()); + } catch (e) { + emit(SosFailedState(errorMessage: e.toString())); + } finally { + isSaving = false; + } + } + + bool _validateInputs() { + final nameError = fullNameValidator(nameController.text); + if (nameError != null) { + CustomSnackBar.displaySnackBar(nameError); + return true; + } + return false; + } + + String? fullNameValidator(String? value) { + if (value == null) return 'name is required'; + final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim(); + if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) { + return 'name must be between 2 and 30 characters long'; + } + if (RegExp(r"/[^ a-zA-Z0-9-\']/").hasMatch(withoutExtraSpaces)) { + return 'Only alphanumeric characters, space, dash and single quote are allowed'; + } + return null; + } + + void _changeName(ChangeNameEvent event, Emitter emit) { + emit(SosLoadingState()); + editName = event.value!; + if (editName) { + Future.delayed(const Duration(milliseconds: 500), () { + focusNode.requestFocus(); + }); + } else { + focusNode.unfocus(); + } + emit(NameEditingState(editName: editName)); + } + +//============================== update setting =============================== + bool enableUpdate = false; + + void _toggleUpdate(ToggleUpdateEvent event, Emitter emit) async { + try { + emit(SosLoadingState()); + enableUpdate = event.isUpdateEnabled!; + emit(SaveState()); + } catch (e) { + emit(SosFailedState(errorMessage: e.toString())); + } + } + + //============================ Question and faq ============================ + + final List faqQuestions = [ + QuestionModel( + id: 1, + question: 'How does an SOS emergency button work?', + answer: + 'The SOS emergency button sends an alert to your contacts when pressed.', + ), + QuestionModel( + id: 2, + question: 'How long will an SOS alarm persist?', + answer: + 'The SOS alarm will persist until it is manually turned off or after a set time.', + ), + QuestionModel( + id: 3, + question: 'What should I do if the SOS button is unresponsive?', + answer: 'Try restarting the device. If it persists, contact support.', + ), + QuestionModel( + id: 4, + question: 'Can I use the SOS feature without a network connection?', + answer: + 'No, a network connection is required to send the alert to your contacts.', + ), + QuestionModel( + id: 5, + question: 'How often should I check the SOS battery?', + answer: + 'Check the SOS battery at least once a month to ensure it is operational.', + ), + ]; + + Future _onSosInitial( + SosInitialQuestion event, Emitter emit) async { + emit(SosLoadingState()); + emit(FaqLoadedState(filteredFaqQuestions: faqQuestions)); + } + + void _onSearchFaq(SearchFaqEvent event, Emitter emit) { + emit(SosLoadingState()); + List _faqQuestions = faqQuestions.where((question) { + return question.question + .toLowerCase() + .contains(event.query.toLowerCase()); + }).toList(); + emit(FaqSearchState(filteredFaqQuestions: _faqQuestions)); + } + + bool isHelpful = false; + void _toggleHelpful(ToggleHelpfulEvent event, Emitter emit) async { + try { + emit(SosLoadingState()); + isHelpful = event.isHelpful!; + emit(SaveState()); + } catch (e) { + emit(SosFailedState(errorMessage: e.toString())); + } + } + +//===================== delete Device =================================== + + deleteDevice(DeleteDeviceEvent event, Emitter emit) async { + try { + emit(SosLoadingState()); + var response = await DevicesAPI.resetDevise(devicesUuid: sosId); + add(SosInitialDeviseInfo()); + add(const SosInitial()); + CustomSnackBar.displaySnackBar('Reset Successfully'); + } catch (e) { + emit(SosFailedState(errorMessage: e.toString())); + } + } +} diff --git a/lib/features/devices/bloc/sos_bloc/sos_event.dart b/lib/features/devices/bloc/sos_bloc/sos_event.dart new file mode 100644 index 0000000..79ad27a --- /dev/null +++ b/lib/features/devices/bloc/sos_bloc/sos_event.dart @@ -0,0 +1,131 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; +import 'package:syncrow_app/features/devices/model/device_info_model.dart'; + +abstract class SosEvent extends Equatable { + const SosEvent(); + + @override + List get props => []; +} + +class DeleteDeviceEvent extends SosEvent {} + +class SosInitialDeviseInfo extends SosEvent {} + +class SosInitial extends SosEvent { + const SosInitial(); +} + +class ReportLogsInitial extends SosEvent { + const ReportLogsInitial(); +} + +class ToggleEnableAlarmEvent extends SosEvent { + final bool isLowBatteryEnabled; + + const ToggleEnableAlarmEvent(this.isLowBatteryEnabled); + + @override + List get props => [isLowBatteryEnabled]; +} + +class ToggleClosingReminderEvent extends SosEvent { + final bool isClosingReminderEnabled; + + const ToggleClosingReminderEvent(this.isClosingReminderEnabled); + + @override + List get props => [isClosingReminderEnabled]; +} + +class SaveNameEvent extends SosEvent {} + +class ChangeNameEvent extends SosEvent { + final bool? value; + const ChangeNameEvent({this.value}); +} + +class SearchFaqEvent extends SosEvent { + final String query; + + const SearchFaqEvent(this.query); +} + +class SosInitialQuestion extends SosEvent { + const SosInitialQuestion(); +} + +class FetchRoomsEvent extends SosEvent { + final SpaceModel unit; + + const FetchRoomsEvent({required this.unit}); + + @override + List get props => [unit]; +} + +class SelectOptionEvent extends SosEvent { + dynamic selectedOption; + SelectOptionEvent({ + this.selectedOption, + }); +} + +class SaveSelectionEvent extends SosEvent { + BuildContext context; + SaveSelectionEvent({ + required this.context, + }); +} + +class AssignRoomEvent extends SosEvent { + final String roomId; + final SpaceModel unit; + final BuildContext context; + + const AssignRoomEvent({ + required this.roomId, + required this.unit, + required this.context, + }); + + @override + List get props => [ + roomId, + unit, + context, + ]; +} + +class UnassignRoomEvent extends SosEvent { + final String roomId; + final String deviceId; + final SpaceModel unit; + + const UnassignRoomEvent( + {required this.roomId, required this.deviceId, required this.unit}); + + @override + List get props => [roomId, unit]; +} + +class LoadingDeviceInfo extends SosEvent { + DeviceInfoModel deviceInfo; + LoadingDeviceInfo({required this.deviceInfo}); + + @override + List get props => [deviceInfo]; +} + +class ToggleUpdateEvent extends SosEvent { + final bool? isUpdateEnabled; + const ToggleUpdateEvent({this.isUpdateEnabled}); +} + +class ToggleHelpfulEvent extends SosEvent { + final bool? isHelpful; + const ToggleHelpfulEvent({this.isHelpful}); +} diff --git a/lib/features/devices/bloc/sos_bloc/sos_state.dart b/lib/features/devices/bloc/sos_bloc/sos_state.dart new file mode 100644 index 0000000..cd010cc --- /dev/null +++ b/lib/features/devices/bloc/sos_bloc/sos_state.dart @@ -0,0 +1,93 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/device_info_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/question_model.dart'; +import 'package:syncrow_app/features/devices/model/sos_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; + +class SosState extends Equatable { + const SosState(); + + @override + List get props => []; +} + +class SosLoadingState extends SosState {} + +class SaveState extends SosState {} + +class LoadingSosDeviceInfo extends SosState { + final DeviceInfoModel? deviceInfo; + const LoadingSosDeviceInfo({this.deviceInfo}); +} + +class SosFailedState extends SosState { + final String errorMessage; + + const SosFailedState({required this.errorMessage}); + + @override + List get props => [errorMessage]; +} + +class UpdateState extends SosState { + final SosModel sensor; + const UpdateState({required this.sensor}); + + @override + List get props => [sensor]; +} + +class LoadingNewSate extends SosState { + final SosModel sosSensor; + const LoadingNewSate({required this.sosSensor}); + + @override + List get props => [sosSensor]; +} + +class NameEditingState extends SosState { + final bool editName; + + NameEditingState({required this.editName}); +} + +class FaqLoadedState extends SosState { + final List filteredFaqQuestions; + + FaqLoadedState({this.filteredFaqQuestions = const []}); +} + +class FaqSearchState extends SosState { + final List filteredFaqQuestions; + + const FaqSearchState({this.filteredFaqQuestions = const []}); +} + +class FetchRoomsState extends SosState { + final List roomsList; + final List devicesList; + + const FetchRoomsState({required this.devicesList, required this.roomsList}); + + @override + List get props => [devicesList]; +} + +class OptionSelectedState extends SosState { + final String selectedOption; + final bool hasSelectionChanged; + + OptionSelectedState({ + required this.selectedOption, + required this.hasSelectionChanged, + }); + + @override + List get props => [selectedOption, hasSelectionChanged]; +} + +class SaveSelectionSuccessState extends SosState { + @override + List get props => []; +} diff --git a/lib/features/devices/model/device_info_model.dart b/lib/features/devices/model/device_info_model.dart new file mode 100644 index 0000000..9c01d70 --- /dev/null +++ b/lib/features/devices/model/device_info_model.dart @@ -0,0 +1,149 @@ +import 'dart:convert'; + +class DeviceInfoModel { + final int activeTime; + final String category; + final String categoryName; + final int createTime; + final String gatewayId; + final String icon; + final String ip; + final String lat; + final String localKey; + final String lon; + final String model; + final String name; + final String nodeId; + final bool online; + final String ownerId; + final String productName; + final bool sub; + final String timeZone; + final int updateTime; + final String uuid; + final String productUuid; + final String productType; + final String permissionType; + final String macAddress; + final Subspace subspace; + + DeviceInfoModel({ + required this.activeTime, + required this.category, + required this.categoryName, + required this.createTime, + required this.gatewayId, + required this.icon, + required this.ip, + required this.lat, + required this.localKey, + required this.lon, + required this.model, + required this.name, + required this.nodeId, + required this.online, + required this.ownerId, + required this.productName, + required this.sub, + required this.timeZone, + required this.updateTime, + required this.uuid, + required this.productUuid, + required this.productType, + required this.permissionType, + required this.macAddress, + required this.subspace, + }); + + factory DeviceInfoModel.fromJson(Map json) { + return DeviceInfoModel( + activeTime: json['activeTime'], + category: json['category'], + categoryName: json['categoryName'], + createTime: json['createTime'], + gatewayId: json['gatewayId'], + icon: json['icon'], + ip: json['ip'] ?? "", + lat: json['lat'], + localKey: json['localKey'], + lon: json['lon'], + model: json['model'], + name: json['name'], + nodeId: json['nodeId'], + online: json['online'], + ownerId: json['ownerId'], + productName: json['productName'], + sub: json['sub'], + timeZone: json['timeZone'], + updateTime: json['updateTime'], + uuid: json['uuid'], + productUuid: json['productUuid'], + productType: json['productType'], + permissionType: json['permissionType'], + macAddress: json['macAddress'], + subspace: Subspace.fromJson(json['subspace']), + ); + } + + Map toJson() { + return { + 'activeTime': activeTime, + 'category': category, + 'categoryName': categoryName, + 'createTime': createTime, + 'gatewayId': gatewayId, + 'icon': icon, + 'ip': ip, + 'lat': lat, + 'localKey': localKey, + 'lon': lon, + 'model': model, + 'name': name, + 'nodeId': nodeId, + 'online': online, + 'ownerId': ownerId, + 'productName': productName, + 'sub': sub, + 'timeZone': timeZone, + 'updateTime': updateTime, + 'uuid': uuid, + 'productUuid': productUuid, + 'productType': productType, + 'permissionType': permissionType, + 'macAddress': macAddress, + 'subspace': subspace.toJson(), + }; + } +} + +class Subspace { + final String uuid; + final String createdAt; + final String updatedAt; + final String subspaceName; + + Subspace({ + required this.uuid, + required this.createdAt, + required this.updatedAt, + required this.subspaceName, + }); + + factory Subspace.fromJson(Map json) { + return Subspace( + uuid: json['uuid'], + createdAt: json['createdAt'], + updatedAt: json['updatedAt'], + subspaceName: json['subspaceName'], + ); + } + + Map toJson() { + return { + 'uuid': uuid, + 'createdAt': createdAt, + 'updatedAt': updatedAt, + 'subspaceName': subspaceName, + }; + } +} diff --git a/lib/features/devices/model/device_model.dart b/lib/features/devices/model/device_model.dart index f1af2f3..557e83f 100644 --- a/lib/features/devices/model/device_model.dart +++ b/lib/features/devices/model/device_model.dart @@ -78,6 +78,12 @@ class DeviceModel { tempIcon = Assets.waterLeakIcon; } else if (type == DeviceType.PC) { tempIcon = Assets.powerClampIcon; + } else if (type == DeviceType.FourScene) { + tempIcon = Assets.fourSceneHomeIcon; + } else if (type == DeviceType.SixScene) { + tempIcon = Assets.sixSceneHomeIcon; + } else if (type == DeviceType.SOS) { + tempIcon = Assets.sosHomeIcon; } else { tempIcon = Assets.assetsIconsLogo; } diff --git a/lib/features/devices/model/four_scene_model.dart b/lib/features/devices/model/four_scene_model.dart new file mode 100644 index 0000000..8bf1ce0 --- /dev/null +++ b/lib/features/devices/model/four_scene_model.dart @@ -0,0 +1,52 @@ +import 'package:syncrow_app/features/devices/model/status_model.dart'; + +class FourSceneModelState { + dynamic scene_1; + dynamic scene_2; + dynamic scene_3; + dynamic scene_4; + dynamic scene_id_group_id; + dynamic switch_backlight; + + FourSceneModelState({ + required this.scene_1, + required this.scene_2, + required this.scene_3, + required this.scene_4, + required this.scene_id_group_id, + required this.switch_backlight, + }); + + factory FourSceneModelState.fromJson(List jsonList) { + late dynamic _scene_1; + late dynamic _scene_2; + late dynamic _scene_3; + late dynamic _scene_4; + late dynamic _scene_id_group_id; + late dynamic _switch_backlight; + + for (int i = 0; i < jsonList.length; i++) { + if (jsonList[i].code == 'scene_1') { + _scene_1 = jsonList[i].value ?? ''; + } else if (jsonList[i].code == 'scene_2') { + _scene_2 = jsonList[i].value ?? ''; + } else if (jsonList[i].code == 'scene_3') { + _scene_3 = jsonList[i].value ?? ''; + } else if (jsonList[i].code == 'scene_4') { + _scene_4 = jsonList[i].value ?? ''; + } else if (jsonList[i].code == 'scene_id_group_id') { + _scene_id_group_id = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'switch_backlight') { + _switch_backlight = jsonList[i].value ?? false; + } + } + return FourSceneModelState( + scene_1: _scene_1, + scene_2: _scene_2, + scene_3: _scene_3, + scene_4: _scene_4, + scene_id_group_id: _scene_id_group_id, + switch_backlight: _switch_backlight, + ); + } +} diff --git a/lib/features/devices/model/four_scene_switch_model.dart b/lib/features/devices/model/four_scene_switch_model.dart new file mode 100644 index 0000000..d837262 --- /dev/null +++ b/lib/features/devices/model/four_scene_switch_model.dart @@ -0,0 +1,85 @@ +class FourSceneSwitchModel { + final String switchName; + final DateTime createdAt; + final DateTime updatedAt; + final String deviceUuid; + final Scene scene; + + FourSceneSwitchModel({ + required this.switchName, + required this.createdAt, + required this.updatedAt, + required this.deviceUuid, + required this.scene, + }); + + factory FourSceneSwitchModel.fromJson(Map json) { + return FourSceneSwitchModel( + switchName: json['switchName'], + createdAt: DateTime.parse(json['createdAt']), + updatedAt: DateTime.parse(json['updatedAt']), + deviceUuid: json['deviceUuid'], + scene: Scene.fromJson(json['scene']), + ); + } +} + +class Scene { + final String uuid; + final String sceneTuyaId; + final String name; + final String status; + final String icon; + final String iconUuid; + final bool showInHome; + final String type; + final List actions; + + Scene({ + required this.uuid, + required this.sceneTuyaId, + required this.name, + required this.status, + required this.icon, + required this.iconUuid, + required this.showInHome, + required this.type, + required this.actions, + }); + + factory Scene.fromJson(Map json) { + return Scene( + uuid: json['uuid'], + sceneTuyaId: json['sceneTuyaId'], + name: json['name'], + status: json['status'], + icon: json['icon'], + iconUuid: json['iconUuid'], + showInHome: json['showInHome'], + type: json['type'], + actions: (json['actions'] as List) + .map((action) => Action.fromJson(action)) + .toList(), + ); + } +} + +class Action { + final String actionExecutor; + final String entityId; + final Map executorProperty; + + Action({ + required this.actionExecutor, + required this.entityId, + required this.executorProperty, + }); + + factory Action.fromJson(Map json) { + return Action( + actionExecutor: json['actionExecutor'], + entityId: json['entityId'], + executorProperty: json['executorProperty'], + ); + } +} diff --git a/lib/features/devices/model/group_devices_model.dart b/lib/features/devices/model/group_devices_model.dart new file mode 100644 index 0000000..b0a36d3 --- /dev/null +++ b/lib/features/devices/model/group_devices_model.dart @@ -0,0 +1,6 @@ +class GroupDevicesModel { + String? icon; + final String? name; + final String? dec; + GroupDevicesModel({this.icon, this.name, this.dec}); +} diff --git a/lib/features/devices/model/question_model.dart b/lib/features/devices/model/question_model.dart new file mode 100644 index 0000000..2965620 --- /dev/null +++ b/lib/features/devices/model/question_model.dart @@ -0,0 +1,11 @@ +class QuestionModel { + final int id; + final String question; + final String answer; + + QuestionModel({ + required this.id, + required this.question, + required this.answer, + }); +} diff --git a/lib/features/devices/model/six_scene_model.dart b/lib/features/devices/model/six_scene_model.dart new file mode 100644 index 0000000..c3c01b5 --- /dev/null +++ b/lib/features/devices/model/six_scene_model.dart @@ -0,0 +1,64 @@ +import 'package:syncrow_app/features/devices/model/status_model.dart'; + +class SixSceneModel { + dynamic scene_1; + dynamic scene_2; + dynamic scene_3; + dynamic scene_4; + dynamic scene_5; + dynamic scene_6; + dynamic scene_id_group_id; + dynamic switch_backlight; + + SixSceneModel({ + required this.scene_1, + required this.scene_2, + required this.scene_3, + required this.scene_4, + required this.scene_5, + required this.scene_6, + required this.scene_id_group_id, + required this.switch_backlight, + }); + + factory SixSceneModel.fromJson(List jsonList) { + late dynamic _scene_1; + late dynamic _scene_2; + late dynamic _scene_3; + late dynamic _scene_4; + late dynamic _scene_5; + late dynamic _scene_6; + late dynamic _scene_id_group_id; + late dynamic _switch_backlight; + + for (int i = 0; i < jsonList.length; i++) { + if (jsonList[i].code == 'scene_1') { + _scene_1 = jsonList[i].value ?? ''; + } else if (jsonList[i].code == 'scene_2') { + _scene_2 = jsonList[i].value ?? ''; + } else if (jsonList[i].code == 'scene_3') { + _scene_3 = jsonList[i].value ?? ''; + } else if (jsonList[i].code == 'scene_4') { + _scene_4 = jsonList[i].value ?? ''; + } else if (jsonList[i].code == 'scene_5') { + _scene_5 = jsonList[i].value ?? ''; + } else if (jsonList[i].code == 'scene_6') { + _scene_6 = jsonList[i].value ?? ''; + } else if (jsonList[i].code == 'scene_id_group_id') { + _scene_id_group_id = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'switch_backlight') { + _switch_backlight = jsonList[i].value ?? false; + } + } + return SixSceneModel( + scene_1: _scene_1, + scene_2: _scene_2, + scene_3: _scene_3, + scene_4: _scene_4, + scene_5: _scene_5, + scene_6: _scene_6, + scene_id_group_id: _scene_id_group_id, + switch_backlight: _switch_backlight, + ); + } +} diff --git a/lib/features/devices/model/six_scene_question_model.dart b/lib/features/devices/model/six_scene_question_model.dart new file mode 100644 index 0000000..6822d93 --- /dev/null +++ b/lib/features/devices/model/six_scene_question_model.dart @@ -0,0 +1,11 @@ +class SixSceneQuestionModel { + final int id; + final String question; + final String answer; + + SixSceneQuestionModel({ + required this.id, + required this.question, + required this.answer, + }); +} diff --git a/lib/features/devices/model/sos_model.dart b/lib/features/devices/model/sos_model.dart new file mode 100644 index 0000000..7534b60 --- /dev/null +++ b/lib/features/devices/model/sos_model.dart @@ -0,0 +1,28 @@ +import 'package:syncrow_app/features/devices/model/status_model.dart'; + +class SosModel { + String sosContactState; + int batteryPercentage; + + SosModel({ + required this.sosContactState, + required this.batteryPercentage, + }); + + factory SosModel.fromJson(List jsonList) { + late String _sosContactState; + late int _batteryPercentage; + + for (int i = 0; i < jsonList.length; i++) { + if (jsonList[i].code == 'sos') { + _sosContactState = jsonList[i].value ?? ''; + } else if (jsonList[i].code == 'battery_percentage') { + _batteryPercentage = jsonList[i].value ?? 0; + } + } + return SosModel( + sosContactState: _sosContactState, + batteryPercentage: _batteryPercentage, + ); + } +} diff --git a/lib/features/devices/view/device_settings/create_group.dart b/lib/features/devices/view/device_settings/create_group.dart new file mode 100644 index 0000000..a756ca1 --- /dev/null +++ b/lib/features/devices/view/device_settings/create_group.dart @@ -0,0 +1,200 @@ +// import 'package:flutter/material.dart'; +// import 'package:flutter_bloc/flutter_bloc.dart'; +// import 'package:flutter_svg/svg.dart'; +// import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart'; +// import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_event.dart'; +// import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_state.dart'; +// import 'package:syncrow_app/features/devices/model/device_model.dart'; +// import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +// import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +// import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +// import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +// import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +// import 'package:syncrow_app/generated/assets.dart'; +// import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +// class FourSceneCreateGroup extends StatelessWidget { +// final DeviceModel? device; + +// const FourSceneCreateGroup({super.key, this.device}); + +// @override +// Widget build(BuildContext context) { +// return DefaultScaffold( +// title: 'Create Group', +// child: BlocProvider( +// create: (context) => FourSceneBloc(fourSceneId: device?.uuid ?? '') +// ..add(const FourSceneInitial()), +// child: BlocBuilder( +// builder: (context, state) { +// final sensor = BlocProvider.of(context); +// return state is LoadingNewSate +// ? const Center( +// child: DefaultContainer( +// width: 50, +// height: 50, +// child: CircularProgressIndicator()), +// ) +// : Padding( +// padding: const EdgeInsets.all(8.0), +// child: Column( +// children: [ +// const Padding( +// padding: EdgeInsets.only(left: 25, right: 25), +// child: BodySmall( +// text: +// 'Devices in the same group can be controlled together', +// fontColor: ColorsManager.primaryTextColor, +// textAlign: TextAlign.center, +// ), +// ), +// Flexible( +// child: ListView.builder( +// itemCount: sensor.groupDevices.length, +// itemBuilder: (context, index) { +// return InkWell( +// onTap: () { +// BlocProvider.of(context).add( +// RemoveDeviceFromGroup( +// sensor.groupDevices[index], +// Assets.addDevicesIcon)); +// }, +// child: DefaultContainer( +// child: Padding( +// padding: const EdgeInsets.all(5.0), +// child: Row( +// crossAxisAlignment: +// CrossAxisAlignment.start, +// mainAxisAlignment: +// MainAxisAlignment.spaceBetween, +// children: [ +// Row( +// children: [ +// SvgPicture.asset( +// sensor.groupDevices[index].icon!, +// fit: BoxFit.contain, +// ), +// const SizedBox( +// width: 15, +// ), +// BodyMedium( +// text: sensor +// .groupDevices[index].name!, +// fontColor: +// ColorsManager.primaryTextColor, +// textAlign: TextAlign.center, +// fontSize: 15, +// ), +// ], +// ), +// BodyMedium( +// text: sensor.groupDevices[index].dec!, +// fontColor: ColorsManager.grayColor, +// textAlign: TextAlign.center, +// fontSize: 15, +// ), +// ], +// ), +// )), +// ); +// }, +// ), +// ), +// Flexible( +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// const BodyLarge( +// text: 'Devices to be added', +// fontColor: ColorsManager.grayColor, +// textAlign: TextAlign.center, +// fontSize: 12, +// fontWeight: FontWeight.w700, +// ), +// const SizedBox( +// height: 5, +// ), +// sensor.devices.isNotEmpty +// ? Expanded( +// child: ListView.builder( +// itemCount: sensor.devices.length, +// itemBuilder: (context, index) { +// final device = sensor.devices[index]; +// return GestureDetector( +// onTap: () { +// BlocProvider.of( +// context) +// .add(AddDeviceToGroup(device, +// Assets.minusIcon)); +// }, +// child: DefaultContainer( +// child: Padding( +// padding: +// const EdgeInsets.all(5.0), +// child: Row( +// crossAxisAlignment: +// CrossAxisAlignment.start, +// mainAxisAlignment: +// MainAxisAlignment +// .spaceBetween, +// children: [ +// Row( +// children: [ +// SvgPicture.asset( +// device.icon!, +// fit: BoxFit.contain, +// ), +// const SizedBox( +// width: 15, +// ), +// BodyMedium( +// text: device.name!, +// fontColor: ColorsManager +// .primaryTextColor, +// textAlign: +// TextAlign.center, +// fontSize: 15, +// ), +// ], +// ), +// BodyMedium( +// text: device.dec!, +// fontColor: ColorsManager +// .grayColor, +// textAlign: +// TextAlign.center, +// fontSize: 15, +// ), +// ], +// ), +// ), +// ), +// ); +// }, +// ), +// ) +// : const Column( +// children: [ +// BodySmall( +// text: +// 'Currently no devices available to create group', +// fontColor: ColorsManager.grayColor, +// textAlign: TextAlign.center, +// fontSize: 12, +// fontWeight: FontWeight.w400, +// ), +// ], +// ), +// ], +// ), +// ), +// const Spacer() +// ], +// ), +// ); +// }, +// ), +// ), +// ); +// } +// } diff --git a/lib/features/devices/view/device_settings/faq_page.dart b/lib/features/devices/view/device_settings/faq_page.dart new file mode 100644 index 0000000..c1f64a3 --- /dev/null +++ b/lib/features/devices/view/device_settings/faq_page.dart @@ -0,0 +1,154 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/question_model.dart'; +import 'package:syncrow_app/features/devices/view/device_settings/question_page.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class FaqSettingPage extends StatelessWidget { + final DeviceModel? device; + + const FaqSettingPage({super.key, this.device}); + + @override + Widget build(BuildContext context) { + TextEditingController _searchController = TextEditingController(); + return DefaultScaffold( + title: 'FAQ', + child: BlocProvider( + create: (context) => DeviceSettingBloc(deviceId: device?.uuid ?? '') + ..add(const DeviceSettingInitialQuestion()), + child: BlocBuilder( + builder: (context, state) { + final sensor = BlocProvider.of(context); + List displayedQuestions = []; + if (state is FaqSearchState) { + displayedQuestions = state.filteredFaqQuestions; + } else if (state is FaqLoadedState) { + displayedQuestions = state.filteredFaqQuestions; + } + return state is DeviceSettingLoadingState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + DefaultContainer( + padding: const EdgeInsets.all(5), + child: TextFormField( + controller: _searchController, + onChanged: (value) { + sensor.add(SearchFaqEvent(value)); + }, + decoration: InputDecoration( + hintText: 'Enter your questions', + hintStyle: const TextStyle( + color: ColorsManager.textGray, + fontSize: 16, + fontWeight: FontWeight.w400), + suffixIcon: Container( + padding: const EdgeInsets.all(5.0), + margin: const EdgeInsets.all(10.0), + child: SvgPicture.asset( + Assets.searchIcon, + fit: BoxFit.contain, + ), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + ), + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.04, + ), + BodyMedium( + text: _searchController.text.isEmpty + ? 'Device Related FAQs' + : '${displayedQuestions.length} Help Topics', + fontWeight: FontWeight.w700, + fontSize: 12, + fontColor: ColorsManager.grayColor, + ), + const SizedBox( + height: 8, + ), + displayedQuestions.isEmpty + ? const SizedBox() + : DefaultContainer( + child: ListView.builder( + shrinkWrap: true, + itemCount: displayedQuestions.length, + itemBuilder: (context, index) { + final faq = displayedQuestions[index]; + return InkWell( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + QuestionPageSetting( + deviceId: device!.uuid, + questionModel: faq, + )), + ); + }, + child: SizedBox( + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Expanded( + child: BodyMedium( + fontSize: 14, + fontWeight: FontWeight.w400, + text: faq.question, + ), + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ), + ), + if (index != + displayedQuestions.length - + 1) // Exclude divider for the last item + const Divider( + color: ColorsManager.dividerColor, + ), + ], + ), + ), + ); + }, + )), + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/features/devices/view/device_settings/info_page.dart b/lib/features/devices/view/device_settings/info_page.dart new file mode 100644 index 0000000..9deac99 --- /dev/null +++ b/lib/features/devices/view/device_settings/info_page.dart @@ -0,0 +1,141 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class SettingInfoPage extends StatelessWidget { + final DeviceModel? device; + + const SettingInfoPage({super.key, this.device}); + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Device Information', + child: BlocProvider( + create: (context) => DeviceSettingBloc(deviceId: device?.uuid ?? '') + ..add(const DeviceSettingInitial()) + ..add(const DeviceSettingInitialInfo()), + child: BlocBuilder( + builder: (context, state) { + final _bloc = BlocProvider.of(context); + return state is DeviceSettingLoadingState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : RefreshIndicator( + onRefresh: () async { + _bloc.add(const DeviceSettingInitial()); + }, + child: DefaultContainer( + child: Padding( + padding: const EdgeInsets.only(left: 5, right: 5), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const BodyLarge( + text: 'Virtual ID', + fontSize: 15, + fontWeight: FontWeight.w400, + fontColor: ColorsManager.blackColor, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox( + width: + MediaQuery.of(context).size.width * 0.61, + child: BodySmall( + text: _bloc.deviceInfo.productUuid, + fontColor: ColorsManager.primaryTextColor, + ), + ), + InkWell( + onTap: () { + Clipboard.setData( + ClipboardData( + text: _bloc.deviceInfo.productUuid, + ), + ); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Copied to Clipboard"), + ), + ); + }, + child: const Row( + children: [ + Icon( + Icons.copy, + color: ColorsManager.blueColor, + ), + BodyMedium( + text: 'Copy', + fontColor: ColorsManager.blueColor, + ), + ], + ), + ) + ], + ), + const Divider( + color: ColorsManager.dividerColor, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + text: 'MAC', + fontSize: 15, + fontWeight: FontWeight.w400, + fontColor: ColorsManager.blackColor, + ), + BodySmall( + text: _bloc.deviceInfo.macAddress, + fontColor: ColorsManager.primaryTextColor, + ), + ], + ), + const Divider( + color: ColorsManager.dividerColor, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + text: 'Time Zone', + fontSize: 15, + fontWeight: FontWeight.w400, + fontColor: ColorsManager.blackColor, + ), + BodySmall( + text: _bloc.deviceInfo.timeZone, + fontColor: ColorsManager.primaryTextColor, + ), + ], + ), + ]), + ))); + }, + ), + ), + ); + } +} diff --git a/lib/features/devices/view/device_settings/location_setting.dart b/lib/features/devices/view/device_settings/location_setting.dart new file mode 100644 index 0000000..ba668e8 --- /dev/null +++ b/lib/features/devices/view/device_settings/location_setting.dart @@ -0,0 +1,210 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_state.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class LocationSettingPage extends StatelessWidget { + final SpaceModel? space; + final String? deviceId; + + const LocationSettingPage({ + super.key, + this.space, + this.deviceId, + }); + + @override + Widget build(BuildContext context) { + String roomIdSelected = ''; + + return Scaffold( + body: BlocProvider( + create: (context) => DeviceSettingBloc(deviceId: deviceId ?? '') + ..add(const DeviceSettingInitial()) + ..add(const DeviceSettingInitialInfo()) + ..add(FetchRoomsEvent(unit: space!)), + child: BlocBuilder( + builder: (context, state) { + final _bloc = BlocProvider.of(context); + if (state is SaveSelectionSuccessState) { + Future.delayed(const Duration(microseconds: 500), () { + _bloc.add(const DeviceSettingInitialInfo()); + Navigator.of(context).pop(true); + }); + } + return state is DeviceSettingLoadingState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator(), + ), + ) + : DefaultScaffold( + actions: [ + BlocBuilder( + builder: (context, state) { + final bool canSave = state is OptionSelectedState && + state.hasSelectionChanged; + return InkWell( + onTap: canSave + ? () { + context.read().add( + AssignRoomEvent( + context: context, + roomId: roomIdSelected, + unit: space!)); + } + : null, + child: BodyMedium( + text: 'Save', + fontWeight: FontWeight.w700, + fontSize: 16, + fontColor: canSave + ? ColorsManager.slidingBlueColor + : ColorsManager.primaryTextColor, + ), + ); + }, + ), + const SizedBox(width: 20), + ], + child: ListView( + shrinkWrap: true, + padding: const EdgeInsets.symmetric(vertical: 20), + children: [ + const BodyMedium( + text: 'Smart Device Location', + fontWeight: FontWeight.w700, + fontSize: 12, + fontColor: ColorsManager.grayColor, + ), + const SizedBox(height: 5), + DefaultContainer( + padding: const EdgeInsets.all(20), + child: ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: _bloc.roomsList.length, + itemBuilder: (context, index) { + final fromRoom = _bloc.roomsList[index]; + final isSelected = (state + is OptionSelectedState && + state.selectedOption == fromRoom.id) || + (state is! OptionSelectedState && + fromRoom.id == + _bloc.deviceInfo.subspace.uuid); + + return Column( + children: [ + _buildCheckboxOption( + label: fromRoom.name!, + isSelected: isSelected, + onTap: (label) { + context.read().add( + SelectOptionEvent( + selectedOption: fromRoom.id!, + ), + ); + roomIdSelected = fromRoom.id!; + }, + ), + if (index < _bloc.roomsList.length - 1) ...[ + const SizedBox(height: 10), + const Divider( + color: ColorsManager.dividerColor, + ), + const SizedBox(height: 10), + ], + ], + ); + }, + ), + ), + ], + ), + ); + }, + ), + ), + ); + } +} + +class CircularCheckbox extends StatefulWidget { + final bool value; + final ValueChanged onChanged; + + const CircularCheckbox( + {super.key, required this.value, required this.onChanged}); + + @override + _CircularCheckboxState createState() => _CircularCheckboxState(); +} + +class _CircularCheckboxState extends State { + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + widget.onChanged(!widget.value); + }, + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: widget.value + ? ColorsManager.primaryColorWithOpacity.withOpacity(0.01) + : Colors.grey, + width: 2.0, + ), + color: widget.value + ? ColorsManager.primaryColorWithOpacity + : Colors.transparent, + ), + width: 24.0, + height: 24.0, + child: widget.value + ? const Icon( + Icons.check, + color: Colors.white, + size: 16.0, + ) + : null, + ), + ); + } +} + +Widget _buildCheckboxOption({ + required String label, + required bool isSelected, + required Function(String) onTap, +}) { + return Padding( + padding: const EdgeInsets.only(bottom: 10, top: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + BodyMedium( + text: label, + style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w400), + ), + CircularCheckbox( + value: isSelected, + onChanged: (bool? value) { + if (value == true) { + onTap(label); + } + }, + ), + ], + ), + ); +} diff --git a/lib/features/devices/view/device_settings/profile_page.dart b/lib/features/devices/view/device_settings/profile_page.dart new file mode 100644 index 0000000..83b53cf --- /dev/null +++ b/lib/features/devices/view/device_settings/profile_page.dart @@ -0,0 +1,199 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/view/device_settings/location_setting.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class SettingProfilePage extends StatelessWidget { + final DeviceModel? device; + + const SettingProfilePage({super.key, this.device}); + + @override + Widget build(BuildContext context) { + var spaces = HomeCubit.getInstance().spaces; + + return DefaultScaffold( + title: 'Device Settings', + leading: IconButton( + onPressed: () { + Navigator.of(context).pop(true); + }, + icon: const Icon(Icons.arrow_back_ios)), + child: BlocProvider( + create: (context) => DeviceSettingBloc(deviceId: device?.uuid ?? '') + ..add(const DeviceSettingInitial()) + ..add(const DeviceSettingInitialInfo()), + child: BlocBuilder( + builder: (context, state) { + final _bloc = BlocProvider.of(context); + return state is DeviceSettingLoadingState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : RefreshIndicator( + onRefresh: () async { + _bloc.add(const DeviceSettingInitial()); + }, + child: ListView( + children: [ + CircleAvatar( + radius: device!.type != "SOS" ? 60 : 52, + backgroundColor: Colors.white, + child: device!.type == "SOS" + ? ClipOval( + child: SvgPicture.asset( + Assets.sosHomeIcon, + fit: BoxFit.fitHeight, + height: + MediaQuery.of(context).size.height * 0.13, + )) + : CircleAvatar( + radius: 55, + backgroundColor: ColorsManager.graysColor, + child: ClipOval( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Center( + child: SvgPicture.asset( + device!.type == "4S" + ? Assets.fourSceneIcon + : Assets.sixSceneIcon, + fit: BoxFit.contain, + height: MediaQuery.of(context) + .size + .height * + 0.08, + ), + ), + ], + ), + ), + ), + ), + const SizedBox( + height: 10, + ), + SizedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + IntrinsicWidth( + child: ConstrainedBox( + constraints: + const BoxConstraints(maxWidth: 200), + child: TextFormField( + maxLength: 30, + style: const TextStyle( + color: Colors.black, + ), + textAlign: TextAlign.center, + focusNode: _bloc.focusNode, + controller: _bloc.nameController, + enabled: _bloc.editName, + onEditingComplete: () { + _bloc.add(const SaveNameEvent()); + }, + decoration: const InputDecoration( + hintText: "Your Name", + border: InputBorder.none, + fillColor: Colors.white10, + counterText: '', + ), + ), + ), + ), + const SizedBox(width: 5), + InkWell( + onTap: () { + _bloc.add(const ChangeNameEvent(value: true)); + }, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 10), + child: SvgPicture.asset( + Assets.sosEditProfile, + color: Colors.grey, + fit: BoxFit.contain, + height: MediaQuery.of(context).size.height * + 0.02, + ), + ), + ), + ], + ), + ), + const SizedBox(height: 20), + const BodyMedium( + text: 'Smart Device Information', + fontWeight: FontWeight.w700, + fontSize: 12, + fontColor: ColorsManager.grayColor, + ), + const SizedBox(height: 7), + DefaultContainer( + padding: const EdgeInsets.all(20), + child: InkWell( + onTap: () async { + bool val = await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => LocationSettingPage( + space: spaces!.first, + deviceId: device?.uuid ?? '', + )), + ); + if (val == true) { + _bloc.add(const DeviceSettingInitialInfo()); + } + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox( + child: Text('Location'), + ), + Row( + children: [ + SizedBox( + child: BodyMedium( + text: _bloc + .deviceInfo.subspace.subspaceName, + fontColor: ColorsManager.textGray, + ), + ), + const Icon( + Icons.arrow_forward_ios, + size: 15, + color: ColorsManager.textGray, + ), + ], + ) + ], + ), + ), + ) + ], + ), + ); + }, + ), + ), + ); + } +} diff --git a/lib/features/devices/view/device_settings/question_page.dart b/lib/features/devices/view/device_settings/question_page.dart new file mode 100644 index 0000000..7e23f88 --- /dev/null +++ b/lib/features/devices/view/device_settings/question_page.dart @@ -0,0 +1,142 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_state.dart'; +import 'package:syncrow_app/features/devices/model/question_model.dart'; +import 'package:syncrow_app/features/shared_widgets/default_button.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class QuestionPageSetting extends StatelessWidget { + final QuestionModel? questionModel; + final String? deviceId; + const QuestionPageSetting({super.key, this.questionModel, this.deviceId}); + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'FAQ', + child: BlocProvider( + create: (context) => DeviceSettingBloc(deviceId: deviceId!) + ..add(const DeviceSettingInitial()), + child: BlocBuilder( + builder: (context, state) { + final sensor = BlocProvider.of(context); + return state is DeviceSettingLoadingState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : Column( + children: [ + DefaultContainer( + padding: const EdgeInsets.all(15), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + BodyLarge( + text: questionModel!.question, + fontSize: 22, + fontWeight: FontWeight.w400, + fontColor: ColorsManager.blackColor, + ), + const SizedBox( + height: 15, + ), + BodyMedium( + text: questionModel!.answer, + fontSize: 14, + fontWeight: FontWeight.w400, + fontColor: ColorsManager.secondaryTextColor, + ), + ], + ), + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.15, + ), + Center( + child: SizedBox( + width: 180, + child: DefaultButton( + backgroundColor: sensor.isHelpful == true + ? ColorsManager.grayColor + : ColorsManager.grayButtonColors, + borderRadius: 50, + onPressed: () { + sensor.add( + const ToggleHelpfulEvent(isHelpful: true)); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + Assets.thumbUp, + fit: BoxFit.fill, + ), + const SizedBox( + width: 10, + ), + const BodyMedium( + text: 'Helpful', + fontSize: 12, + fontWeight: FontWeight.w400, + fontColor: ColorsManager.blackColor, + ), + ], + )), + ), + ), + const SizedBox( + height: 15, + ), + Center( + child: SizedBox( + width: 180, + child: DefaultButton( + backgroundColor: sensor.isHelpful == false + ? ColorsManager.grayColor + : ColorsManager.grayButtonColors, + borderRadius: 50, + onPressed: () { + sensor.add( + const ToggleHelpfulEvent(isHelpful: false)); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + Assets.thumbDown, + fit: BoxFit.fill, + ), + const SizedBox( + width: 10, + ), + const BodyMedium( + text: 'Not Helpful', + fontSize: 12, + fontWeight: FontWeight.w400, + fontColor: ColorsManager.blackColor, + ), + ], + )), + ), + ), + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/features/devices/view/device_settings/settings_page.dart b/lib/features/devices/view/device_settings/settings_page.dart new file mode 100644 index 0000000..fdfed4d --- /dev/null +++ b/lib/features/devices/view/device_settings/settings_page.dart @@ -0,0 +1,451 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/view/device_settings/faq_page.dart'; +import 'package:syncrow_app/features/devices/view/device_settings/info_page.dart'; +import 'package:syncrow_app/features/devices/view/device_settings/profile_page.dart'; +import 'package:syncrow_app/features/devices/view/device_settings/share_Device_page.dart'; +import 'package:syncrow_app/features/devices/view/device_settings/update_dialog.dart'; +import 'package:syncrow_app/features/devices/view/device_settings/update_page.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/delete_device_dialogs.dart'; +import 'package:syncrow_app/features/shared_widgets/setting_widget.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class SettingsPage extends StatelessWidget { + final DeviceModel? device; + + const SettingsPage({super.key, this.device}); + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Device Settings', + child: BlocProvider( + create: (context) => DeviceSettingBloc(deviceId: device?.uuid ?? '') + ..add(const DeviceSettingInitial()) + ..add(const DeviceSettingInitialInfo()), + child: BlocBuilder( + builder: (context, state) { + final _bloc = BlocProvider.of(context); + + return state is DeviceSettingLoadingState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : ListView( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, + ), + child: InkWell( + onTap: () async { + bool val = await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => SettingProfilePage( + device: device, + ), + ), + ); + if (val == true) { + _bloc.add(const DeviceSettingInitialInfo()); + } + }, + child: Stack( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 20), + DefaultContainer( + borderRadius: const BorderRadius.all( + Radius.circular(30)), + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Padding( + padding: + const EdgeInsets.only(left: 90), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + SizedBox( + child: Text( + _bloc.deviceInfo.name, + style: const TextStyle( + fontSize: 16, + fontWeight: + FontWeight.w700, + color: ColorsManager + .grayColor, + ), + overflow: TextOverflow + .ellipsis, // Adds ellipsis (...) when the text overflows. + maxLines: + 1, // Restricts the text to a single line. + )), + const SizedBox( + height: 5, + ), + BodySmall( + text: _bloc + .deviceInfo + .subspace + .subspaceName), + ], + ), + ), + SvgPicture.asset( + Assets.editNameSetting, + fit: BoxFit.contain, + height: 30, + ), + ], + ), + ), + ), + ), + ], + ), + Positioned( + top: 0, + left: 20, + child: CircleAvatar( + radius: 43, + backgroundColor: Colors.white, + child: CircleAvatar( + radius: 40, + backgroundColor: Colors.white, + child: CircleAvatar( + radius: 40, + backgroundColor: + ColorsManager.backgroundColor, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + device!.type == "SOS" + ? ClipOval( + child: SvgPicture.asset( + Assets.sosHomeIcon, + fit: BoxFit.contain, + height: 70, + ), + ) + : Padding( + padding: device!.type == + "4S" + ? const EdgeInsets.only( + top: 5) + : const EdgeInsets.only( + top: 0), + child: SizedBox( + height: 70, + child: SvgPicture.asset( + device!.type == "4S" + ? Assets + .fourSceneIcon + : Assets + .sixSceneIcon, + fit: BoxFit.contain, + ), + ), + ), + ], + ), + ), + )), + ), + ], + ), + ), + ), + const SizedBox(height: 20), + const BodyMedium( + text: 'Device Management', + fontWeight: FontWeight.w700, + fontSize: 12, + fontColor: ColorsManager.grayColor, + ), + DefaultContainer( + child: Column( + children: [ + SettingWidget( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => SettingInfoPage( + device: device!, + )), + ); + }, + text: 'Device Information', + icon: Assets.infoIcon, + ), + // const Divider( + // color: ColorsManager.dividerColor, + // ), + // SettingWidget( + // onTap: () { + // Navigator.of(context).push( + // MaterialPageRoute( + // builder: (context) => + // ShareFourScenePage( + // device: device!)), + // ); + // }, + // text: 'Tap-to Run and Automation', + // icon: Assets.tapRunIcon, + // ), + ], + ), + ), + const SizedBox(height: 20), + const BodyMedium( + text: 'Device Offline Notification', + fontWeight: FontWeight.w700, + fontSize: 12, + fontColor: ColorsManager.grayColor, + ), + DefaultContainer( + child: Column( + children: [ + SettingWidget( + value: _bloc.enableAlarm, + onChanged: (p0) { + context + .read() + .add(ToggleEnableAlarmEvent(p0)); + }, + isNotification: true, + onTap: () {}, + text: 'Offline Notification', + icon: Assets.notificationIcon, + ), + ], + ), + ), + const SizedBox(height: 20), + const BodyMedium( + text: 'Others', + fontWeight: FontWeight.w700, + fontSize: 12, + fontColor: ColorsManager.grayColor, + ), + const SizedBox(height: 5), + DefaultContainer( + child: Column( + children: [ + SettingWidget( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + ShareDevicePage(device: device!)), + ); + }, + text: 'Share Device', + icon: Assets.shareIcon, + ), + // const Divider( + // color: ColorsManager.dividerColor, + // ), + // SettingWidget( + // onTap: () { + // Navigator.of(context).push( + // MaterialPageRoute( + // builder: (context) => + // FourSceneCreateGroup( + // device: device!)), + // ); + // }, + // text: 'Create Group', + // icon: Assets.createGroupIcon, + // ), + const Divider( + color: ColorsManager.dividerColor, + ), + SettingWidget( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + FaqSettingPage(device: device!)), + ); + }, + text: 'Device FAQ', + icon: Assets.faqIcon, + ), + const Divider( + color: ColorsManager.dividerColor, + ), + SettingWidget( + onTapUpdate: () { + showDialog( + context: context, + builder: (context) { + return UpdateInfoDialog( + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + Navigator.of(context).pop(); + }, + ); + }, + ); + }, + isUpdate: true, + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + const UpdatePageSetting()), + ); + }, + text: 'Device Update', + icon: Assets.updateIcon, + ), + ], + ), + ), + const SizedBox(height: 20), + InkWell( + onTap: () { + showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return Container( + height: 200, + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const BodyMedium( + text: 'Remove Device', + fontWeight: FontWeight.w700, + fontSize: 16, + fontColor: ColorsManager.red, + ), + const SizedBox(height: 10), + const SizedBox( + width: 250, + child: Divider( + color: ColorsManager.dividerColor, + ), + ), + const SizedBox(height: 10), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + InkWell( + onTap: () { + showDialog( + context: context, + builder: (context) { + return DisconnectDeviceDialog( + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + Navigator.of(context).pop(); + }, + ); + }, + ); + }, + child: const BodyMedium( + text: 'Disconnect Device', + fontWeight: FontWeight.w400, + fontSize: 15, + fontColor: + ColorsManager.textPrimaryColor, + ), + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ) + ], + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + InkWell( + onTap: () { + showDialog( + context: context, + builder: (context) { + return DisconnectWipeData( + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + _bloc.add( + DeleteDeviceEvent()); + }, + ); + }, + ); + }, + child: const BodyMedium( + text: + 'Disconnect Device and Wipe Data', + fontWeight: FontWeight.w400, + fontSize: 15, + fontColor: + ColorsManager.textPrimaryColor, + ), + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ) + ], + ), + ], + ), + ); + }, + ); + }, + child: const Center( + child: BodyMedium( + text: 'Remove Device', + fontWeight: FontWeight.w400, + fontSize: 15, + fontColor: ColorsManager.red, + ), + ), + ), + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/features/devices/view/device_settings/share_device_page.dart b/lib/features/devices/view/device_settings/share_device_page.dart new file mode 100644 index 0000000..0064f99 --- /dev/null +++ b/lib/features/devices/view/device_settings/share_device_page.dart @@ -0,0 +1,102 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; +import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/menu/view/widgets/manage_home/home_settings.dart'; +import 'package:syncrow_app/features/shared_widgets/default_button.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/utils/helpers/custom_page_route.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class ShareDevicePage extends StatelessWidget { + final DeviceModel? device; + const ShareDevicePage({super.key, this.device}); + @override + Widget build(BuildContext context) { + var spaces = HomeCubit.getInstance().spaces; + return DefaultScaffold( + title: 'Share Device', + child: BlocProvider( + create: (context) => FourSceneBloc(fourSceneId: device?.uuid ?? '') + ..add(const FourSceneInitial()), + child: BlocBuilder( + builder: (context, state) { + final sensor = BlocProvider.of(context); + return state is LoadingNewSate + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : RefreshIndicator( + onRefresh: () async { + sensor.add(const FourSceneInitial()); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: MediaQuery.of(context).size.height * 0.05, + ), + const BodyLarge( + text: 'Sharing Method Not Supported', + fontSize: 15, + fontWeight: FontWeight.w400, + fontColor: ColorsManager.blackColor, + ), + const BodyMedium( + text: + 'Currently, you cannot use the specified method to share Bluetooth mesh devices Zigbee devices, infrared devices, Bluetooth Beacon Devices, and certain Bluetooth LE devices with other users.', + fontSize: 14, + fontWeight: FontWeight.w400, + fontColor: ColorsManager.secondaryTextColor, + ), + const SizedBox( + height: 10, + ), + const BodyLarge( + text: 'Recommended Sharing Method', + fontSize: 15, + fontWeight: FontWeight.w400, + fontColor: ColorsManager.blackColor, + ), + const BodyMedium( + text: + 'If the recipient is a home member or a reliable user, tap Me > Home Management > Add Member and add the recipient to your home. Then, devices in the home can be shared with the recipient in bulk.', + fontSize: 14, + fontWeight: FontWeight.w400, + fontColor: ColorsManager.secondaryTextColor, + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.15, + ), + Center( + child: SizedBox( + width: 250, + child: DefaultButton( + backgroundColor: ColorsManager.blueColor1, + borderRadius: 50, + onPressed: () { + Navigator.of(context).push(CustomPageRoute( + builder: (context) => HomeSettingsView( + space: spaces!.first, + ))); + }, + child: const Text('Add Home Member')), + ), + ) + ], + )); + }, + ), + ), + ); + } +} diff --git a/lib/features/devices/view/device_settings/update_dialog.dart b/lib/features/devices/view/device_settings/update_dialog.dart new file mode 100644 index 0000000..0529161 --- /dev/null +++ b/lib/features/devices/view/device_settings/update_dialog.dart @@ -0,0 +1,118 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class UpdateInfoDialog extends StatelessWidget { + final Function()? cancelTab; + final Function()? confirmTab; + + const UpdateInfoDialog({ + super.key, + required this.cancelTab, + required this.confirmTab, + }); + + @override + Widget build(BuildContext context) { + return AlertDialog( + contentPadding: EdgeInsets.zero, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + height: 10, + ), + BodyLarge( + text: 'Update Available', + fontWeight: FontWeight.w700, + fontColor: ColorsManager.switchButton.withOpacity(0.6), + fontSize: 16, + ), + const Padding( + padding: EdgeInsets.only(left: 15, right: 15), + child: Divider( + color: ColorsManager.textGray, + ), + ), + const Padding( + padding: EdgeInsets.only(left: 15, right: 20, top: 15, bottom: 20), + child: Column( + children: [ + Center( + child: Text( + 'An update is available for your device. Version 2.1.0 includes new features and important fixes to enhance performance and security.', + textAlign: TextAlign.center, + )), + ], + ), + ), + Row( + children: [ + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + right: BorderSide( + color: ColorsManager.textGray, + width: 0.5, + ), + top: BorderSide( + color: ColorsManager.textGray, + width: 1.0, + ), + )), + child: SizedBox( + child: InkWell( + onTap: cancelTab, + child: const Padding( + padding: const EdgeInsets.only(top: 15, bottom: 15), + child: Center( + child: Text( + 'Remind me later', + style: TextStyle( + color: ColorsManager.textGray, + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + ), + ), + ), + ), + ), + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + left: BorderSide( + color: ColorsManager.textGray, + width: 0.5, + ), + top: BorderSide( + color: ColorsManager.textGray, + width: 1.0, + ), + )), + child: InkWell( + onTap: confirmTab, + child: Padding( + padding: const EdgeInsets.only(top: 15, bottom: 15), + child: Center( + child: Text( + 'Update Now', + style: TextStyle( + color: + ColorsManager.switchButton.withOpacity(0.6), + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + )), + )) + ], + ) + ], + ), + ); + } +} diff --git a/lib/features/devices/view/device_settings/update_note.dart b/lib/features/devices/view/device_settings/update_note.dart new file mode 100644 index 0000000..04f5c3e --- /dev/null +++ b/lib/features/devices/view/device_settings/update_note.dart @@ -0,0 +1,118 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class UpDateNoteSetting extends StatelessWidget { + final Function()? cancelTab; + final Function()? confirmTab; + + const UpDateNoteSetting({ + super.key, + required this.cancelTab, + required this.confirmTab, + }); + + @override + Widget build(BuildContext context) { + return AlertDialog( + contentPadding: EdgeInsets.zero, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + height: 10, + ), + BodyLarge( + text: 'Update Note', + fontWeight: FontWeight.w700, + fontColor: ColorsManager.switchButton.withOpacity(0.6), + fontSize: 16, + ), + const Padding( + padding: EdgeInsets.only(left: 15, right: 15), + child: Divider( + color: ColorsManager.textGray, + ), + ), + const Padding( + padding: EdgeInsets.only(left: 15, right: 20, top: 15, bottom: 20), + child: Column( + children: [ + Center( + child: Text( + 'This update may take a long time. Make sure that the device is fully charged. The device will be unavailable during the update.', + textAlign: TextAlign.center, + )), + ], + ), + ), + Row( + children: [ + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + right: BorderSide( + color: ColorsManager.textGray, + width: 0.5, + ), + top: BorderSide( + color: ColorsManager.textGray, + width: 1.0, + ), + )), + child: SizedBox( + child: InkWell( + onTap: cancelTab, + child: const Padding( + padding: const EdgeInsets.only(top: 15, bottom: 15), + child: Center( + child: Text( + 'Cancel', + style: TextStyle( + color: ColorsManager.textGray, + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + ), + ), + ), + ), + ), + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + left: BorderSide( + color: ColorsManager.textGray, + width: 0.5, + ), + top: BorderSide( + color: ColorsManager.textGray, + width: 1.0, + ), + )), + child: InkWell( + onTap: confirmTab, + child: Padding( + padding: const EdgeInsets.only(top: 15, bottom: 15), + child: Center( + child: Text( + 'Start Update', + style: TextStyle( + color: + ColorsManager.switchButton.withOpacity(0.6), + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + )), + )) + ], + ) + ], + ), + ); + } +} diff --git a/lib/features/devices/view/device_settings/update_page.dart b/lib/features/devices/view/device_settings/update_page.dart new file mode 100644 index 0000000..60f9313 --- /dev/null +++ b/lib/features/devices/view/device_settings/update_page.dart @@ -0,0 +1,351 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:percent_indicator/linear_percent_indicator.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/device_settings_bloc/device_scene_state.dart'; +import 'package:syncrow_app/features/devices/view/device_settings/update_note.dart'; +import 'package:syncrow_app/features/shared_widgets/default_button.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class UpdatePageSetting extends StatelessWidget { + final String? deviceId; + const UpdatePageSetting({super.key, this.deviceId}); + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Device Update', + child: BlocProvider( + create: (context) => DeviceSettingBloc(deviceId: deviceId ?? '') + ..add(const DeviceSettingInitial()) + ..add(const DeviceSettingInitialInfo()), + child: BlocBuilder( + builder: (context, state) { + final _bloc = BlocProvider.of(context); + + return state is DeviceSettingLoadingState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : RefreshIndicator( + onRefresh: () async {}, + child: Column( + children: [ + DefaultContainer( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 50, + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: SizedBox( + width: 200, + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(10), + decoration: const BoxDecoration( + color: ColorsManager + .primaryColor, + borderRadius: + BorderRadius.all( + Radius.circular(50))), + child: SvgPicture.asset( + Assets.checkUpdateIcon, + fit: BoxFit.fill, + height: 25, + ), + ), + const SizedBox( + width: 10, + ), + InkWell( + onTap: () {}, + child: const BodyMedium( + text: 'Automatic Update', + fontWeight: FontWeight.normal, + ), + ), + ], + ), + ), + trailing: Container( + width: 100, + child: Row( + mainAxisAlignment: + MainAxisAlignment.end, + children: [ + BodyMedium( + text: _bloc.enableUpdate + ? 'true' + : 'Off', + fontColor: ColorsManager.textGray, + ), + Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: _bloc.enableUpdate, + onChanged: (value) { + _bloc.add(ToggleUpdateEvent( + isUpdateEnabled: value)); + }, + applyTheme: true, + ), + ), + ], + ), + )), + ), + ], + ), + ), + const SizedBox( + height: 10, + ), + const UpdateSosContainerWithProgressBar( + sosDescription: + 'Connectivity Issue Resolved Fixed a bug that caused the SOS button to disconnect from the app intermittently.', + sosVersion: 'SOS v2.0.5', + ), + // const UpdatedContainer( + // sosVersion: 'SOS v1.0.13', + // sosDescription: 'SOS is up to date', + // ), + + // const NewUpdateContainer( + // sosVersion: 'SOS v2.0.5', + // sosDescription: + // 'Connectivity Issue Resolved Fixed a bug that caused the SOS button to disconnect from the app intermittently.', + // ), + const SizedBox( + height: 15, + ), + ], + )); + }, + ), + )); + } +} + +class UpdatedContainer extends StatelessWidget { + final String? sosVersion; + final String? sosDescription; + const UpdatedContainer({ + this.sosVersion, + this.sosDescription, + super.key, + }); + + @override + Widget build(BuildContext context) { + return DefaultContainer( + height: MediaQuery.of(context).size.height * 0.35, + child: Padding( + padding: const EdgeInsets.all(25), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + SvgPicture.asset( + Assets.emptyUpdateIcon, + fit: BoxFit.fill, + ), + BodyMedium( + text: sosVersion!, + fontColor: ColorsManager.primaryTextColor, + ), + BodyMedium( + text: sosDescription!, + fontColor: ColorsManager.blackColor, + ), + ], + ), + ], + ), + ), + ); + } +} + +class NewUpdateContainer extends StatelessWidget { + final String? sosVersion; + final String? sosDescription; + const NewUpdateContainer({ + this.sosVersion, + this.sosDescription, + super.key, + }); + + @override + Widget build(BuildContext context) { + return DefaultContainer( + height: MediaQuery.of(context).size.height * 0.50, + child: Padding( + padding: const EdgeInsets.all(25), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + SvgPicture.asset( + Assets.emptyUpdateIcon, + fit: BoxFit.fill, + ), + const BodyMedium( + text: 'New Update Available Now!', + fontColor: ColorsManager.blueColor, + ), + BodyMedium( + text: sosVersion!, + fontColor: ColorsManager.primaryTextColor, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.7, + child: BodyMedium( + text: sosDescription!, + fontColor: ColorsManager.textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.6, + child: DefaultButton( + borderRadius: 25, + backgroundColor: ColorsManager.blueColor1, + height: 150, + onPressed: () { + showDialog( + context: context, + builder: (context) { + return UpDateNoteSetting( + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + Navigator.of(context).pop(); + }, + ); + }, + ); + }, + child: const BodyMedium( + text: 'Update Now', + fontColor: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w700, + textAlign: TextAlign.center, + ), + ), + ) + ], + ), + ], + ), + ), + ); + } +} + +class UpdateSosContainerWithProgressBar extends StatelessWidget { + final String? sosVersion; + final String? sosDescription; + const UpdateSosContainerWithProgressBar({ + this.sosVersion, + this.sosDescription, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + DefaultContainer( + height: MediaQuery.of(context).size.height * 0.50, + child: Padding( + padding: const EdgeInsets.all(25), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + SvgPicture.asset( + Assets.emptyUpdateIcon, + fit: BoxFit.fill, + ), + const BodyMedium( + text: 'New Update Available Now!', + fontColor: ColorsManager.blueColor, + ), + BodyMedium( + text: sosVersion!, + fontColor: ColorsManager.primaryTextColor, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.7, + child: BodyMedium( + text: sosDescription!, + fontColor: ColorsManager.textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + LinearPercentIndicator( + barRadius: const Radius.circular(10), + width: 170.0, + animation: true, + animationDuration: 1000, + lineHeight: 5.0, + percent: 0.2, + linearStrokeCap: LinearStrokeCap.butt, + progressColor: ColorsManager.blueColor1, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.7, + child: const BodyMedium( + text: 'Downloading Update please be patient', + fontColor: ColorsManager.textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + ], + ), + ], + ), + ), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.7, + child: const BodyMedium( + text: + 'Please keep the power of the device connected during the upgrade process.', + fontColor: ColorsManager.textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + ], + ); + } +} diff --git a/lib/features/devices/view/widgets/6_scene_switch/select_scene_page.dart b/lib/features/devices/view/widgets/6_scene_switch/select_scene_page.dart new file mode 100644 index 0000000..ab6fb36 --- /dev/null +++ b/lib/features/devices/view/widgets/6_scene_switch/select_scene_page.dart @@ -0,0 +1,327 @@ +import 'dart:typed_data'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; +import 'package:syncrow_app/features/devices/bloc/6_scene_switch_bloc/6_scene_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/6_scene_switch_bloc/6_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/6_scene_switch_bloc/6_scene_state.dart'; +import 'package:syncrow_app/features/devices/view/widgets/restart_status_dialog.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class SixSelectSceneFourPage extends StatelessWidget { + final String? switchSelected; + final String? deviceId; + SixSelectSceneFourPage({super.key, this.switchSelected, this.deviceId}); + + final TextEditingController _searchController = TextEditingController(); + final int? selectedSwitchIndex = 0; + final spaces = HomeCubit.getInstance().spaces; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => SixSceneBloc(sixSceneId: deviceId!) + ..add(LoadScenes( + unit: spaces!.first, + unitId: spaces!.first.id, + showInDevice: false, + )) + ..add(GetSceneBySwitchName(switchName: switchSelected)), + child: BlocBuilder( + builder: (context, state) { + final sensorBloc = BlocProvider.of(context); + if (state is SaveSelectionSuccessState) { + Future.delayed(const Duration(milliseconds: 250), () { + Navigator.of(context).pop(true); + Navigator.of(context).pop(true); + }); + } + return DefaultScaffold( + title: 'Select Scene', + actions: [_buildSaveButton(context, state, sensorBloc)], + child: _buildSceneContent(context, sensorBloc, state), + ); + }, + ), + ); + } + + // Save button builder + Widget _buildSaveButton( + BuildContext context, SixSceneState state, SixSceneBloc sensorBloc) { + final bool canSave = state is SceneSelectionUpdatedState; + + return GestureDetector( + onTap: canSave + ? () { + context.read().add(AssignDeviceScene( + sceneUuid: sensorBloc.selectedSceneId, + switchName: switchSelected, + unit: spaces!.first)); + } + : null, + child: Padding( + padding: const EdgeInsets.only(left: 10, right: 10), + child: BodyMedium( + text: 'Save', + fontWeight: FontWeight.w700, + fontSize: 16, + fontColor: canSave + ? ColorsManager.slidingBlueColor + : ColorsManager.primaryTextColor, + ), + ), + ); + } + + // Loading indicator + Widget _buildLoadingIndicator() { + return const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator(), + ), + ); + } + + // Main scene content with search bar and grid + Widget _buildSceneContent( + BuildContext context, SixSceneBloc sensorBloc, SixSceneState state) { + return Column( + children: [ + _buildSearchBar(sensorBloc), + const SizedBox(height: 20), + Expanded( + child: state is SixSceneLoadingState + ? _buildLoadingIndicator() + : sensorBloc.filteredScenes.isEmpty + ? const Center( + child: SizedBox( + child: BodySmall(text: 'No Scenes available'), + ), + ) + : _buildSceneGrid(sensorBloc, state), + ), + ], + ); + } + + // Search bar widget + Widget _buildSearchBar(SixSceneBloc sensorBloc) { + return TextFormField( + controller: _searchController, + onChanged: (value) { + sensorBloc.add(SearchScenesEvent(query: value)); + }, + decoration: InputDecoration( + hintText: 'Search', + hintStyle: const TextStyle( + color: ColorsManager.textGray, + fontSize: 16, + fontWeight: FontWeight.w400), + suffixIcon: Container( + padding: const EdgeInsets.all(5.0), + margin: const EdgeInsets.all(10.0), + child: SvgPicture.asset( + Assets.searchIcon, + fit: BoxFit.contain, + ), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + ); + } + + // Scene grid builder + Widget _buildSceneGrid(SixSceneBloc sensorBloc, SixSceneState state) { + final scenes = sensorBloc.filteredScenes; + + return GridView.builder( + itemCount: scenes.length, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 16, + crossAxisSpacing: 16, + mainAxisExtent: 120, + ), + itemBuilder: (context, index) { + final scene = scenes[index]; + bool isSelected = scene.id == + (state is SceneSelectionUpdatedState + ? state.selectedSceneId + : sensorBloc.selectedFormApiSceneId); + return SceneItem( + id: scene.id, + value: isSelected, + disablePlayButton: false, + onChanged: (isSelected) { + sensorBloc.add(SelectOptionEvent(selectedOption: scene.id)); + sensorBloc.add(SelectSceneEvent( + selectedSceneId: scene.id, isSelected: isSelected)); + }, + icon: scene.iconInBytes, + title: scene.name, + ); + + // if (index == scenes.length) { + // return InkWell( + // onTap: () => Navigator.pushNamed( + // NavigationService.navigatorKey.currentContext!, + // Routes.sceneTasksRoute, + // arguments: SceneSettingsRouteArguments( + // sceneType: '', + // sceneId: '', + // sceneName: '', + // ), + // ), + // child: CreateSceneItem(), + // ); + // } else { + // final scene = scenes[index]; + // bool isSelected = scene.id == + // (state is SceneSelectionUpdatedState + // ? state.selectedSceneId + // : sensorBloc.selectedFormApiSceneId); + // return SceneItem( + // id: scene.id, + // value: isSelected, + // disablePlayButton: false, + // onChanged: (isSelected) { + // sensorBloc.add(SelectOptionEvent(selectedOption: scene.id)); + + // sensorBloc.add(SelectSceneEvent(unitId: scene.id)); + // }, + // icon: scene.iconInBytes, + // title: scene.name, + // ); + // } + }, + ); + } +} + +class SceneItem extends StatelessWidget { + final String id; + final Uint8List icon; + final String title; + final bool disablePlayButton; + final bool value; + final Function(bool) onChanged; + + const SceneItem({ + required this.id, + required this.icon, + required this.title, + this.disablePlayButton = false, + required this.value, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 2, + spreadRadius: 1, + offset: Offset(0, 3), + ), + ], + ), + padding: const EdgeInsets.all(16), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (!disablePlayButton) + Image.memory( + icon, + height: 32, + width: 32, + fit: BoxFit.fill, + errorBuilder: (context, error, stackTrace) => Image.asset( + Assets.assetsIconsLogo, + height: 32, + width: 32, + fit: BoxFit.fill), + ), + ], + ), + CircularCheckbox( + value: value, + onChanged: (isSelected) => onChanged(isSelected!), + ), + ], + ), + BodyMedium( + text: title, + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w700, + color: ColorsManager.secondaryTextColor), + textAlign: TextAlign.center, + ), + ], + ), + ); + } +} + +// Widget for the static "Create Scene" button +class CreateSceneItem extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 5, + spreadRadius: 2, + offset: Offset(0, 3), + ), + ], + ), + padding: const EdgeInsets.all(16), + child: const Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(Icons.add_circle, + color: ColorsManager.grayButtonColors, size: 36), + BodyMedium( + text: 'Create Scene', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: ColorsManager.secondaryTextColor), + textAlign: TextAlign.center, + ), + ], + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/6_scene_switch/select_switch_dialog.dart b/lib/features/devices/view/widgets/6_scene_switch/select_switch_dialog.dart new file mode 100644 index 0000000..f873634 --- /dev/null +++ b/lib/features/devices/view/widgets/6_scene_switch/select_switch_dialog.dart @@ -0,0 +1,177 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/devices/view/widgets/6_scene_switch/six_switches_card.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +// ignore: must_be_immutable +class SelectSwitchDialog extends StatefulWidget { + final Function()? cancelTab; + final Function(String)? confirmTab; + String? switch1Title; + String? switch2Title; + String? switch3Title; + String? switch4Title; + String? switch5Title; + String? switch6Title; + SelectSwitchDialog( + {super.key, + required this.cancelTab, + required this.confirmTab, + this.switch1Title, + this.switch2Title, + this.switch3Title, + this.switch4Title, + this.switch5Title, + this.switch6Title}); + + @override + State createState() => _SelectSwitchDialogState(); +} + +class _SelectSwitchDialogState extends State { + String selectedSwitchName = ''; + + @override + Widget build(BuildContext context) { + return AlertDialog( + backgroundColor: ColorsManager.onPrimaryColor, + contentPadding: EdgeInsets.zero, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 10), + const BodyLarge( + text: 'Select Switch', + fontWeight: FontWeight.w700, + fontColor: ColorsManager.primaryColor, + fontSize: 16, + ), + const Padding( + padding: EdgeInsets.only(left: 15, right: 15), + child: Divider(color: ColorsManager.textGray), + ), + const SizedBox(height: 20), + Row( + children: [ + SixSwitchsCardDialog( + switch1Title: widget.switch1Title, + switch2Title: widget.switch2Title, + switch3Title: widget.switch3Title, + switch4Title: widget.switch4Title, + switch5Title: widget.switch5Title, + switch6Title: widget.switch6Title, + switch1Down: selectedSwitchName == 'scene_4' + ? Assets.removeSceneIcon + : Assets.addSwitchIcon, + switch1Up: selectedSwitchName == 'scene_1' + ? Assets.removeSceneIcon + : Assets.addSwitchIcon, + switch2Down: selectedSwitchName == 'scene_5' + ? Assets.removeSceneIcon + : Assets.addSwitchIcon, + switch2Up: selectedSwitchName == 'scene_2' + ? Assets.removeSceneIcon + : Assets.addSwitchIcon, + switch3Up: selectedSwitchName == 'scene_3' + ? Assets.removeSceneIcon + : Assets.addSwitchIcon, + switch3Down: selectedSwitchName == 'scene_6' + ? Assets.removeSceneIcon + : Assets.addSwitchIcon, + onSwitch3DownTap: () { + setState(() => selectedSwitchName = 'scene_3'); + }, + onSwitch3UpTap: () { + setState(() => selectedSwitchName = 'scene_6'); + }, + onSwitch1UpTap: () { + setState(() => selectedSwitchName = 'scene_1'); + }, + onSwitch1DownTap: () { + setState(() => selectedSwitchName = 'scene_4'); + }, + onSwitch2UpTap: () { + setState(() => selectedSwitchName = 'scene_5'); + }, + onSwitch2DownTap: () { + setState(() => selectedSwitchName = 'scene_2'); + }, + ), + ], + ), + const SizedBox(height: 20), + const Padding( + padding: EdgeInsets.only(bottom: 15, left: 5, right: 5, top: 10), + child: Center( + child: Text( + 'Please select one of the switches available to continue', + textAlign: TextAlign.center, + ), + ), + ), + Row( + children: [ + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + right: + BorderSide(color: ColorsManager.textGray, width: 0.5), + top: BorderSide(color: ColorsManager.textGray, width: 1.0), + )), + child: SizedBox( + child: InkWell( + onTap: widget.cancelTab, + child: const Padding( + padding: EdgeInsets.all(15), + child: Center( + child: Text( + 'Cancel', + style: TextStyle( + color: ColorsManager.textPrimaryColor, + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + ), + ), + ), + ), + ), + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + left: BorderSide(color: ColorsManager.textGray, width: 0.5), + top: BorderSide(color: ColorsManager.textGray, width: 1.0), + )), + child: InkWell( + onTap: selectedSwitchName == '' + ? () {} + : () => widget.confirmTab!(selectedSwitchName), + child: Padding( + padding: EdgeInsets.all(15), + child: Center( + child: Text( + 'Next', + style: TextStyle( + color: selectedSwitchName == '' + ? ColorsManager.textPrimaryColor + .withOpacity(0.6) + : ColorsManager.primaryColor, + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + ), + ), + ), + ), + ], + ) + ], + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/6_scene_switch/six_scene_screen.dart b/lib/features/devices/view/widgets/6_scene_switch/six_scene_screen.dart new file mode 100644 index 0000000..4b4a369 --- /dev/null +++ b/lib/features/devices/view/widgets/6_scene_switch/six_scene_screen.dart @@ -0,0 +1,248 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/6_scene_switch_bloc/6_scene_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/6_scene_switch_bloc/6_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/6_scene_switch_bloc/6_scene_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/six_scene_model.dart'; +import 'package:syncrow_app/features/devices/view/device_settings/settings_page.dart'; +import 'package:syncrow_app/features/devices/view/widgets/6_scene_switch/select_scene_page.dart'; +import 'package:syncrow_app/features/devices/view/widgets/6_scene_switch/select_switch_dialog.dart'; +import 'package:syncrow_app/features/devices/view/widgets/6_scene_switch/six_switches_card.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; + +class SixSceneScreen extends StatelessWidget { + final DeviceModel? device; + + const SixSceneScreen({super.key, this.device}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => SixSceneBloc(sixSceneId: device?.uuid ?? '') + ..add(const SixSceneInitial()) + ..add(const SixSceneInitialInfo()) + ..add(const SexSceneSwitchInitial()), + child: BlocBuilder( + builder: (context, state) { + final _bloc = BlocProvider.of(context); + SixSceneModel model = SixSceneModel( + scene_1: '', + scene_2: '', + scene_3: '', + scene_4: '', + scene_5: '', + scene_6: '', + scene_id_group_id: '', + switch_backlight: '', + ); + if (state is LoadingNewSate) { + model = state.device; + } else if (state is UpdateState) { + model = state.device; + } + return DefaultScaffold( + title: _bloc.deviceInfo.name, + actions: [ + InkWell( + onTap: () async { + var val = await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => SettingsPage(device: device!), + ), + ); + if (val == null) { + _bloc.add(const SixSceneInitialInfo()); + _bloc.add(const SixSceneInitial()); + _bloc.add(const SexSceneSwitchInitial()); + } + }, + child: SvgPicture.asset(Assets.assetsIconsSettings), + ), + const SizedBox(width: 10), + ], + child: state is SixSceneLoadingState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator(), + ), + ) + : RefreshIndicator( + onRefresh: () async { + _bloc.add(const SixSceneInitial()); + }, + child: ListView( + children: [ + SizedBox( + height: MediaQuery.sizeOf(context).height * 0.8, + child: Column( + children: [ + SixSwitchsCardDialog( + switch1Title: model.scene_1, + switch2Title: model.scene_2, + switch3Title: model.scene_3, + switch4Title: model.scene_4, + switch5Title: model.scene_5, + switch6Title: model.scene_6, + switch1Down: _bloc.deviceStatus.switch_backlight + ? Assets.switchOn + : Assets.switchOff, + switch1Up: _bloc.deviceStatus.switch_backlight + ? Assets.switchOn + : Assets.switchOff, + switch2Down: _bloc.deviceStatus.switch_backlight + ? Assets.switchOn + : Assets.switchOff, + switch2Up: _bloc.deviceStatus.switch_backlight + ? Assets.switchOn + : Assets.switchOff, + switch3Up: _bloc.deviceStatus.switch_backlight + ? Assets.switchOn + : Assets.switchOff, + switch3Down: _bloc.deviceStatus.switch_backlight + ? Assets.switchOn + : Assets.switchOff, + onSwitch3DownTap: () { + debugPrint("onSwitch3DownTap"); + }, + onSwitch3UpTap: () { + debugPrint("onSwitch3UpTap"); + }, + onSwitch1UpTap: () { + debugPrint("Switch 1 Up tapped"); + }, + onSwitch1DownTap: () { + debugPrint("Switch 1 Down tapped"); + }, + onSwitch2UpTap: () { + debugPrint("Switch 2 Up tapped"); + }, + onSwitch2DownTap: () { + debugPrint("Switch 2 Down tapped"); + }, + ), + Flexible( + child: Row( + children: [ + Expanded( + child: DefaultContainer( + onTap: () { + _bloc.add(ChangeSwitchStatusEvent()); + }, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + ConstrainedBox( + constraints: const BoxConstraints( + maxHeight: 46, + maxWidth: 50, + ), + child: SvgPicture.asset( + Assets.backlightIcon), + ), + const SizedBox(height: 15), + const Flexible( + child: FittedBox( + child: BodySmall( + text: 'Backlight', + textAlign: TextAlign.center, + ), + ), + ), + ], + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: DefaultContainer( + onTap: () async { + var value = await showDialog( + context: context, + builder: (context) { + return SelectSwitchDialog( + switch1Title: model.scene_1, + switch2Title: model.scene_2, + switch3Title: model.scene_3, + switch4Title: model.scene_4, + switch5Title: model.scene_5, + switch6Title: model.scene_6, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: (v) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + SixSelectSceneFourPage( + deviceId: device!.uuid, + switchSelected: v, + ), + ), + ); + }, + ); + }, + ); + if (value == true) { + Future.delayed( + const Duration( + milliseconds: 200), () { + _bloc.add( + const SexSceneSwitchInitial()); + }); + } + }, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + ConstrainedBox( + constraints: const BoxConstraints( + maxHeight: 46, + maxWidth: 50, + ), + child: SvgPicture.asset( + Assets.addSceneIcon), + ), + const SizedBox(height: 15), + const Flexible( + child: FittedBox( + child: BodySmall( + text: 'Add Scene', + textAlign: TextAlign.center, + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ); + }, + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/6_scene_switch/six_switches_card.dart b/lib/features/devices/view/widgets/6_scene_switch/six_switches_card.dart new file mode 100644 index 0000000..6ed4cd2 --- /dev/null +++ b/lib/features/devices/view/widgets/6_scene_switch/six_switches_card.dart @@ -0,0 +1,193 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class SixSwitchsCardDialog extends StatelessWidget { + final String? switch1Title; + final String? switch2Title; + final String? switch3Title; + final String? switch4Title; + final String? switch5Title; + final String? switch6Title; + + final String switch1Up; + final String switch1Down; + final String switch2Up; + final String switch2Down; + + final String switch3Down; + final String switch3Up; + + final VoidCallback onSwitch1UpTap; + final VoidCallback onSwitch1DownTap; + final VoidCallback onSwitch2UpTap; + final VoidCallback onSwitch2DownTap; + + final VoidCallback onSwitch3DownTap; + final VoidCallback onSwitch3UpTap; + + SixSwitchsCardDialog( + {required this.switch1Down, + required this.switch1Up, + required this.switch2Down, + required this.switch2Up, + required this.onSwitch1UpTap, + required this.onSwitch1DownTap, + required this.onSwitch2UpTap, + required this.onSwitch2DownTap, + required this.onSwitch3DownTap, + required this.onSwitch3UpTap, + required this.switch3Down, + this.switch1Title, + this.switch2Title, + this.switch3Title, + this.switch4Title, + this.switch5Title, + this.switch6Title, + required this.switch3Up}); + + @override + Widget build(BuildContext context) { + return Expanded( + flex: 4, + child: InkWell( + overlayColor: MaterialStateProperty.all(Colors.transparent), + onTap: () {}, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 10), + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(10)), + ), + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 10), + decoration: BoxDecoration( + color: ColorsManager.onPrimaryColor, + boxShadow: [ + BoxShadow( + color: Colors.white.withOpacity(0.1), + blurRadius: 24, + offset: const Offset(-2, -2), + blurStyle: BlurStyle.outer, + ), + BoxShadow( + color: Colors.black.withOpacity(0.11), + blurRadius: 5, + offset: const Offset(2, 0), + blurStyle: BlurStyle.outer, + ), + BoxShadow( + color: Colors.white.withOpacity(0.13), + blurRadius: 10, + offset: const Offset(5, 1), + blurStyle: BlurStyle.inner, + ), + ], + ), + child: SizedBox( + height: 300, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + // Switch 1 + _buildSwitchColumn( + switchUp: switch1Up, + switchDown: switch1Down, + onUpTap: onSwitch1UpTap, + onDownTap: onSwitch1DownTap, + titleDown: switch4Title!, + titleUp: switch1Title!), + _buildDivider(), + // Switch 2 + _buildSwitchColumn( + switchUp: switch2Up, + switchDown: switch2Down, + onDownTap: onSwitch2UpTap, + onUpTap: onSwitch2DownTap, + titleDown: switch5Title!, + titleUp: switch2Title!), + _buildDivider(), + // Switch 3 + _buildSwitchColumn( + switchUp: switch3Up, + switchDown: switch3Down, + onDownTap: onSwitch3UpTap, + onUpTap: onSwitch3DownTap, + titleDown: switch6Title!, + titleUp: switch3Title!), + ], + ), + ), + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildSwitchColumn({ + String switchUp = '', + String titleUp = '', + String titleDown = '', + String switchDown = '', + VoidCallback? onUpTap, + VoidCallback? onDownTap, + }) { + return Expanded( + child: Padding( + padding: const EdgeInsets.only(top: 30, bottom: 30), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + InkWell( + onTap: onUpTap, + child: Container( + height: 20, + width: 20, + child: SvgPicture.asset(switchUp), + ), + ), + BodySmall( + text: titleUp, + ) + ], + ), + Column( + children: [ + InkWell( + onTap: onDownTap, + child: Container( + height: 20, + width: 20, + child: SvgPicture.asset(switchDown), + ), + ), + BodySmall( + text: titleDown, + ) + ], + ), + ], + ), + ), + ); + } + + Widget _buildDivider() { + return Container( + width: 3, + color: ColorsManager.dividerColor, + ); + } +} diff --git a/lib/features/devices/view/widgets/four_scene_switch/four_scene_screen.dart b/lib/features/devices/view/widgets/four_scene_switch/four_scene_screen.dart new file mode 100644 index 0000000..9e63b16 --- /dev/null +++ b/lib/features/devices/view/widgets/four_scene_switch/four_scene_screen.dart @@ -0,0 +1,234 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/four_scene_model.dart'; +import 'package:syncrow_app/features/devices/view/device_settings/settings_page.dart'; +import 'package:syncrow_app/features/devices/view/widgets/four_scene_switch/four_select_scene_page.dart'; +import 'package:syncrow_app/features/devices/view/widgets/four_scene_switch/four_select_switch_dialog.dart'; +import 'package:syncrow_app/features/devices/view/widgets/four_scene_switch/four_switches_card.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; + +class FourSceneScreen extends StatelessWidget { + final DeviceModel? device; + + const FourSceneScreen({super.key, this.device}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => FourSceneBloc(fourSceneId: device?.uuid ?? '') + ..add(const FourSceneInitial()) + ..add(const FourSceneInitialInfo()) + ..add(const FourSceneSwitchInitial()), + child: BlocBuilder( + builder: (context, state) { + final _bloc = BlocProvider.of(context); + + FourSceneModelState model = FourSceneModelState( + scene_1: '', + scene_2: '', + scene_3: '', + scene_4: '', + scene_id_group_id: '', + switch_backlight: '', + ); + + if (state is LoadingNewSate) { + model = state.device; + } else if (state is UpdateState) { + model = state.device; + } + + return DefaultScaffold( + title: _bloc.deviceInfo.name, + actions: [ + InkWell( + onTap: () async { + var val = await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => SettingsPage(device: device!), + ), + ); + if (val == null) { + _bloc.add(const FourSceneInitial()); + _bloc.add(const FourSceneInitialInfo()); + _bloc.add(const FourSceneSwitchInitial()); + } + }, + child: SvgPicture.asset(Assets.assetsIconsSettings), + ), + const SizedBox(width: 10), + ], + child: state is FourSceneLoadingState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator(), + ), + ) + : RefreshIndicator( + onRefresh: () async { + _bloc.add(const FourSceneInitial()); + }, + child: ListView( + children: [ + SizedBox( + height: MediaQuery.sizeOf(context).height * 0.8, + child: Column( + children: [ + FourSwitchsCard( + title1: model.scene_1, + title2: model.scene_2, + title3: model.scene_3, + title4: model.scene_4, + switch1: _bloc.deviceStatus.switch_backlight + ? Assets.switchOn + : Assets.switchOff, + switch2: _bloc.deviceStatus.switch_backlight + ? Assets.switchOn + : Assets.switchOff, + switch3: _bloc.deviceStatus.switch_backlight + ? Assets.switchOn + : Assets.switchOff, + switch4: _bloc.deviceStatus.switch_backlight + ? Assets.switchOn + : Assets.switchOff, + onSwitch1UpTap: () { + debugPrint("Switch 1 Up tapped"); + }, + onSwitch2UpTap: () { + debugPrint("Switch 2 Up tapped"); + }, + onSwitch3DownTap: () { + debugPrint("onSwitch3DownTap"); + }, + onSwitch4DownTap: () { + debugPrint("onSwitch4DownTap"); + }, + ), + Flexible( + child: Row( + children: [ + Expanded( + child: DefaultContainer( + onTap: () { + _bloc.add(ChangeSwitchStatusEvent()); + }, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + ConstrainedBox( + constraints: const BoxConstraints( + maxHeight: 46, + maxWidth: 50, + ), + child: SvgPicture.asset( + Assets.backlightIcon), + ), + const SizedBox(height: 15), + const Flexible( + child: FittedBox( + child: BodySmall( + text: 'Backlight', + textAlign: TextAlign.center, + ), + ), + ), + ], + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: DefaultContainer( + onTap: () async { + var value = await showDialog( + context: context, + builder: (context) { + return FourSelectSwitchDialog( + switch1Title: model.scene_1, + switch2Title: model.scene_2, + switch3Title: model.scene_3, + switch4Title: model.scene_4, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: + (switchSelected) async { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + FourSelectSceneFourPage( + switchSelected: + switchSelected, + deviceId: device!.uuid, + ), + ), + ); + }, + ); + }, + ); + + if (value == true) { + Future.delayed( + const Duration( + milliseconds: 250), () { + _bloc.add( + const FourSceneSwitchInitial()); + }); + } + }, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + ConstrainedBox( + constraints: const BoxConstraints( + maxHeight: 46, + maxWidth: 50, + ), + child: SvgPicture.asset( + Assets.addSceneIcon), + ), + const SizedBox(height: 15), + const Flexible( + child: FittedBox( + child: BodySmall( + text: 'Add Scene', + textAlign: TextAlign.center, + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ); + }, + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/four_scene_switch/four_select_scene_page.dart b/lib/features/devices/view/widgets/four_scene_switch/four_select_scene_page.dart new file mode 100644 index 0000000..9346f58 --- /dev/null +++ b/lib/features/devices/view/widgets/four_scene_switch/four_select_scene_page.dart @@ -0,0 +1,332 @@ +import 'dart:typed_data'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; +import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_event.dart'; +import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_state.dart'; +import 'package:syncrow_app/features/devices/view/widgets/restart_status_dialog.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class FourSelectSceneFourPage extends StatelessWidget { + final String? switchSelected; + final String? deviceId; + FourSelectSceneFourPage({super.key, this.switchSelected, this.deviceId}); + + final TextEditingController _searchController = TextEditingController(); + final int? selectedSwitchIndex = 0; + final spaces = HomeCubit.getInstance().spaces; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => FourSceneBloc(fourSceneId: deviceId!) + ..add(LoadScenes( + unit: spaces!.first, + unitId: spaces!.first.id, + showInDevice: false, + )) + ..add(GetSceneBySwitchName(switchName: switchSelected)), + child: BlocBuilder( + builder: (context, state) { + final sensorBloc = BlocProvider.of(context); + if (state is SaveSelectionSuccessState) { + Future.delayed(const Duration(milliseconds: 250), () { + Navigator.of(context).pop(true); + Navigator.of(context).pop(true); + }); + } + return DefaultScaffold( + title: 'Select Scene', + actions: [_buildSaveButton(context, state, sensorBloc)], + child: _buildSceneContent(context, sensorBloc, state), + ); + }, + ), + ); + } + + // Save button builder + Widget _buildSaveButton( + BuildContext context, FourSceneState state, FourSceneBloc sensorBloc) { + final bool canSave = state is SceneSelectionUpdatedState; + + return GestureDetector( + onTap: canSave + ? () { + context.read().add(AssignDeviceScene( + sceneUuid: sensorBloc.selectedSceneId, + switchName: switchSelected, + unit: spaces!.first)); + } + : null, + child: Padding( + padding: const EdgeInsets.only(left: 10, right: 15), + child: BodyMedium( + text: 'Save', + fontWeight: FontWeight.w700, + fontSize: 16, + fontColor: canSave + ? ColorsManager.slidingBlueColor + : ColorsManager.primaryTextColor, + ), + ), + ); + } + + // Loading indicator + Widget _buildLoadingIndicator() { + return const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator(), + ), + ); + } + + // Main scene content with search bar and grid + Widget _buildSceneContent( + BuildContext context, FourSceneBloc sensorBloc, FourSceneState state) { + return Column( + children: [ + _buildSearchBar(sensorBloc), + const SizedBox(height: 20), + Expanded( + child: state is FourSceneLoadingState + ? _buildLoadingIndicator() + : sensorBloc.filteredScenes.isEmpty + ? const Center( + child: SizedBox( + child: BodySmall(text: 'No Scenes available'), + ), + ) + : _buildSceneGrid(sensorBloc, state), + ), + ], + ); + } + + // Search bar widget + Widget _buildSearchBar(FourSceneBloc sensorBloc) { + return TextFormField( + controller: _searchController, + onChanged: (value) { + sensorBloc.add(SearchScenesEvent(query: value)); + }, + decoration: InputDecoration( + hintText: 'Search', + hintStyle: const TextStyle( + color: ColorsManager.textGray, + fontSize: 16, + fontWeight: FontWeight.w400), + suffixIcon: Container( + padding: const EdgeInsets.all(5.0), + margin: const EdgeInsets.all(10.0), + child: SvgPicture.asset( + Assets.searchIcon, + fit: BoxFit.contain, + ), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + ); + } + + // Scene grid builder + Widget _buildSceneGrid(FourSceneBloc sensorBloc, FourSceneState state) { + final scenes = sensorBloc.filteredScenes; + + return GridView.builder( + itemCount: scenes.length, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 16, + crossAxisSpacing: 16, + mainAxisExtent: 120, + ), + itemBuilder: (context, index) { + final scene = scenes[index]; + + // Check if this scene is currently selected + bool isSelected = scene.id == + (state is SceneSelectionUpdatedState + ? state.selectedSceneId + : sensorBloc.selectedFormApiSceneId); + + return SceneItem( + id: scene.id, + value: isSelected, // Pass isSelected to the SceneItem + disablePlayButton: false, + onChanged: (newSelection) { + // If selected, assign the scene + sensorBloc.add(SelectOptionEvent(selectedOption: scene.id)); + sensorBloc.add(SelectSceneEvent( + selectedSceneId: scene.id, isSelected: newSelection)); + }, + icon: scene.iconInBytes, + title: scene.name, + ); + }, + ); + } +} + +class SceneItem extends StatelessWidget { + final String id; + final Uint8List icon; + final String title; + final bool disablePlayButton; + final bool value; + final Function(bool) onChanged; + + const SceneItem({ + required this.id, + required this.icon, + required this.title, + this.disablePlayButton = false, + required this.value, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 2, + spreadRadius: 1, + offset: Offset(0, 3), + ), + ], + ), + padding: const EdgeInsets.all(16), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (!disablePlayButton) + Image.memory( + icon, + height: 32, + width: 32, + fit: BoxFit.fill, + errorBuilder: (context, error, stackTrace) => Image.asset( + Assets.assetsIconsLogo, + height: 32, + width: 32, + fit: BoxFit.fill), + ), + ], + ), + CircularCheckbox( + value: value, + onChanged: (isSelected) => onChanged(isSelected!), + ), + ], + ), + BodyMedium( + text: title, + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w700, + color: ColorsManager.secondaryTextColor), + textAlign: TextAlign.center, + ), + ], + ), + ); + } +} + +// Widget for the static "Create Scene" button +class CreateSceneItem extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 5, + spreadRadius: 2, + offset: Offset(0, 3), + ), + ], + ), + padding: const EdgeInsets.all(16), + child: const Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(Icons.add_circle, + color: ColorsManager.grayButtonColors, size: 36), + BodyMedium( + text: 'Create Scene', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: ColorsManager.secondaryTextColor), + textAlign: TextAlign.center, + ), + ], + ), + ); + } +} + + + // if (index == scenes.length) { + // return InkWell( + // onTap: () => Navigator.pushNamed( + // NavigationService.navigatorKey.currentContext!, + // Routes.sceneTasksRoute, + // arguments: SceneSettingsRouteArguments( + // sceneType: '', + // sceneId: '', + // sceneName: '', + // ), + // ), + // child: CreateSceneItem(), + // ); + // } else { + // final scene = scenes[index]; + // bool isSelected = scene.id == + // (state is SceneSelectionUpdatedState + // ? state.selectedSceneId + // : sensorBloc.selectedFormApiSceneId); + // return SceneItem( + // id: scene.id, + // value: isSelected, + // disablePlayButton: false, + // onChanged: (isSelected) { + // sensorBloc.add(SelectOptionEvent(selectedOption: scene.id)); + + // sensorBloc.add(SelectSceneEvent(selectedSceneId: scene.id)); + // }, + // icon: scene.iconInBytes, + // title: scene.name, + // ); + // } \ No newline at end of file diff --git a/lib/features/devices/view/widgets/four_scene_switch/four_select_switch_dialog.dart b/lib/features/devices/view/widgets/four_scene_switch/four_select_switch_dialog.dart new file mode 100644 index 0000000..c41a916 --- /dev/null +++ b/lib/features/devices/view/widgets/four_scene_switch/four_select_switch_dialog.dart @@ -0,0 +1,160 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/devices/view/widgets/four_scene_switch/four_switches_card_dialog.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class FourSelectSwitchDialog extends StatefulWidget { + final Function()? cancelTab; + final Function(String)? confirmTab; + + final String switch1Title; + final String switch2Title; + final String switch3Title; + final String switch4Title; + + const FourSelectSwitchDialog( + {super.key, + required this.cancelTab, + required this.confirmTab, + required this.switch1Title, + required this.switch2Title, + required this.switch3Title, + required this.switch4Title}); + + @override + State createState() => _FourSelectSwitchDialogState(); +} + +class _FourSelectSwitchDialogState extends State { + String selectedSwitchName = ''; // State variable to track selected switch + + @override + Widget build(BuildContext context) { + return AlertDialog( + backgroundColor: ColorsManager.onPrimaryColor, + contentPadding: EdgeInsets.zero, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 10), + const BodyLarge( + text: 'Select Switch', + fontWeight: FontWeight.w700, + fontColor: ColorsManager.primaryColor, + fontSize: 16, + ), + const Padding( + padding: EdgeInsets.only(left: 15, right: 15), + child: Divider(color: ColorsManager.textGray), + ), + const SizedBox(height: 20), + Row( + children: [ + FourSwitchsCardDialog( + switch1Title: widget.switch1Title, + switch2Title: widget.switch2Title, + switch3Title: widget.switch3Title, + switch4Title: widget.switch4Title, + switch1Down: selectedSwitchName == 'scene_3' + ? Assets.removeSceneIcon + : Assets.addSwitchIcon, + switch1Up: selectedSwitchName == 'scene_1' + ? Assets.removeSceneIcon + : Assets.addSwitchIcon, + switch2Down: selectedSwitchName == 'scene_4' + ? Assets.removeSceneIcon + : Assets.addSwitchIcon, + switch2Up: selectedSwitchName == 'scene_2' + ? Assets.removeSceneIcon + : Assets.addSwitchIcon, + onSwitch1UpTap: () { + setState(() => selectedSwitchName = 'scene_1'); + }, + onSwitch1DownTap: () { + setState(() => selectedSwitchName = 'scene_3'); + }, + onSwitch2UpTap: () { + setState(() => selectedSwitchName = 'scene_4'); + }, + onSwitch2DownTap: () { + setState(() => selectedSwitchName = 'scene_2'); + }, + ), + ], + ), + const SizedBox(height: 20), + const Padding( + padding: EdgeInsets.only(bottom: 15, left: 5, right: 5, top: 10), + child: Center( + child: Text( + 'Please select one of the switches available to continue', + textAlign: TextAlign.center, + ), + ), + ), + Row( + children: [ + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + right: + BorderSide(color: ColorsManager.textGray, width: 0.5), + top: BorderSide(color: ColorsManager.textGray, width: 1.0), + )), + child: SizedBox( + child: InkWell( + onTap: widget.cancelTab, + child: const Padding( + padding: EdgeInsets.all(15), + child: Center( + child: Text( + 'Cancel', + style: TextStyle( + color: ColorsManager.textPrimaryColor, + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + ), + ), + ), + ), + ), + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + left: BorderSide(color: ColorsManager.textGray, width: 0.5), + top: BorderSide(color: ColorsManager.textGray, width: 1.0), + )), + child: InkWell( + onTap: selectedSwitchName == '' + ? () {} + : () => widget.confirmTab!(selectedSwitchName), + child: Padding( + padding: EdgeInsets.all(15), + child: Center( + child: Text( + 'Next', + style: TextStyle( + color: selectedSwitchName == '' + ? ColorsManager.textPrimaryColor + .withOpacity(0.6) + : ColorsManager.primaryColor, + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + ), + ), + ), + ), + ], + ) + ], + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/four_scene_switch/four_switches_card.dart b/lib/features/devices/view/widgets/four_scene_switch/four_switches_card.dart new file mode 100644 index 0000000..1217067 --- /dev/null +++ b/lib/features/devices/view/widgets/four_scene_switch/four_switches_card.dart @@ -0,0 +1,165 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class FourSwitchsCard extends StatelessWidget { + final String switch1; + final String title1; + final String switch3; + final String title3; + + final String switch2; + final String title2; + + final String switch4; + final String title4; + + final VoidCallback onSwitch1UpTap; + final VoidCallback onSwitch4DownTap; + final VoidCallback onSwitch2UpTap; + final VoidCallback onSwitch3DownTap; + + FourSwitchsCard({ + required this.title1, + required this.title2, + required this.title3, + required this.title4, + required this.switch3, + required this.switch1, + required this.switch4, + required this.switch2, + required this.onSwitch1UpTap, + required this.onSwitch3DownTap, + required this.onSwitch2UpTap, + required this.onSwitch4DownTap, + }); + + @override + Widget build(BuildContext context) { + return Expanded( + flex: 4, + child: InkWell( + overlayColor: MaterialStateProperty.all(Colors.transparent), + onTap: () {}, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 10), + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(10)), + ), + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 10), + decoration: BoxDecoration( + color: ColorsManager.onPrimaryColor, + boxShadow: [ + BoxShadow( + color: Colors.white.withOpacity(0.1), + blurRadius: 24, + offset: const Offset(-2, -2), + blurStyle: BlurStyle.outer, + ), + BoxShadow( + color: Colors.black.withOpacity(0.11), + blurRadius: 5, + offset: const Offset(2, 0), + blurStyle: BlurStyle.outer, + ), + BoxShadow( + color: Colors.white.withOpacity(0.13), + blurRadius: 10, + offset: const Offset(5, 1), + blurStyle: BlurStyle.inner, + ), + ], + ), + child: SizedBox( + height: 300, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + _buildSwitchColumn( + switchUp: switch1, + switchDown: switch3, + onUpTap: onSwitch1UpTap, + onDownTap: onSwitch3DownTap, + titleDown: title3, + titleUp: title1), + _buildDivider(), + _buildSwitchColumn( + titleDown: title4, + titleUp: title2, + switchUp: switch2, + switchDown: switch4, + onUpTap: onSwitch2UpTap, + onDownTap: onSwitch4DownTap), + ], + ), + ), + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildSwitchColumn({ + String switchUp = '', + String switchDown = '', + String titleUp = '', + String titleDown = '', + VoidCallback? onUpTap, + VoidCallback? onDownTap, + }) { + return Expanded( + child: Padding( + padding: const EdgeInsets.only(top: 30, bottom: 30), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + InkWell( + onTap: onUpTap, + child: Container( + height: 20, + width: 20, + child: SvgPicture.asset(switchUp), + ), + ), + Text(titleUp) + ], + ), + Column( + children: [ + InkWell( + onTap: onDownTap, + child: Container( + height: 20, + width: 20, + child: SvgPicture.asset(switchDown), + ), + ), + Text(titleDown) + ], + ), + ], + ), + ), + ); + } + + Widget _buildDivider() { + return Container( + width: 3, + color: ColorsManager.dividerColor, + ); + } +} diff --git a/lib/features/devices/view/widgets/four_scene_switch/four_switches_card_dialog.dart b/lib/features/devices/view/widgets/four_scene_switch/four_switches_card_dialog.dart new file mode 100644 index 0000000..2d1edad --- /dev/null +++ b/lib/features/devices/view/widgets/four_scene_switch/four_switches_card_dialog.dart @@ -0,0 +1,171 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class FourSwitchsCardDialog extends StatelessWidget { + final String? switch1Title; + final String? switch2Title; + final String? switch3Title; + final String? switch4Title; + + final String switch1Up; + final String switch1Down; + final String switch2Up; + final String switch2Down; + + final VoidCallback onSwitch1UpTap; + final VoidCallback onSwitch1DownTap; + final VoidCallback onSwitch2UpTap; + final VoidCallback onSwitch2DownTap; + + FourSwitchsCardDialog({ + required this.switch1Down, + required this.switch1Up, + required this.switch2Down, + required this.switch2Up, + required this.onSwitch1UpTap, + required this.onSwitch1DownTap, + required this.onSwitch2UpTap, + required this.onSwitch2DownTap, + this.switch1Title, + this.switch2Title, + this.switch3Title, + this.switch4Title, + }); + + @override + Widget build(BuildContext context) { + return Expanded( + flex: 4, + child: InkWell( + overlayColor: MaterialStateProperty.all(Colors.transparent), + onTap: () {}, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 10), + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(10)), + ), + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 10), + decoration: BoxDecoration( + color: ColorsManager.onPrimaryColor, + boxShadow: [ + BoxShadow( + color: Colors.white.withOpacity(0.1), + blurRadius: 24, + offset: const Offset(-2, -2), + blurStyle: BlurStyle.outer, + ), + BoxShadow( + color: Colors.black.withOpacity(0.11), + blurRadius: 5, + offset: const Offset(2, 0), + blurStyle: BlurStyle.outer, + ), + BoxShadow( + color: Colors.white.withOpacity(0.13), + blurRadius: 10, + offset: const Offset(5, 1), + blurStyle: BlurStyle.inner, + ), + ], + ), + child: SizedBox( + height: 300, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + // Switch 1 + _buildSwitchColumn( + switchUp: switch1Up, + switchDown: switch1Down, + onUpTap: onSwitch1UpTap, + onDownTap: onSwitch1DownTap, + titleDown: switch3Title!, + titleUp: switch1Title!), + _buildDivider(), + // Switch 2 + _buildSwitchColumn( + switchUp: switch2Up, + switchDown: switch2Down, + onDownTap: onSwitch2UpTap, + onUpTap: onSwitch2DownTap, + titleDown: switch4Title!, + titleUp: switch2Title!), + ], + ), + ), + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildSwitchColumn({ + String switchUp = '', + String titleUp = '', + String titleDown = '', + String switchDown = '', + VoidCallback? onUpTap, + VoidCallback? onDownTap, + }) { + return Expanded( + child: Padding( + padding: const EdgeInsets.only(top: 30, bottom: 30), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + InkWell( + onTap: onUpTap, + child: Container( + height: 20, + width: 20, + child: SvgPicture.asset(switchUp), + ), + ), + BodySmall( + text: titleUp, + ) + ], + ), + Column( + children: [ + InkWell( + onTap: onDownTap, + child: Container( + height: 20, + width: 20, + child: SvgPicture.asset(switchDown), + ), + ), + BodySmall( + text: titleDown, + ) + ], + ), + ], + ), + ), + ); + } + + Widget _buildDivider() { + return Container( + width: 3, + color: ColorsManager.dividerColor, + ); + } +} diff --git a/lib/features/devices/view/widgets/room_page.dart b/lib/features/devices/view/widgets/room_page.dart index 3a30ba4..f76210f 100644 --- a/lib/features/devices/view/widgets/room_page.dart +++ b/lib/features/devices/view/widgets/room_page.dart @@ -36,7 +36,7 @@ class _RoomPageState extends State { final query = _searchController.text.toLowerCase(); setState(() { _filteredDevices = widget.room.devices! - .where((device) => device.type!.toLowerCase().contains(query)) + .where((device) => device.name!.toLowerCase().contains(query)) .toList(); }); } @@ -51,7 +51,9 @@ class _RoomPageState extends State { decoration: InputDecoration( hintText: 'Search', hintStyle: const TextStyle( - color: ColorsManager.textGray, fontSize: 16, fontWeight: FontWeight.w400), + color: ColorsManager.textGray, + fontSize: 16, + fontWeight: FontWeight.w400), prefixIcon: Container( padding: const EdgeInsets.all(5.0), margin: const EdgeInsets.all(10.0), diff --git a/lib/features/devices/view/widgets/room_page_switch.dart b/lib/features/devices/view/widgets/room_page_switch.dart index 4a0acab..af1bfd5 100644 --- a/lib/features/devices/view/widgets/room_page_switch.dart +++ b/lib/features/devices/view/widgets/room_page_switch.dart @@ -7,15 +7,18 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/6_scene_switch/six_scene_screen.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/door_sensor/door_sensor_screen.dart'; +import 'package:syncrow_app/features/devices/view/widgets/four_scene_switch/four_scene_screen.dart'; import 'package:syncrow_app/features/devices/view/widgets/garage_door/garage_door_screen.dart'; import 'package:syncrow_app/features/devices/view/widgets/gateway/gateway_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_Interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/one_touch/one_touch_screen.dart'; import 'package:syncrow_app/features/devices/view/widgets/power_clamp/power_clamp_page.dart'; +import 'package:syncrow_app/features/devices/view/widgets/sos/sos_screen.dart'; import 'package:syncrow_app/features/devices/view/widgets/three_touch/three_touch_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_Interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/two_touch/two_touch_Interface.dart'; @@ -65,7 +68,7 @@ class RoomPageSwitch extends StatelessWidget { Flexible( child: FittedBox( child: Text( - device.type ?? "", + device.name ?? "", overflow: TextOverflow.ellipsis, maxLines: 2, style: context.bodyLarge.copyWith( @@ -87,7 +90,6 @@ class RoomPageSwitch extends StatelessWidget { /// /// The [device] parameter represents the device model. void showDeviceInterface(DeviceModel device, BuildContext context) { - switch (device.productType) { case DeviceType.AC: Navigator.push( @@ -213,6 +215,27 @@ void showDeviceInterface(DeviceModel device, BuildContext context) { pageBuilder: (context, animation1, animation2) => WaterLeakScreen(device: device))); + case DeviceType.SixScene: + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + SixSceneScreen(device: device))); + + case DeviceType.FourScene: + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + FourSceneScreen(device: device))); + + case DeviceType.SOS: + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + SosScreen(device: device))); + break; default: } diff --git a/lib/features/devices/view/widgets/sos/sos_alarm_management_page.dart b/lib/features/devices/view/widgets/sos/sos_alarm_management_page.dart new file mode 100644 index 0000000..8046e61 --- /dev/null +++ b/lib/features/devices/view/widgets/sos/sos_alarm_management_page.dart @@ -0,0 +1,113 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart'; +import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class AlarmManagementPage extends StatelessWidget { + final String sosId; + const AlarmManagementPage({super.key, required this.sosId}); + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Alarm Settings', + child: BlocProvider( + create: (context) => SosBloc(sosId: ''), + child: BlocBuilder( + builder: (context, state) { + final _bloc = BlocProvider.of(context); + + return state is LoadingNewSate + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 10, + ), + const BodyMedium( + text: 'The Alarm Management', + fontWeight: FontWeight.w700, + fontSize: 12, + fontColor: ColorsManager.grayColor, + ), + const SizedBox( + height: 5, + ), + DefaultContainer( + child: Padding( + padding: const EdgeInsets.only(left: 10, right: 10), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 50, + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: const BodyMedium( + text: 'SOS Alarm', + fontWeight: FontWeight.w400, + fontSize: 15, + ), + trailing: Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: _bloc.enableAlarm, + onChanged: (value) { + context.read().add( + ToggleEnableAlarmEvent(value)); + }, + applyTheme: true, + )), + ), + ), + const Divider( + color: ColorsManager.graysColor, + ), + SizedBox( + height: 50, + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: const BodyLarge( + text: 'Low Battery Reminder', + fontWeight: FontWeight.w400, + fontSize: 15, + ), + trailing: Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: _bloc.closingReminder, + onChanged: (value) { + context.read().add( + ToggleClosingReminderEvent( + value)); + }, + applyTheme: true, + )), + ), + ), + ], + ), + ), + ), + ], + ); + }, + ), + )); + } +} diff --git a/lib/features/devices/view/widgets/sos/sos_records_screen.dart b/lib/features/devices/view/widgets/sos/sos_records_screen.dart new file mode 100644 index 0000000..c85c3a8 --- /dev/null +++ b/lib/features/devices/view/widgets/sos/sos_records_screen.dart @@ -0,0 +1,230 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:intl/intl.dart'; +import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart'; +import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart'; +import 'package:syncrow_app/features/devices/model/device_report_model.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class SosRecordsScreen extends StatefulWidget { + final String sosId; + const SosRecordsScreen({super.key, required this.sosId}); + + @override + State createState() => _SosRecordsScreenState(); +} + +class _SosRecordsScreenState extends State { + int _selectedIndex = 0; + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Records', + child: BlocProvider( + create: (context) => + SosBloc(sosId: widget.sosId)..add(const ReportLogsInitial()), + child: BlocBuilder( + builder: (context, state) { + final waterSensorBloc = BlocProvider.of(context); + final Map> groupedRecords = {}; + + if (state is LoadingNewSate) { + return const Center( + child: DefaultContainer( + width: 50, height: 50, child: CircularProgressIndicator()), + ); + } else if (state is UpdateState) { + for (var record in waterSensorBloc.recordGroups.data!) { + final DateTime eventDateTime = + DateTime.fromMillisecondsSinceEpoch(record.eventTime!); + final String formattedDate = + DateFormat('EEEE, dd/MM/yyyy').format(eventDateTime); + if (groupedRecords.containsKey(formattedDate)) { + groupedRecords[formattedDate]!.add(record); + } else { + groupedRecords[formattedDate] = [record]; + } + } + } + + return DefaultTabController( + length: 2, + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width, + decoration: const ShapeDecoration( + color: ColorsManager.onPrimaryColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(30)), + ), + ), + child: TabBar( + onTap: (value) { + setState(() { + _selectedIndex = value; + }); + }, + indicatorColor: Colors.white, + dividerHeight: 0, + indicatorSize: TabBarIndicatorSize.tab, + indicator: const ShapeDecoration( + color: ColorsManager.slidingBlueColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20)), + ), + ), + tabs: [ + Tab( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 10), + child: BodySmall( + text: 'Record', + style: context.bodySmall.copyWith( + color: _selectedIndex == 0 + ? Colors.white + : ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + Tab( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Text( + 'Automation Log', + style: context.bodySmall.copyWith( + color: _selectedIndex == 1 + ? Colors.white + : ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + ], + ), + ), + Expanded( + child: TabBarView( + physics: const NeverScrollableScrollPhysics(), + children: [ + groupedRecords.isEmpty + ? Center( + child: SvgPicture.asset( + Assets.emptyLog, + fit: BoxFit.fill, + ), + ) + : ListView.builder( + itemCount: groupedRecords.length, + itemBuilder: (context, index) { + final String date = + groupedRecords.keys.elementAt(index); + final List recordsForDate = + groupedRecords[date]!; + + return Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + date, + style: const TextStyle( + color: ColorsManager.grayColor, + fontSize: 13, + fontWeight: FontWeight.w700, + ), + ), + ), + DefaultContainer( + child: Column( + children: [ + ...recordsForDate + .asMap() + .entries + .map((entry) { + final int idx = entry.key; + final DeviceEvent record = + entry.value; + final DateTime eventDateTime = + DateTime + .fromMillisecondsSinceEpoch( + record.eventTime!); + final String formattedTime = + DateFormat('HH:mm:ss') + .format(eventDateTime); + + return Column( + children: [ + ListTile( + leading: SvgPicture.asset( + record.value == 'true' + ? Assets + .leakDetectedIcon + : Assets + .leakNormalIcon, + height: 25, + width: 25, + ), + title: const Text( + "SOS Alert Triggered", + style: const TextStyle( + fontWeight: + FontWeight.bold, + fontSize: 18, + ), + ), + subtitle: + Text(formattedTime), + ), + if (idx != + recordsForDate.length - 1) + const Divider( + color: ColorsManager + .graysColor, + ), + ], + ); + }).toList(), + ], + ), + ), + ], + ); + }, + ), + Container( + height: 10, + width: 20, + child: Center( + child: SvgPicture.asset( + Assets.emptyLog, + fit: BoxFit.fill, + ), + ), + ), + ], + ), + ), + ], + ), + ); + }, + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/sos/sos_screen.dart b/lib/features/devices/view/widgets/sos/sos_screen.dart new file mode 100644 index 0000000..c26920d --- /dev/null +++ b/lib/features/devices/view/widgets/sos/sos_screen.dart @@ -0,0 +1,230 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart'; +import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/sos_model.dart'; +import 'package:syncrow_app/features/devices/view/device_settings/settings_page.dart'; +import 'package:syncrow_app/features/devices/view/widgets/sos/sos_alarm_management_page.dart'; +import 'package:syncrow_app/features/devices/view/widgets/sos/sos_records_screen.dart'; +import 'package:syncrow_app/features/shared_widgets/battery_bar.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; + +class SosScreen extends StatelessWidget { + final DeviceModel? device; + + const SosScreen({super.key, this.device}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => SosBloc(sosId: device?.uuid ?? '') + ..add(const SosInitial()) + ..add(SosInitialDeviseInfo()), + child: BlocBuilder( + builder: (context, state) { + final sensor = BlocProvider.of(context); + SosModel model = SosModel( + batteryPercentage: 0, + sosContactState: '', + ); + if (state is LoadingNewSate) { + model = state.sosSensor; + } else if (state is UpdateState) { + model = state.sensor; + } + return DefaultScaffold( + title: sensor.deviceInfo.name, + actions: [ + InkWell( + onTap: () async { + var val = await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => SettingsPage(device: device!), + ), + ); + if (val == null) { + sensor.add(SosInitialDeviseInfo()); + sensor.add(const SosInitial()); + } + }, + child: SvgPicture.asset(Assets.assetsIconsSettings), + ), + const SizedBox(width: 10), + ], + child: state is SosLoadingState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator(), + ), + ) + : RefreshIndicator( + onRefresh: () async { + sensor.add(const SosInitial()); + }, + child: ListView( + children: [ + SizedBox( + height: MediaQuery.sizeOf(context).height * 0.8, + child: Column( + children: [ + BatteryBar( + batteryPercentage: + sensor.deviceStatus.batteryPercentage, + ), + Expanded( + flex: 4, + child: InkWell( + overlayColor: MaterialStateProperty.all( + Colors.transparent), + onTap: () { + // Add functionality for the main SOS button here + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(890), + boxShadow: [ + BoxShadow( + color: + Colors.white.withOpacity(0.1), + blurRadius: 24, + offset: const Offset(-5, -5), + blurStyle: BlurStyle.outer, + ), + BoxShadow( + color: Colors.black + .withOpacity(0.11), + blurRadius: 25, + offset: const Offset(5, 5), + blurStyle: BlurStyle.outer, + ), + BoxShadow( + color: Colors.black + .withOpacity(0.13), + blurRadius: 30, + offset: const Offset(5, 5), + blurStyle: BlurStyle.inner, + ), + ], + ), + child: SvgPicture.asset( + sensor.deviceStatus.sosContactState != + 'sos' + ? Assets.redSos + : Assets.greenSos, + fit: BoxFit.fill, + ), + ), + ], + ), + ), + ), + Flexible( + child: Row( + children: [ + Expanded( + child: DefaultContainer( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + SosRecordsScreen( + sosId: device!.uuid!), + ), + ); + }, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + ConstrainedBox( + constraints: const BoxConstraints( + maxHeight: 46, + maxWidth: 50, + ), + child: SvgPicture.asset( + Assets.doorRecordsIcon), + ), + const SizedBox(height: 15), + const Flexible( + child: FittedBox( + child: BodySmall( + text: 'Records', + textAlign: TextAlign.center, + ), + ), + ), + ], + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: DefaultContainer( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + AlarmManagementPage( + sosId: device!.uuid!, + ), + ), + ); + }, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + ConstrainedBox( + constraints: const BoxConstraints( + maxHeight: 46, + maxWidth: 50, + ), + child: SvgPicture.asset(Assets + .doorNotificationSetting), + ), + const SizedBox(height: 15), + const Flexible( + child: FittedBox( + child: BodySmall( + text: 'Alarm Settings', + textAlign: TextAlign.center, + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ); + }, + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/sos/sos_settings.dart b/lib/features/devices/view/widgets/sos/sos_settings.dart new file mode 100644 index 0000000..94987a9 --- /dev/null +++ b/lib/features/devices/view/widgets/sos/sos_settings.dart @@ -0,0 +1,377 @@ +// import 'package:flutter/material.dart'; +// import 'package:flutter_bloc/flutter_bloc.dart'; +// import 'package:flutter_svg/flutter_svg.dart'; +// import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart'; +// import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart'; +// import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart'; +// import 'package:syncrow_app/features/devices/model/device_model.dart'; +// import 'package:syncrow_app/features/devices/model/sos_model.dart'; +// import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/sos_profile_page.dart'; +// import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/faq_sos_page.dart'; +// import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/share_sos_page.dart'; +// import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/sos_info_page.dart'; +// import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/sos_update_page.dart'; +// import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/update_dialog_sos.dart'; +// import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +// import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +// import 'package:syncrow_app/features/shared_widgets/delete_device_dialogs.dart'; +// import 'package:syncrow_app/features/shared_widgets/setting_widget.dart'; +// import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +// import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +// import 'package:syncrow_app/generated/assets.dart'; +// import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +// class SosSettings extends StatelessWidget { +// final DeviceModel? device; +// const SosSettings({ +// super.key, +// this.device, +// }); + +// @override +// Widget build(BuildContext context) { +// return DefaultScaffold( +// title: 'Device Settings', +// child: BlocProvider( +// create: (context) => SosBloc(sosId: device?.uuid ?? '') +// ..add(const SosInitial()) +// ..add(SosInitialDeviseInfo()), +// child: BlocBuilder( +// builder: (context, state) { +// final _bloc = BlocProvider.of(context); +// SosModel model = +// SosModel(batteryPercentage: 0, sosContactState: 'normal'); +// if (state is LoadingNewSate) { +// model = state.sosSensor; +// } else if (state is UpdateState) { +// model = state.sensor; +// } +// return state is SosLoadingState +// ? const Center( +// child: DefaultContainer( +// width: 50, +// height: 50, +// child: CircularProgressIndicator()), +// ) +// : ListView( +// children: [ +// Padding( +// padding: const EdgeInsets.symmetric( +// vertical: 10, +// ), +// child: InkWell( +// onTap: () async { +// bool val = await Navigator.of(context).push( +// MaterialPageRoute( +// builder: (context) => SosProfilePage( +// device: device, +// ), +// ), +// ); +// if (val == true) { +// _bloc.add(SosInitialDeviseInfo()); +// _bloc.add(const SosInitial()); +// } +// }, +// child: Stack( +// children: [ +// Column( +// crossAxisAlignment: CrossAxisAlignment.stretch, +// children: [ +// const SizedBox(height: 20), +// DefaultContainer( +// borderRadius: const BorderRadius.all( +// Radius.circular(30)), +// child: Padding( +// padding: const EdgeInsets.all(10.0), +// child: Padding( +// padding: +// const EdgeInsets.only(left: 90), +// child: Row( +// mainAxisAlignment: +// MainAxisAlignment.spaceBetween, +// children: [ +// Column( +// crossAxisAlignment: +// CrossAxisAlignment.start, +// children: [ +// BodyMedium( +// text: _bloc.deviceInfo.name, +// fontWeight: FontWeight.bold, +// ), +// const SizedBox( +// height: 5, +// ), +// BodySmall( +// text: _bloc.deviceInfo +// .subspace.subspaceName), +// ], +// ), +// const Icon( +// Icons.edit_outlined, +// color: ColorsManager.grayColor, +// ), +// ], +// ), +// ), +// ), +// ), +// ], +// ), +// Positioned( +// top: 0, +// left: 20, +// child: CircleAvatar( +// radius: 40, +// backgroundColor: Colors.white, +// child: CircleAvatar( +// radius: 40, +// backgroundColor: +// Colors.white.withOpacity(0), +// child: ClipOval( +// child: SvgPicture.asset( +// Assets.sosHomeIcon, +// fit: BoxFit.contain, +// height: 80, +// ), +// )), +// ), +// ), +// ], +// ), +// ), +// ), +// const SizedBox(height: 20), +// const BodyMedium( +// text: 'Device Management', +// fontWeight: FontWeight.w700, +// fontSize: 12, +// fontColor: ColorsManager.grayColor, +// ), +// DefaultContainer( +// child: SettingWidget( +// onTap: () { +// Navigator.of(context).push( +// MaterialPageRoute( +// builder: (context) => +// SosInfoPage(device: device!)), +// ); +// }, +// text: 'Device Information', +// icon: Assets.infoIcon, +// ), +// ), +// const SizedBox(height: 20), +// const BodyMedium( +// text: 'Device Offline Notification', +// fontWeight: FontWeight.w700, +// fontSize: 12, +// fontColor: ColorsManager.grayColor, +// ), +// DefaultContainer( +// child: Column( +// children: [ +// SettingWidget( +// value: _bloc.enableAlarm, +// onChanged: (p0) { +// context +// .read() +// .add(ToggleEnableAlarmEvent(p0)); +// }, +// isNotification: true, +// onTap: () {}, +// text: 'Offline Notification', +// icon: Assets.notificationIcon, +// ), +// ], +// ), +// ), +// const SizedBox(height: 20), +// const BodyMedium( +// text: 'Others', +// fontWeight: FontWeight.w700, +// fontSize: 12, +// fontColor: ColorsManager.grayColor, +// ), +// const SizedBox(height: 5), +// DefaultContainer( +// child: Column( +// children: [ +// SettingWidget( +// onTap: () { +// Navigator.of(context).push( +// MaterialPageRoute( +// builder: (context) => +// ShareSosPage(device: device!)), +// ); +// }, +// text: 'Share Device', +// icon: Assets.shareIcon, +// ), +// const Divider( +// color: ColorsManager.dividerColor, +// ), +// SettingWidget( +// onTap: () { +// Navigator.of(context).push( +// MaterialPageRoute( +// builder: (context) => +// FaqSosPage(device: device!)), +// ); +// }, +// text: 'Device FAQ', +// icon: Assets.faqIcon, +// ), +// const Divider( +// color: ColorsManager.dividerColor, +// ), +// SettingWidget( +// onTapUpdate: () { +// showDialog( +// context: context, +// builder: (context) { +// return UpdateInfoDialog( +// cancelTab: () { +// Navigator.of(context).pop(); +// }, +// confirmTab: () { +// Navigator.of(context).pop(); +// }, +// ); +// }, +// ); +// }, +// isUpdate: true, +// onTap: () { +// Navigator.of(context).push( +// MaterialPageRoute( +// builder: (context) => +// const SosUpdatePage()), +// ); +// }, +// text: 'Device Update', +// icon: Assets.updateIcon, +// ), +// ], +// ), +// ), +// const SizedBox(height: 20), +// InkWell( +// onTap: () { +// showModalBottomSheet( +// context: context, +// builder: (BuildContext context) { +// return Container( +// height: 200, +// padding: const EdgeInsets.all(16.0), +// child: Column( +// mainAxisSize: MainAxisSize.min, +// children: [ +// const BodyMedium( +// text: 'Remove Device', +// fontWeight: FontWeight.w700, +// fontSize: 16, +// fontColor: ColorsManager.red, +// ), +// const SizedBox(height: 10), +// const SizedBox( +// width: 250, +// child: Divider( +// color: ColorsManager.dividerColor, +// ), +// ), +// const SizedBox(height: 10), +// Row( +// mainAxisAlignment: +// MainAxisAlignment.spaceBetween, +// children: [ +// InkWell( +// onTap: () { +// showDialog( +// context: context, +// builder: (context) { +// return DisconnectDeviceDialog( +// cancelTab: () { +// Navigator.of(context).pop(); +// }, +// confirmTab: () { +// Navigator.of(context).pop(); +// }, +// ); +// }, +// ); +// }, +// child: const BodyMedium( +// text: 'Disconnect Device', +// fontWeight: FontWeight.w400, +// fontSize: 15, +// fontColor: +// ColorsManager.textPrimaryColor, +// ), +// ), +// const Icon( +// Icons.keyboard_arrow_right, +// color: ColorsManager.textGray, +// ) +// ], +// ), +// const SizedBox(height: 20), +// Row( +// mainAxisAlignment: +// MainAxisAlignment.spaceBetween, +// children: [ +// InkWell( +// onTap: () { +// showDialog( +// context: context, +// builder: (context) { +// return DisconnectWipeData( +// cancelTab: () { +// Navigator.of(context).pop(); +// }, +// confirmTab: () { +// _bloc.add( +// DeleteDeviceEvent()); +// }, +// ); +// }, +// ); +// }, +// child: const BodyMedium( +// text: +// 'Disconnect Device and Wipe Data', +// fontWeight: FontWeight.w400, +// fontSize: 15, +// fontColor: +// ColorsManager.textPrimaryColor, +// ), +// ), +// const Icon( +// Icons.keyboard_arrow_right, +// color: ColorsManager.textGray, +// ) +// ], +// ), +// ], +// ), +// ); +// }, +// ); +// }, +// child: const Center( +// child: BodyMedium( +// text: 'Remove Device', +// fontWeight: FontWeight.w400, +// fontSize: 15, +// fontColor: ColorsManager.red, +// ), +// ), +// ), +// ], +// ); +// }, +// ), +// ), +// ); +// } +// } diff --git a/lib/features/devices/view/widgets/sos/sos_status_bar.dart b/lib/features/devices/view/widgets/sos/sos_status_bar.dart new file mode 100644 index 0000000..e1be227 --- /dev/null +++ b/lib/features/devices/view/widgets/sos/sos_status_bar.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/devices/model/smart_door_model.dart'; +import 'package:syncrow_app/generated/assets.dart'; + +class SosStatusBar extends StatelessWidget { + const SosStatusBar({ + required this.smartDoorModel, + super.key, + }); + + final SmartDoorModel smartDoorModel; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SvgPicture.asset(Assets.assetsIconsWifi), + Transform.rotate( + angle: 1.5708, // 90 degrees in radians (Ï€/2 or 1.5708) + child: Icon( + _getBatteryIcon(smartDoorModel.residualElectricity), + color: _getBatteryColor(smartDoorModel.residualElectricity), + size: 30, + ), + ), + ], + ); + } + + IconData _getBatteryIcon(int batteryLevel) { + // if (batteryState == BatteryState.charging) { + // return Icons.battery_charging_full; + // } else + if (batteryLevel >= 80) { + return Icons.battery_full; + } else if (batteryLevel >= 60) { + return Icons.battery_4_bar; + } else if (batteryLevel >= 40) { + return Icons.battery_3_bar; + } else if (batteryLevel >= 20) { + return Icons.battery_2_bar; + } else { + return Icons.battery_alert; + } + } + + Color _getBatteryColor(int batteryLevel) { + if (batteryLevel >= 80) { + return Colors.green; + } else if (batteryLevel >= 40) { + return Colors.yellowAccent; + } else { + return Colors.red; + } + } +} diff --git a/lib/features/shared_widgets/delete_device_dialogs.dart b/lib/features/shared_widgets/delete_device_dialogs.dart new file mode 100644 index 0000000..67c62e2 --- /dev/null +++ b/lib/features/shared_widgets/delete_device_dialogs.dart @@ -0,0 +1,227 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class DisconnectDeviceDialog extends StatelessWidget { + final Function()? cancelTab; + final Function()? confirmTab; + + const DisconnectDeviceDialog({ + super.key, + required this.cancelTab, + required this.confirmTab, + }); + + @override + Widget build(BuildContext context) { + return AlertDialog( + contentPadding: EdgeInsets.zero, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + height: 10, + ), + const BodyLarge( + text: 'Disconnect Device', + fontWeight: FontWeight.w700, + fontColor: ColorsManager.red, + fontSize: 16, + ), + const Padding( + padding: EdgeInsets.only(left: 15, right: 15), + child: Divider( + color: ColorsManager.textGray, + ), + ), + const Padding( + padding: EdgeInsets.only(left: 15, right: 20, top: 15, bottom: 20), + child: Column( + children: [ + Center( + child: Text( + 'This will disconnect your device from this Application')), + ], + ), + ), + Row( + children: [ + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + right: BorderSide( + color: ColorsManager.textGray, + width: 0.5, + ), + top: BorderSide( + color: ColorsManager.textGray, + width: 1.0, + ), + )), + child: SizedBox( + child: InkWell( + onTap: cancelTab, + child: const Padding( + padding: EdgeInsets.all(15), + child: Center( + child: Text( + 'Cancel', + style: TextStyle( + color: ColorsManager.textGray, + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + ), + ), + ), + ), + ), + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + left: BorderSide( + color: ColorsManager.textGray, + width: 0.5, + ), + top: BorderSide( + color: ColorsManager.textGray, + width: 1.0, + ), + )), + child: InkWell( + onTap: confirmTab, + child: const Padding( + padding: EdgeInsets.all(15), + child: Center( + child: Text( + 'Disconnect', + style: TextStyle( + color: ColorsManager.red, + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + )), + )) + ], + ) + ], + ), + ); + } +} + +class DisconnectWipeData extends StatelessWidget { + final Function()? cancelTab; + final Function()? confirmTab; + + const DisconnectWipeData({ + super.key, + required this.cancelTab, + required this.confirmTab, + }); + + @override + Widget build(BuildContext context) { + return AlertDialog( + contentPadding: EdgeInsets.zero, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + height: 10, + ), + const BodyLarge( + text: 'Disconnect and Wipe Data', + fontWeight: FontWeight.w700, + fontColor: ColorsManager.red, + fontSize: 16, + ), + const Padding( + padding: EdgeInsets.only(left: 15, right: 15), + child: Divider( + color: ColorsManager.textGray, + ), + ), + const Padding( + padding: EdgeInsets.only(left: 15, right: 20, top: 15, bottom: 20), + child: Column( + children: [ + Center( + child: Text( + 'This will disconnect your device from this Application and wipe all the data')), + ], + ), + ), + Row( + children: [ + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + right: BorderSide( + color: ColorsManager.textGray, + width: 0.5, + ), + top: BorderSide( + color: ColorsManager.textGray, + width: 1.0, + ), + )), + child: SizedBox( + child: InkWell( + onTap: cancelTab, + child: const Padding( + padding: EdgeInsets.all(15), + child: Center( + child: Text( + 'Cancel', + style: TextStyle( + color: ColorsManager.textGray, + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + ), + ), + ), + ), + ), + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + left: BorderSide( + color: ColorsManager.textGray, + width: 0.5, + ), + top: BorderSide( + color: ColorsManager.textGray, + width: 1.0, + ), + )), + child: InkWell( + onTap: confirmTab, + child: const Padding( + padding: EdgeInsets.all(15), + child: Center( + child: Text( + 'Disconnect', + style: TextStyle( + color: ColorsManager.red, + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + )), + )) + ], + ) + ], + ), + ); + } +} diff --git a/lib/features/shared_widgets/setting_widget.dart b/lib/features/shared_widgets/setting_widget.dart new file mode 100644 index 0000000..066ec6f --- /dev/null +++ b/lib/features/shared_widgets/setting_widget.dart @@ -0,0 +1,94 @@ + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class SettingWidget extends StatelessWidget { + final String? text; + final bool? isUpdate; + final bool? value; + final bool? isNotification; + final String? icon; + final Function()? onTap; + final Function()? onTapUpdate; + final Function(bool)? onChanged; + const SettingWidget( + {super.key, + this.text, + this.icon, + this.value, + this.onTap, + this.isUpdate, + this.onChanged, + this.isNotification = false, + this.onTapUpdate}); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Row( + children: [ + Expanded( + flex: 2, + child: Container( + padding: const EdgeInsets.all(8), + decoration: const BoxDecoration( + color: ColorsManager.primaryColor, + borderRadius: BorderRadius.all(Radius.circular(20))), + child: SvgPicture.asset( + icon!, + fit: BoxFit.none, + height: 30, + ), + ), + ), + const SizedBox( + width: 8, + ), + Expanded( + flex: isUpdate == true ? 5 : 10, + child: BodyMedium( + text: text!, + fontSize: 15, + fontWeight: FontWeight.w400, + )), + ], + ), + ), + isUpdate == true + ? InkWell( + onTap: onTapUpdate, + child: const BodyMedium( + text: '1 Update Available', + fontSize: 13, + fontWeight: FontWeight.w400, + fontColor: ColorsManager.blueColor, + ), + ) + : const SizedBox(), + isNotification == false + ? const Icon( + Icons.arrow_forward_ios, + color: ColorsManager.graysColor, + size: 20, + ) + : Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: value!, + onChanged: onChanged, + applyTheme: true, + ), + ), + ], + ), + ); + } +} diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index 1a2ecf7..7ff3fa0 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -3,7 +3,8 @@ class Assets { /// Assets for assetsFontsAftikaRegular /// assets/fonts/AftikaRegular.ttf - static const String assetsFontsAftikaRegular = "assets/fonts/AftikaRegular.ttf"; + static const String assetsFontsAftikaRegular = + "assets/fonts/AftikaRegular.ttf"; /// Assets for assetsIcons3GangSwitch /// assets/icons/3GangSwitch.svg @@ -19,82 +20,98 @@ class Assets { /// Assets for assetsIconsAutomatedClock /// assets/icons/automated_clock.svg - static const String assetsIconsAutomatedClock = "assets/icons/automated_clock.svg"; + static const String assetsIconsAutomatedClock = + "assets/icons/automated_clock.svg"; static const String acSwitchIcon = "assets/icons/ac_switch_ic.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOfflowOffpmOffstChargeddmOff /// assets/icons/battery/dmOff/perOffchargOfflowOffpmOffstChargeddmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOfflowOffpmOffstChargeddmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOfflowOffpmOffstChargeddmOff = "assets/icons/battery/dmOff/perOffchargOfflowOffpmOffstChargeddmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOfflowOffpmOffstDefaultdmOff /// assets/icons/battery/dmOff/perOffchargOfflowOffpmOffstDefaultdmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOfflowOffpmOffstDefaultdmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOfflowOffpmOffstDefaultdmOff = "assets/icons/battery/dmOff/perOffchargOfflowOffpmOffstDefaultdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOfflowOffpmOnstChargeddmOff /// assets/icons/battery/dmOff/perOffchargOfflowOffpmOnstChargeddmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOfflowOffpmOnstChargeddmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOfflowOffpmOnstChargeddmOff = "assets/icons/battery/dmOff/perOffchargOfflowOffpmOnstChargeddmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOfflowOnpmOffstDefaultdmOff /// assets/icons/battery/dmOff/perOffchargOfflowOnpmOffstDefaultdmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOfflowOnpmOffstDefaultdmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOfflowOnpmOffstDefaultdmOff = "assets/icons/battery/dmOff/perOffchargOfflowOnpmOffstDefaultdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOfflowOnpmOnstDefaultdmOff /// assets/icons/battery/dmOff/perOffchargOfflowOnpmOnstDefaultdmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOfflowOnpmOnstDefaultdmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOfflowOnpmOnstDefaultdmOff = "assets/icons/battery/dmOff/perOffchargOfflowOnpmOnstDefaultdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOnlowOffpmOffstChargeddmOff /// assets/icons/battery/dmOff/perOffchargOnlowOffpmOffstChargeddmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOnlowOffpmOffstChargeddmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOnlowOffpmOffstChargeddmOff = "assets/icons/battery/dmOff/perOffchargOnlowOffpmOffstChargeddmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOnlowOnpmOffstlowBatterydmOff /// assets/icons/battery/dmOff/perOffchargOnlowOnpmOffstlowBatterydmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOnlowOnpmOffstlowBatterydmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOnlowOnpmOffstlowBatterydmOff = "assets/icons/battery/dmOff/perOffchargOnlowOnpmOffstlowBatterydmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOnlowOnpmOnstlowpmdmOff /// assets/icons/battery/dmOff/perOffchargOnlowOnpmOnstlowpmdmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOnlowOnpmOnstlowpmdmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOnlowOnpmOnstlowpmdmOff = "assets/icons/battery/dmOff/perOffchargOnlowOnpmOnstlowpmdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOfflowOffpmOffstChargeddmOff /// assets/icons/battery/dmOff/perOnchargOfflowOffpmOffstChargeddmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOfflowOffpmOffstChargeddmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOfflowOffpmOffstChargeddmOff = "assets/icons/battery/dmOff/perOnchargOfflowOffpmOffstChargeddmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOfflowOffpmOffstDefaultdmOff /// assets/icons/battery/dmOff/perOnchargOfflowOffpmOffstDefaultdmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOfflowOffpmOffstDefaultdmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOfflowOffpmOffstDefaultdmOff = "assets/icons/battery/dmOff/perOnchargOfflowOffpmOffstDefaultdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOfflowOffpmOnstChargeddmOff /// assets/icons/battery/dmOff/perOnchargOfflowOffpmOnstChargeddmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOfflowOffpmOnstChargeddmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOfflowOffpmOnstChargeddmOff = "assets/icons/battery/dmOff/perOnchargOfflowOffpmOnstChargeddmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOfflowOnpmOffstDefaultdmOff /// assets/icons/battery/dmOff/perOnchargOfflowOnpmOffstDefaultdmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOfflowOnpmOffstDefaultdmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOfflowOnpmOffstDefaultdmOff = "assets/icons/battery/dmOff/perOnchargOfflowOnpmOffstDefaultdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOfflowOnpmOnstDefaultdmOff /// assets/icons/battery/dmOff/perOnchargOfflowOnpmOnstDefaultdmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOfflowOnpmOnstDefaultdmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOfflowOnpmOnstDefaultdmOff = "assets/icons/battery/dmOff/perOnchargOfflowOnpmOnstDefaultdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOnlowOffpmOffstChargeddmOff /// assets/icons/battery/dmOff/perOnchargOnlowOffpmOffstChargeddmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOnlowOffpmOffstChargeddmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOnlowOffpmOffstChargeddmOff = "assets/icons/battery/dmOff/perOnchargOnlowOffpmOffstChargeddmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOnlowOnpmOffstlowBatterydmOff /// assets/icons/battery/dmOff/perOnchargOnlowOnpmOffstlowBatterydmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOnlowOnpmOffstlowBatterydmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOnlowOnpmOffstlowBatterydmOff = "assets/icons/battery/dmOff/perOnchargOnlowOnpmOffstlowBatterydmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOnlowOnpmOnstlowpmdmOff @@ -104,37 +121,44 @@ class Assets { /// Assets for assetsIconsBatteryDmOnPerOffchargOfflowOffpmOffstChargeddmOn /// assets/icons/battery/dmOn/perOffchargOfflowOffpmOffstChargeddmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOfflowOffpmOffstChargeddmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOfflowOffpmOffstChargeddmOn = "assets/icons/battery/dmOn/perOffchargOfflowOffpmOffstChargeddmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOfflowOffpmOffstDefaultdmOn /// assets/icons/battery/dmOn/perOffchargOfflowOffpmOffstDefaultdmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOfflowOffpmOffstDefaultdmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOfflowOffpmOffstDefaultdmOn = "assets/icons/battery/dmOn/perOffchargOfflowOffpmOffstDefaultdmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOfflowOffpmOnstChargeddmOn /// assets/icons/battery/dmOn/perOffchargOfflowOffpmOnstChargeddmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOfflowOffpmOnstChargeddmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOfflowOffpmOnstChargeddmOn = "assets/icons/battery/dmOn/perOffchargOfflowOffpmOnstChargeddmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOfflowOnpmOffstDefaultdmOn /// assets/icons/battery/dmOn/perOffchargOfflowOnpmOffstDefaultdmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOfflowOnpmOffstDefaultdmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOfflowOnpmOffstDefaultdmOn = "assets/icons/battery/dmOn/perOffchargOfflowOnpmOffstDefaultdmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOfflowOnpmOnstDefaultdmOn /// assets/icons/battery/dmOn/perOffchargOfflowOnpmOnstDefaultdmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOfflowOnpmOnstDefaultdmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOfflowOnpmOnstDefaultdmOn = "assets/icons/battery/dmOn/perOffchargOfflowOnpmOnstDefaultdmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOnlowOffpmOffstChargeddmOn /// assets/icons/battery/dmOn/perOffchargOnlowOffpmOffstChargeddmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOnlowOffpmOffstChargeddmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOnlowOffpmOffstChargeddmOn = "assets/icons/battery/dmOn/perOffchargOnlowOffpmOffstChargeddmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOnlowOnpmOffstlowBatterydmOn /// assets/icons/battery/dmOn/perOffchargOnlowOnpmOffstlowBatterydmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOnlowOnpmOffstlowBatterydmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOnlowOnpmOffstlowBatterydmOn = "assets/icons/battery/dmOn/perOffchargOnlowOnpmOffstlowBatterydmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOnlowOnpmOnstlowpmdmOn @@ -144,37 +168,44 @@ class Assets { /// Assets for assetsIconsBatteryDmOnPerOnchargOfflowOffpmOffstChargeddmOn /// assets/icons/battery/dmOn/perOnchargOfflowOffpmOffstChargeddmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOfflowOffpmOffstChargeddmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOfflowOffpmOffstChargeddmOn = "assets/icons/battery/dmOn/perOnchargOfflowOffpmOffstChargeddmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOfflowOffpmOffstDefaultdmOn /// assets/icons/battery/dmOn/perOnchargOfflowOffpmOffstDefaultdmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOfflowOffpmOffstDefaultdmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOfflowOffpmOffstDefaultdmOn = "assets/icons/battery/dmOn/perOnchargOfflowOffpmOffstDefaultdmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOfflowOffpmOnstChargeddmOn /// assets/icons/battery/dmOn/perOnchargOfflowOffpmOnstChargeddmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOfflowOffpmOnstChargeddmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOfflowOffpmOnstChargeddmOn = "assets/icons/battery/dmOn/perOnchargOfflowOffpmOnstChargeddmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOfflowOnpmOffstDefaultdmOn /// assets/icons/battery/dmOn/perOnchargOfflowOnpmOffstDefaultdmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOfflowOnpmOffstDefaultdmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOfflowOnpmOffstDefaultdmOn = "assets/icons/battery/dmOn/perOnchargOfflowOnpmOffstDefaultdmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOfflowOnpmOnstDefaultdmOn /// assets/icons/battery/dmOn/perOnchargOfflowOnpmOnstDefaultdmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOfflowOnpmOnstDefaultdmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOfflowOnpmOnstDefaultdmOn = "assets/icons/battery/dmOn/perOnchargOfflowOnpmOnstDefaultdmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOnlowOffpmOffstChargeddmOn /// assets/icons/battery/dmOn/perOnchargOnlowOffpmOffstChargeddmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOnlowOffpmOffstChargeddmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOnlowOffpmOffstChargeddmOn = "assets/icons/battery/dmOn/perOnchargOnlowOffpmOffstChargeddmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOnlowOnpmOffstlowBatterydmOn /// assets/icons/battery/dmOn/perOnchargOnlowOnpmOffstlowBatterydmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOnlowOnpmOffstlowBatterydmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOnlowOnpmOffstlowBatterydmOn = "assets/icons/battery/dmOn/perOnchargOnlowOnpmOffstlowBatterydmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOnlowOnpmOnstlowpmdmOn @@ -218,7 +249,8 @@ class Assets { static const String assetsIconsCurtainsIconVerticalBlade = "assets/icons/curtainsIcon/left_vertical_blade.svg"; - static const String rightVerticalBlade = "assets/icons/curtainsIcon/right_vertical_blade.svg"; + static const String rightVerticalBlade = + "assets/icons/curtainsIcon/right_vertical_blade.svg"; /// Assets for assetsIconsDashboard /// assets/icons/dashboard.svg @@ -228,7 +260,8 @@ class Assets { /// Assets for assetsIconsDashboardFill /// assets/icons/dashboard-fill.svg - static const String assetsIconsDashboardFill = "assets/icons/dashboard-fill.svg"; + static const String assetsIconsDashboardFill = + "assets/icons/dashboard-fill.svg"; /// Assets for assetsIconsDevices /// assets/icons/Devices.svg @@ -244,7 +277,8 @@ class Assets { /// Assets for assetsIconsDoorLockLinkage /// assets/icons/DoorLockLinkage.svg - static const String assetsIconsDoorLockLinkage = "assets/icons/DoorLockLinkage.svg"; + static const String assetsIconsDoorLockLinkage = + "assets/icons/DoorLockLinkage.svg"; /// Assets for assetsIconsDoorLockLock /// assets/icons/DoorLockLock.svg @@ -252,15 +286,18 @@ class Assets { /// Assets for assetsIconsDoorLockMembers /// assets/icons/DoorLockMembers.svg - static const String assetsIconsDoorLockMembers = "assets/icons/DoorLockMembers.svg"; + static const String assetsIconsDoorLockMembers = + "assets/icons/DoorLockMembers.svg"; /// Assets for assetsIconsDoorLockPassword /// assets/icons/DoorLockPassword.svg - static const String assetsIconsDoorLockPassword = "assets/icons/DoorLockPassword.svg"; + static const String assetsIconsDoorLockPassword = + "assets/icons/DoorLockPassword.svg"; /// Assets for assetsIconsDoorLockRecords /// assets/icons/DoorLockRecords.svg - static const String assetsIconsDoorLockRecords = "assets/icons/DoorLockRecords.svg"; + static const String assetsIconsDoorLockRecords = + "assets/icons/DoorLockRecords.svg"; /// Assets for assetsIconsDoorlockAssetsBatteryIndicator /// assets/icons/doorlock-assets/BatteryIndicator.svg @@ -281,7 +318,8 @@ class Assets { /// assets/icons/doorlock-assets/lockIcon.svg static const String assetsIconsDoorlockAssetsLockIcon = "assets/icons/doorlock-assets/lockIcon.svg"; - static const String doorUnlockIcon = "assets/icons/doorlock-assets/door_un_look_ic.svg"; + static const String doorUnlockIcon = + "assets/icons/doorlock-assets/door_un_look_ic.svg"; /// Assets for assetsIconsDoorlockAssetsMembersManagement /// assets/icons/doorlock-assets/members-management.svg @@ -369,11 +407,13 @@ class Assets { /// Assets for assetsIconsLightSwitchOff /// assets/icons/lightSwitchOff.svg - static const String assetsIconsLightSwitchOff = "assets/icons/lightSwitchOff.svg"; + static const String assetsIconsLightSwitchOff = + "assets/icons/lightSwitchOff.svg"; /// Assets for assetsIconsLightSwitchOn /// assets/icons/lightSwitchOn.svg - static const String assetsIconsLightSwitchOn = "assets/icons/lightSwitchOn.svg"; + static const String assetsIconsLightSwitchOn = + "assets/icons/lightSwitchOn.svg"; /// Assets for assetsIconsLinkageIconsDoorLockAlarm /// assets/icons/linkageIcons/doorLockAlarm.svg @@ -382,7 +422,8 @@ class Assets { /// Assets for assetsIconsLinkTimeLimitedPasswordIcon /// assets/icons/timeLimitedPasswordIcon.svg - static const String timeLimitedPasswordIcon = "assets/icons/timeLimitedPasswordIcon.svg"; + static const String timeLimitedPasswordIcon = + "assets/icons/timeLimitedPasswordIcon.svg"; /// Assets for assetsIconsoneTimePassword /// assets/icons/oneTimePassword.svg @@ -390,7 +431,8 @@ class Assets { /// Assets for assetsIconsTimeLimitedPassword /// assets/icons/timeLimitedPassword.svg - static const String timeLimitedPassword = "assets/icons/timeLimitedPassword.svg"; + static const String timeLimitedPassword = + "assets/icons/timeLimitedPassword.svg"; /// Assets for assetsIconsNoValidPasswords /// assets/icons/noValidPasswords.svg @@ -559,11 +601,13 @@ class Assets { /// Assets for assetsIconsPresenceSensorAssetsParameterSettings /// assets/icons/presence-sensor-assets/space_type_icon.svg - static const String spaceTypeIcon = "assets/icons/presence-sensor-assets/space_type_icon.svg"; + static const String spaceTypeIcon = + "assets/icons/presence-sensor-assets/space_type_icon.svg"; /// Assets for assetsIconsPresenceSensorAssetsParameterSettings /// assets/icons/presence-sensor-assets/space_type_icon.svg - static const String sensitivityIcon = "assets/icons/presence-sensor-assets/Sensitivity.svg"; + static const String sensitivityIcon = + "assets/icons/presence-sensor-assets/Sensitivity.svg"; /// Assets for assetsIconsPresenceSensorAssetsParameterSettings /// assets/icons/presence-sensor-assets/maximum_distance.svg @@ -596,7 +640,8 @@ class Assets { /// Assets for assetsIconsRoutinesFill /// assets/icons/Routines-fill.svg - static const String assetsIconsRoutinesFill = "assets/icons/Routines-fill.svg"; + static const String assetsIconsRoutinesFill = + "assets/icons/Routines-fill.svg"; /// Assets for assetsIconsScan /// assets/icons/Scan.svg @@ -628,7 +673,8 @@ class Assets { /// Assets for assetsIconsSustainability /// assets/icons/sustainability.svg - static const String assetsIconsSustainability = "assets/icons/sustainability.svg"; + static const String assetsIconsSustainability = + "assets/icons/sustainability.svg"; /// Assets for assetsIconsUnlockingMethodsIconsFace /// assets/icons/unlockingMethodsIcons/face.svg @@ -724,7 +770,8 @@ class Assets { /// Assets for assetsImagesHorizintalBlade /// assets/images/HorizintalBlade.png - static const String assetsImagesHorizintalBlade = "assets/images/HorizintalBlade.png"; + static const String assetsImagesHorizintalBlade = + "assets/images/HorizintalBlade.png"; /// Assets for assetsImagesLogo /// assets/images/Logo.svg @@ -732,7 +779,8 @@ class Assets { /// Assets for assetsImagesLogoHorizontal /// assets/images/logo_horizontal.png - static const String assetsImagesLogoHorizontal = "assets/images/logo_horizontal.png"; + static const String assetsImagesLogoHorizontal = + "assets/images/logo_horizontal.png"; /// Assets for assetsImagesPause /// assets/images/Pause.png @@ -762,7 +810,8 @@ class Assets { /// assets/images/Window.png static const String assetsImagesWindow = "assets/images/window_img.svg"; - static const String assetsSensitivityFunction = "assets/icons/functions_icons/sensitivity.svg"; + static const String assetsSensitivityFunction = + "assets/icons/functions_icons/sensitivity.svg"; //assets/icons/functions_icons/sesitivity_operation_icon.svg static const String assetsSensitivityOperationIcon = @@ -770,59 +819,73 @@ class Assets { //assets/icons/functions_icons/ac_power.svg - static const String assetsAcPower = "assets/icons/functions_icons/ac_power.svg"; + static const String assetsAcPower = + "assets/icons/functions_icons/ac_power.svg"; //assets/icons/functions_icons/ac_power_off.svg - static const String assetsAcPowerOFF = "assets/icons/functions_icons/ac_power_off.svg"; + static const String assetsAcPowerOFF = + "assets/icons/functions_icons/ac_power_off.svg"; //assets/icons/functions_icons/child_lock.svg - static const String assetsChildLock = "assets/icons/functions_icons/child_lock.svg"; + static const String assetsChildLock = + "assets/icons/functions_icons/child_lock.svg"; //assets/icons/functions_icons/cooling.svg - static const String assetsFreezing = "assets/icons/functions_icons/freezing.svg"; + static const String assetsFreezing = + "assets/icons/functions_icons/freezing.svg"; //assets/icons/functions_icons/fan_speed.svg - static const String assetsFanSpeed = "assets/icons/functions_icons/fan_speed.svg"; + static const String assetsFanSpeed = + "assets/icons/functions_icons/fan_speed.svg"; //assets/icons/functions_icons/ac_cooling.svg - static const String assetsAcCooling = "assets/icons/functions_icons/ac_cooling.svg"; + static const String assetsAcCooling = + "assets/icons/functions_icons/ac_cooling.svg"; //assets/icons/functions_icons/ac_heating.svg - static const String assetsAcHeating = "assets/icons/functions_icons/ac_heating.svg"; + static const String assetsAcHeating = + "assets/icons/functions_icons/ac_heating.svg"; //assets/icons/functions_icons/celsius_degrees.svg - static const String assetsCelsiusDegrees = "assets/icons/functions_icons/celsius_degrees.svg"; + static const String assetsCelsiusDegrees = + "assets/icons/functions_icons/celsius_degrees.svg"; //assets/icons/functions_icons/tempreture.svg - static const String assetsTempreture = "assets/icons/functions_icons/tempreture.svg"; + static const String assetsTempreture = + "assets/icons/functions_icons/tempreture.svg"; //assets/icons/functions_icons/ac_fan_low.svg - static const String assetsAcFanLow = "assets/icons/functions_icons/ac_fan_low.svg"; + static const String assetsAcFanLow = + "assets/icons/functions_icons/ac_fan_low.svg"; //assets/icons/functions_icons/ac_fan_middle.svg - static const String assetsAcFanMiddle = "assets/icons/functions_icons/ac_fan_middle.svg"; + static const String assetsAcFanMiddle = + "assets/icons/functions_icons/ac_fan_middle.svg"; //assets/icons/functions_icons/ac_fan_high.svg - static const String assetsAcFanHigh = "assets/icons/functions_icons/ac_fan_high.svg"; + static const String assetsAcFanHigh = + "assets/icons/functions_icons/ac_fan_high.svg"; //assets/icons/functions_icons/ac_fan_auto.svg - static const String assetsAcFanAuto = "assets/icons/functions_icons/ac_fan_auto.svg"; + static const String assetsAcFanAuto = + "assets/icons/functions_icons/ac_fan_auto.svg"; //assets/icons/functions_icons/scene_child_lock.svg - static const String assetsSceneChildLock = "assets/icons/functions_icons/scene_child_lock.svg"; + static const String assetsSceneChildLock = + "assets/icons/functions_icons/scene_child_lock.svg"; //assets/icons/functions_icons/scene_child_unlock.svg @@ -831,15 +894,18 @@ class Assets { //assets/icons/functions_icons/scene_refresh.svg - static const String assetsSceneRefresh = "assets/icons/functions_icons/scene_refresh.svg"; + static const String assetsSceneRefresh = + "assets/icons/functions_icons/scene_refresh.svg"; //assets/icons/functions_icons/light_countdown.svg - static const String assetsLightCountdown = "assets/icons/functions_icons/light_countdown.svg"; + static const String assetsLightCountdown = + "assets/icons/functions_icons/light_countdown.svg"; //assets/icons/functions_icons/far_detection.svg - static const String assetsFarDetection = "assets/icons/functions_icons/far_detection.svg"; + static const String assetsFarDetection = + "assets/icons/functions_icons/far_detection.svg"; //assets/icons/functions_icons/far_detection_function.svg @@ -848,11 +914,13 @@ class Assets { //assets/icons/functions_icons/indicator.svg - static const String assetsIndicator = "assets/icons/functions_icons/indicator.svg"; + static const String assetsIndicator = + "assets/icons/functions_icons/indicator.svg"; //assets/icons/functions_icons/motion_detection.svg - static const String assetsMotionDetection = "assets/icons/functions_icons/motion_detection.svg"; + static const String assetsMotionDetection = + "assets/icons/functions_icons/motion_detection.svg"; //assets/icons/functions_icons/motionless_detection.svg @@ -861,15 +929,18 @@ class Assets { //assets/icons/functions_icons/nobody_time.svg - static const String assetsNobodyTime = "assets/icons/functions_icons/nobody_time.svg"; + static const String assetsNobodyTime = + "assets/icons/functions_icons/nobody_time.svg"; //assets/icons/functions_icons/factory_reset.svg - static const String assetsFactoryReset = "assets/icons/functions_icons/factory_reset.svg"; + static const String assetsFactoryReset = + "assets/icons/functions_icons/factory_reset.svg"; //assets/icons/functions_icons/master_state.svg - static const String assetsMasterState = "assets/icons/functions_icons/master_state.svg"; + static const String assetsMasterState = + "assets/icons/functions_icons/master_state.svg"; //assets/icons/functions_icons/switch_alarm_sound.svg @@ -878,7 +949,8 @@ class Assets { //assets/icons/functions_icons/reset_off.svg - static const String assetsResetOff = "assets/icons/functions_icons/reset_off.svg"; + static const String assetsResetOff = + "assets/icons/functions_icons/reset_off.svg"; //assets/icons/functions_icons/automation_functions/card_unlock.svg @@ -952,7 +1024,8 @@ class Assets { //assets/icons/functions_icons/automation_functions/motion.svg - static const String assetsMotion = "assets/icons/functions_icons/automation_functions/motion.svg"; + static const String assetsMotion = + "assets/icons/functions_icons/automation_functions/motion.svg"; //assets/icons/functions_icons/automation_functions/current_temp.svg @@ -974,27 +1047,33 @@ class Assets { static const String waterHeaterOn = "assets/icons/water_heater_on.svg"; static const String waterHeaterOff = "assets/icons/water_heater_off.svg"; - static const String scheduleCelenderIcon = "assets/icons/schedule_celender_icon.svg"; - static const String scheduleCirculateIcon = "assets/icons/schedule_circulate_icon.svg"; - static const String scheduleInchingIcon = "assets/icons/schedule_Inching_icon.svg"; + static const String scheduleCelenderIcon = + "assets/icons/schedule_celender_icon.svg"; + static const String scheduleCirculateIcon = + "assets/icons/schedule_circulate_icon.svg"; + static const String scheduleInchingIcon = + "assets/icons/schedule_Inching_icon.svg"; static const String scheduleTimeIcon = "assets/icons/schedule_time_icon.svg"; static const String waterHeaterIcon = "assets/icons/water_heater_icon.svg"; static const String doorOpen = "assets/icons/opened_door.svg"; static const String doorClose = "assets/icons/closed_door.svg"; - static const String doorNotificationSetting = "assets/icons/door_notification_setting_icon.svg"; + static const String doorNotificationSetting = + "assets/icons/door_notification_setting_icon.svg"; static const String doorRecordsIcon = "assets/icons/door_records_icon.svg"; static const String doorSensorIcon = "assets/icons/door_sensor_icon.svg"; static const String closedGarageIcon = "assets/icons/closed_garage_door.svg"; static const String openGarageIcon = "assets/icons/open_garage_door.svg"; static const String garageCountdown = "assets/icons/garage_countdown.svg"; - static const String garagePreferencesIcon = "assets/icons/garage_preferences_icon.svg"; + static const String garagePreferencesIcon = + "assets/icons/garage_preferences_icon.svg"; static const String garageSchedule = "assets/icons/garage_schedule.svg"; static const String garageIcon = "assets/icons/garageIcon.svg"; static const String normalWaterLeak = "assets/icons/normal_water_leak.svg"; - static const String detectedWaterLeak = "assets/icons/detected_water_leak.svg"; + static const String detectedWaterLeak = + "assets/icons/detected_water_leak.svg"; static const String waterLeakIcon = "assets/icons/waterleak_icon.svg"; static const String leakDetectedIcon = "assets/icons/leak_detected.svg"; @@ -1012,5 +1091,38 @@ class Assets { static const String powerClampIcon = "assets/icons/power_clamp.svg"; static const String automationIcon = "assets/icons/automation_ic.svg"; - //powerClampIcon + static const String redSos = "assets/icons/red_sos.svg"; + static const String greenSos = "assets/icons/green_sos.svg"; + static const String emptyLog = "assets/icons/empty_log.svg"; + static const String sosProfileIcon = "assets/icons/sos_profile_icon.svg"; + static const String sosEditProfile = "assets/icons/edit_sos_icon.svg"; + static const String thumbUp = "assets/icons/thumb_up.svg"; + static const String thumbDown = "assets/icons/thumb_down.svg"; + static const String shareIcon = "assets/icons/share_icon.svg"; + static const String infoIcon = "assets/icons/info.svg"; + static const String notificationIcon = "assets/icons/notification_icon.svg"; + static const String faqIcon = "assets/icons/faq_icon.svg"; + static const String updateIcon = "assets/icons/update_icon.svg"; + static const String emptyUpdateIcon = "assets/icons/empty_update_icon.svg"; + static const String checkUpdateIcon = "assets/icons/check_update_icon.svg"; + + static const String switchOn = "assets/icons/switch_on.svg"; + static const String switchOff = "assets/icons/switch_off.svg"; + static const String backlightIcon = "assets/icons/backlight_icon.svg"; + static const String addSwitchIcon = "assets/icons/add_switch_icon.svg"; + static const String addSceneIcon = "assets/icons/add_scene_icon.svg"; + static const String removeSceneIcon = "assets/icons/remove_scene_icon.svg"; + static const String tapRunIcon = "assets/icons/tap_run_icon.svg"; + static const String createGroupIcon = "assets/icons/create_group_icon.svg"; + static const String sixSceneIcon = "assets/icons/six_scene_icon.svg"; + static const String minusIcon = "assets/icons/minus_icon.svg"; + static const String addDevicesIcon = "assets/icons/add_devices_icon.svg"; + static const String fourSceneIcon = "assets/icons/four_scene_icon.svg"; + static const String fourSceneHomeIcon = + "assets/icons/four_scene_home_icon.svg"; + static const String sixSceneHomeIcon = "assets/icons/six_scene_home_icon.svg"; + static const String editDeviceNameIcon = + "assets/icons/edit_device_name_icon.svg"; + static const String sosHomeIcon = "assets/icons/sos_home_icon.svg"; + static const String editNameSetting = "assets/icons/edit_name_setting.svg"; } diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index 5d9528a..c4fff35 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -213,4 +213,12 @@ abstract class ApiEndpoints { '/device/report-logs/{deviceUuid}?code={code}&startTime={startTime}&endTime={endTime}'; static const String controlBatch = '/device/control/batch'; static const String statusBatch = '/device/status/batch'; + static const String deviceScene = '/device/{deviceUuid}/scenes'; + + static const String fourSceneByName = + '/device/{deviceUuid}/scenes?switchName={switchName}'; + + static const String resetDevice = '/factory/reset/{deviceUuid}'; + static const String unAssignScenesDevice = + '/device/{deviceUuid}/scenes?switchName={switchName}'; } diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index fe6cc56..3990a65 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -31,6 +31,22 @@ class DevicesAPI { } } + static Future> putDeviceName( + {required String deviceId, required String deviceName}) async { + try { + final response = await _httpService.put( + path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId), + body: {"deviceName": deviceName}, + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } catch (e) { + rethrow; + } + } + static Future> controlDevice( DeviceControlModel controlModel, String deviceId) async { try { @@ -65,7 +81,7 @@ class DevicesAPI { showServerMessage: false, expectedResponseModel: (json) => DevicesCategoryModel.fromJsonList(json), ); - + return response; } @@ -121,6 +137,62 @@ class DevicesAPI { return response; } + static Future getDeviceSceneInfo(String deviceId) async { + final response = await _httpService.get( + path: ApiEndpoints.deviceScene.replaceAll('{deviceUuid}', deviceId), + showServerMessage: false, + expectedResponseModel: (json) { + return json; + }); + return response; + } + + static Future getSceneBySwitchName( + {String? deviceId, String? switchName}) async { + final response = await _httpService.get( + path: ApiEndpoints.fourSceneByName + .replaceAll('{deviceUuid}', deviceId!) + .replaceAll('{switchName}', switchName!), + showServerMessage: false, + expectedResponseModel: (json) { + return json; + }); + return response; + } + + static Future postDeviceSceneInfo({ + String? switchName, + String? sceneUuid, + String? deviceId, + String? spaceUuid, + }) async { + final response = await _httpService.post( + path: ApiEndpoints.deviceScene.replaceAll('{deviceUuid}', deviceId!), + body: jsonEncode( + { + "switchName": switchName, + "sceneUuid": sceneUuid, + "spaceUuid": spaceUuid + }, + ), + showServerMessage: false, + expectedResponseModel: (json) { + return json; + }); + return response; + } + + static Future getDeviceInfo(String deviceId) async { + final response = await _httpService.get( + path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId), + showServerMessage: false, + expectedResponseModel: (json) { + print('object-*-*-*${json}'); + return json; + }); + return response; + } + static Future> getDeviceByGroupName( String unitId, String groupName) async { final response = await _httpService.get( @@ -439,4 +511,36 @@ class DevicesAPI { ); return response; } + + static Future resetDevise({ + String? devicesUuid, + }) async { + final response = await _httpService.post( + path: ApiEndpoints.resetDevice.replaceAll('{deviceUuid}', devicesUuid!), + showServerMessage: false, + body: { + "devicesUuid": [devicesUuid] + }, + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } + + static Future unAssignScenesDevice({ + String? deviceUuid, + String? switchName, + }) async { + final response = await _httpService.delete( + path: ApiEndpoints.unAssignScenesDevice + .replaceAll('{deviceUuid}', deviceUuid!) + .replaceAll('{switchName}', switchName!), + showServerMessage: false, + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } } diff --git a/lib/services/api/scene_api.dart b/lib/services/api/scene_api.dart index e2040e9..fe4c1ae 100644 --- a/lib/services/api/scene_api.dart +++ b/lib/services/api/scene_api.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'package:syncrow_app/features/scene/model/create_automation_model.dart'; import 'package:syncrow_app/features/scene/model/create_scene_model.dart'; diff --git a/lib/services/api/spaces_api.dart b/lib/services/api/spaces_api.dart index 7a27bf1..7a2402a 100644 --- a/lib/services/api/spaces_api.dart +++ b/lib/services/api/spaces_api.dart @@ -60,6 +60,8 @@ class SpacesAPI { } } + //factory/reset/{deviceUuid} + static Future generateInvitationCode( String unitId, String communityId) async { final response = await _httpService.get( diff --git a/lib/utils/resource_manager/color_manager.dart b/lib/utils/resource_manager/color_manager.dart index c078eab..ac3a4bb 100644 --- a/lib/utils/resource_manager/color_manager.dart +++ b/lib/utils/resource_manager/color_manager.dart @@ -31,5 +31,8 @@ abstract class ColorsManager { static const Color switchButton = Color(0xff023DFE); static const Color grayBox = Color(0xffF5F5F5); static const Color chart = Color(0xff023DFE); + static const Color blueColor = Color(0xff5481F3); + static const Color blueColor1 = Color(0xff0A7AFF); + static const Color grayButtonColors = Color(0xffCCCCCC); } //background: #F5F5F5;023DFE diff --git a/lib/utils/resource_manager/constants.dart b/lib/utils/resource_manager/constants.dart index b1129b2..8ffaa7b 100644 --- a/lib/utils/resource_manager/constants.dart +++ b/lib/utils/resource_manager/constants.dart @@ -56,6 +56,9 @@ enum DeviceType { GarageDoor, WaterLeak, PC, + FourScene, + SixScene, + SOS, Other, } @@ -88,11 +91,17 @@ Map devicesTypesMap = { "GD": DeviceType.GarageDoor, "WL": DeviceType.WaterLeak, "PC": DeviceType.PC, + "4S": DeviceType.FourScene, + "6S": DeviceType.SixScene, + "SOS": DeviceType.SOS, }; + Map> devicesFunctionsMap = { DeviceType.AC: [ FunctionModel( - code: 'switch', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'mode', type: functionTypesMap['Enum'], @@ -115,7 +124,9 @@ Map> devicesFunctionsMap = { // "range": ["low", "middle", "high", "auto"] })), FunctionModel( - code: 'child_lock', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'child_lock', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), ], DeviceType.Gateway: [ FunctionModel( @@ -129,7 +140,9 @@ Map> devicesFunctionsMap = { "range": ["normal", "alarm"] })), FunctionModel( - code: 'factory_reset', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'factory_reset', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'alarm_active', type: functionTypesMap['String'], @@ -139,7 +152,8 @@ Map> devicesFunctionsMap = { FunctionModel( code: 'sensitivity', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "", "min": 1, "max": 10, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "", "min": 1, "max": 10, "scale": 0, "step": 1})), ], DeviceType.DoorLock: [ FunctionModel( @@ -147,7 +161,9 @@ Map> devicesFunctionsMap = { type: functionTypesMap['Raw'], values: ValueModel.fromJson({})), FunctionModel( - code: 'remote_no_dp_key', type: functionTypesMap['Raw'], values: ValueModel.fromJson({})), + code: 'remote_no_dp_key', + type: functionTypesMap['Raw'], + values: ValueModel.fromJson({})), FunctionModel( code: 'normal_open_switch', type: functionTypesMap['Boolean'], @@ -157,64 +173,87 @@ Map> devicesFunctionsMap = { FunctionModel( code: 'far_detection', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "cm", "min": 75, "max": 600, "scale": 0, "step": 75})), + values: ValueModel.fromJson( + {"unit": "cm", "min": 75, "max": 600, "scale": 0, "step": 75})), FunctionModel( code: 'presence_time', type: functionTypesMap['Integer'], - values: - ValueModel.fromJson({"unit": "Min", "min": 0, "max": 65535, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "Min", "min": 0, "max": 65535, "scale": 0, "step": 1})), FunctionModel( code: 'motion_sensitivity_value', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})), FunctionModel( code: 'motionless_sensitivity', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})), FunctionModel( - code: 'indicator', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'indicator', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), ], DeviceType.OneGang: [ FunctionModel( - code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'countdown_1', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), ], DeviceType.TwoGang: [ FunctionModel( - code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( - code: 'switch_2', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_2', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'countdown_1', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'countdown_2', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), ], DeviceType.ThreeGang: [ FunctionModel( - code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( - code: 'switch_2', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_2', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( - code: 'switch_3', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_3', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'countdown_1', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'countdown_2', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'countdown_3', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), ], DeviceType.Curtain: [ FunctionModel( @@ -226,15 +265,19 @@ Map> devicesFunctionsMap = { FunctionModel( code: 'percent_control', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "%", "min": 0, "max": 100, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "%", "min": 0, "max": 100, "scale": 0, "step": 1})), ], DeviceType.WH: [ FunctionModel( - code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'countdown_1', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'relay_status', type: functionTypesMap['Enum'], @@ -258,7 +301,9 @@ Map> devicesFunctionsMap = { ], DeviceType.DS: [ FunctionModel( - code: 'doorcontact_state', type: functionTypesMap['Raw'], values: ValueModel.fromJson({})), + code: 'doorcontact_state', + type: functionTypesMap['Raw'], + values: ValueModel.fromJson({})), FunctionModel( code: 'battery_percentage', type: functionTypesMap['Integer'], @@ -266,11 +311,14 @@ Map> devicesFunctionsMap = { ], DeviceType.OneTouch: [ FunctionModel( - code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'countdown_1', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'relay_status', type: functionTypesMap['Enum'], @@ -292,17 +340,23 @@ Map> devicesFunctionsMap = { ], DeviceType.TowTouch: [ FunctionModel( - code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( - code: 'switch_2', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_2', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'countdown_1', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'countdown_2', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'relay_status', type: functionTypesMap['Enum'], @@ -330,23 +384,32 @@ Map> devicesFunctionsMap = { ], DeviceType.ThreeTouch: [ FunctionModel( - code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( - code: 'switch_2', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_2', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( - code: 'switch_3', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_3', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'countdown_1', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'countdown_2', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'countdown_3', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'relay_status', type: functionTypesMap['Enum'], @@ -380,19 +443,24 @@ Map> devicesFunctionsMap = { ], DeviceType.GarageDoor: [ FunctionModel( - code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'countdown_1', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})), FunctionModel( code: 'tr_timecon', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 120, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 120, "scale": 0, "step": 1})), FunctionModel( code: 'countdown_alarm', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})), FunctionModel( code: 'door_control_1', type: functionTypesMap['Enum'], @@ -413,19 +481,24 @@ Map> devicesFunctionsMap = { DeviceType.WaterLeak: [], DeviceType.PC: [ FunctionModel( - code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'countdown_1', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})), FunctionModel( code: 'tr_timecon', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 120, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 120, "scale": 0, "step": 1})), FunctionModel( code: 'countdown_alarm', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})), FunctionModel( code: 'door_control_1', type: functionTypesMap['Enum'], @@ -443,6 +516,96 @@ Map> devicesFunctionsMap = { "range": ["unclosed_time", "close_time_alarm", "none"] })), ], + DeviceType.FourScene: [ + FunctionModel( + code: 'scene_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({ + "range": ["scene"] + })), + FunctionModel( + code: 'scene_2', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson({ + "range": ["scene"] + })), + FunctionModel( + code: 'scene_3', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson({ + "range": ["scene"] + })), + FunctionModel( + code: 'scene_4', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson({ + "range": ["scene"] + })), + FunctionModel( + code: 'scene_id_group_id', + type: functionTypesMap['Raw'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'switch_backlight', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({})), + ], + DeviceType.SixScene: [ + FunctionModel( + code: 'scene_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({ + "range": ["scene"] + })), + FunctionModel( + code: 'scene_2', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson({ + "range": ["scene"] + })), + FunctionModel( + code: 'scene_3', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson({ + "range": ["scene"] + })), + FunctionModel( + code: 'scene_4', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson({ + "range": ["scene"] + })), + FunctionModel( + code: 'scene_5', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson({ + "range": ["scene"] + })), + FunctionModel( + code: 'scene_6', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson({ + "range": ["scene"] + })), + FunctionModel( + code: 'scene_id_group_id', + type: functionTypesMap['Raw'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'switch_backlight', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({})), + ], + DeviceType.SOS: [ + FunctionModel( + code: 'contact_state', + type: functionTypesMap['Raw'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'battery_percentage', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson({})), + ], }; enum TempModes { hot, cold, wind } @@ -588,7 +751,11 @@ List> menuSections = [ 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsMessages, 'page': null }, - {'title': 'FAQs', 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsFAQs, 'page': null}, + { + 'title': 'FAQs', + 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsFAQs, + 'page': null + }, { 'title': 'Help & Feedback', 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsHelpAndFeedback, @@ -618,7 +785,11 @@ List> menuSections = [ 'title': 'Legal Information', 'color': const Color(0xFF001B72), 'buttons': [ - {'title': 'About', 'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsAbout, 'page': null}, + { + 'title': 'About', + 'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsAbout, + 'page': null + }, { 'title': 'Privacy Policy', 'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsPrivacyPolicy, diff --git a/pubspec.lock b/pubspec.lock index 4b11f46..057d001 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -704,6 +704,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.1" + percent_indicator: + dependency: "direct main" + description: + name: percent_indicator + sha256: c37099ad833a883c9d71782321cb65c3a848c21b6939b6185f0ff6640d05814c + url: "https://pub.dev" + source: hosted + version: "4.2.3" permission_handler: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index e2f30e0..bc5d09f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ description: This is the mobile application project, developed with Flutter for # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: "none" # Remove this line if you wish to publish to pub.dev -version: 1.0.7+40 +version: 1.0.9+45 environment: sdk: ">=3.0.6 <4.0.0" @@ -46,6 +46,7 @@ dependencies: device_info_plus: ^10.1.0 fl_chart: ^0.69.0 firebase_database: ^10.5.7 + percent_indicator: ^4.2.3 dev_dependencies: