Compare commits

..

24 Commits

Author SHA1 Message Date
76be98354b Bug fixes in the doorlock real time 2024-09-03 00:34:43 +03:00
3418fbe7b4 Merge pull request #48 from SyncrowIOT/sp-231
Sp 231
2024-09-02 15:05:31 +03:00
ea3bc4b5e1 curtain changes 2024-09-02 15:04:16 +03:00
e63bf2a2c2 Merge pull request #47 from SyncrowIOT/bugfix/SP-286
fix: Shows only one error at a time for password validation error
2024-09-02 09:05:03 +04:00
bf944a6121 curtain changes 2024-09-01 10:21:04 +03:00
eb5aa56536 curtain changes 2024-08-29 16:57:15 +03:00
1e35bb8736 curtain changes 2024-08-29 16:56:19 +03:00
9c23ab1399 curtain changes 2024-08-29 16:54:43 +03:00
b3bb0b9eea fix: Correct error message and improve consistency in passwordValidator 2024-08-29 16:37:49 +04:00
dcc98445d7 Implemented smart door animation 2024-08-26 16:45:50 +03:00
611c515173 Updated icons and bug fixes 2024-08-21 16:10:57 +03:00
e733dd9230 Updated the build number 2024-08-12 17:39:02 +03:00
8e104aeea7 Read devices status from Firebase realtime database 2024-08-12 12:32:20 +03:00
f6fbf452a0 Merge pull request #45 from SyncrowIOT/fix_bugs_doorlock_qa
fix password
2024-08-07 17:25:48 +03:00
5aec3d37fb fix password 2024-08-07 17:20:19 +03:00
a32d885e50 Changed the action executor of the tab to run scenes and changed the order of the effective days 2024-08-07 15:52:27 +03:00
afe37dd68a Merge pull request #44 from SyncrowIOT/automation_fixes3
Automation fixes3
2024-08-06 15:28:38 +03:00
f83224ce60 Merge pull request #43 from SyncrowIOT/update_door_lock
door lock password update
2024-08-06 15:07:12 +03:00
0ef2f3b866 door lock password update 2024-08-06 14:58:06 +03:00
3f0fcc79aa set default value for tempreture at confirm 2024-08-05 21:39:11 +03:00
33ec234c0d push fixes to days un-select and temp_current normilization 2024-08-05 21:19:07 +03:00
905399f037 Bug fixes 2024-08-05 11:06:59 +03:00
c31e54afc6 Updated the build number 2024-08-04 23:41:21 +03:00
14825524e8 Merge pull request #42 from SyncrowIOT/automation_fixes2
Automation fixes2
2024-08-04 23:31:04 +03:00
98 changed files with 3144 additions and 1517 deletions

View File

@ -1,46 +1,54 @@
PODS: PODS:
- device_info_plus (0.0.1): - device_info_plus (0.0.1):
- Flutter - Flutter
- Firebase/Analytics (10.20.0): - Firebase/Analytics (10.25.0):
- Firebase/Core - Firebase/Core
- Firebase/Core (10.20.0): - Firebase/Core (10.25.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseAnalytics (~> 10.20.0) - FirebaseAnalytics (~> 10.25.0)
- Firebase/CoreOnly (10.20.0): - Firebase/CoreOnly (10.25.0):
- FirebaseCore (= 10.20.0) - FirebaseCore (= 10.25.0)
- Firebase/Crashlytics (10.20.0): - Firebase/Crashlytics (10.25.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseCrashlytics (~> 10.20.0) - FirebaseCrashlytics (~> 10.25.0)
- Firebase/Database (10.25.0):
- Firebase/CoreOnly
- FirebaseDatabase (~> 10.25.0)
- firebase_analytics (10.8.7): - firebase_analytics (10.8.7):
- Firebase/Analytics (= 10.20.0) - Firebase/Analytics (= 10.25.0)
- firebase_core - firebase_core
- Flutter - Flutter
- firebase_core (2.25.5): - firebase_core (2.32.0):
- Firebase/CoreOnly (= 10.20.0) - Firebase/CoreOnly (= 10.25.0)
- Flutter - Flutter
- firebase_crashlytics (3.4.16): - firebase_crashlytics (3.4.16):
- Firebase/Crashlytics (= 10.20.0) - Firebase/Crashlytics (= 10.25.0)
- firebase_core - firebase_core
- Flutter - Flutter
- FirebaseAnalytics (10.20.0): - firebase_database (10.5.7):
- FirebaseAnalytics/AdIdSupport (= 10.20.0) - Firebase/Database (= 10.25.0)
- firebase_core
- Flutter
- FirebaseAnalytics (10.25.0):
- FirebaseAnalytics/AdIdSupport (= 10.25.0)
- FirebaseCore (~> 10.0) - FirebaseCore (~> 10.0)
- FirebaseInstallations (~> 10.0) - FirebaseInstallations (~> 10.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.11) - GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11) - GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11) - GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)" - "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0) - nanopb (< 2.30911.0, >= 2.30908.0)
- FirebaseAnalytics/AdIdSupport (10.20.0): - FirebaseAnalytics/AdIdSupport (10.25.0):
- FirebaseCore (~> 10.0) - FirebaseCore (~> 10.0)
- FirebaseInstallations (~> 10.0) - FirebaseInstallations (~> 10.0)
- GoogleAppMeasurement (= 10.20.0) - GoogleAppMeasurement (= 10.25.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.11) - GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11) - GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11) - GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)" - "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0) - nanopb (< 2.30911.0, >= 2.30908.0)
- FirebaseCore (10.20.0): - FirebaseAppCheckInterop (10.29.0)
- FirebaseCore (10.25.0):
- FirebaseCoreInternal (~> 10.0) - FirebaseCoreInternal (~> 10.0)
- GoogleUtilities/Environment (~> 7.12) - GoogleUtilities/Environment (~> 7.12)
- GoogleUtilities/Logger (~> 7.12) - GoogleUtilities/Logger (~> 7.12)
@ -48,19 +56,27 @@ PODS:
- FirebaseCore (~> 10.0) - FirebaseCore (~> 10.0)
- FirebaseCoreInternal (10.29.0): - FirebaseCoreInternal (10.29.0):
- "GoogleUtilities/NSData+zlib (~> 7.8)" - "GoogleUtilities/NSData+zlib (~> 7.8)"
- FirebaseCrashlytics (10.20.0): - FirebaseCrashlytics (10.25.0):
- FirebaseCore (~> 10.5) - FirebaseCore (~> 10.5)
- FirebaseInstallations (~> 10.0) - FirebaseInstallations (~> 10.0)
- FirebaseRemoteConfigInterop (~> 10.23)
- FirebaseSessions (~> 10.5) - FirebaseSessions (~> 10.5)
- GoogleDataTransport (~> 9.2) - GoogleDataTransport (~> 9.2)
- GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/Environment (~> 7.8)
- nanopb (< 2.30910.0, >= 2.30908.0) - nanopb (< 2.30911.0, >= 2.30908.0)
- PromisesObjC (~> 2.1) - PromisesObjC (~> 2.1)
- FirebaseDatabase (10.25.0):
- FirebaseAppCheckInterop (~> 10.17)
- FirebaseCore (~> 10.0)
- FirebaseSharedSwift (~> 10.0)
- GoogleUtilities/UserDefaults (~> 7.13)
- leveldb-library (~> 1.22)
- FirebaseInstallations (10.29.0): - FirebaseInstallations (10.29.0):
- FirebaseCore (~> 10.0) - FirebaseCore (~> 10.0)
- GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/Environment (~> 7.8)
- GoogleUtilities/UserDefaults (~> 7.8) - GoogleUtilities/UserDefaults (~> 7.8)
- PromisesObjC (~> 2.1) - PromisesObjC (~> 2.1)
- FirebaseRemoteConfigInterop (10.29.0)
- FirebaseSessions (10.29.0): - FirebaseSessions (10.29.0):
- FirebaseCore (~> 10.5) - FirebaseCore (~> 10.5)
- FirebaseCoreExtension (~> 10.0) - FirebaseCoreExtension (~> 10.0)
@ -70,31 +86,32 @@ PODS:
- GoogleUtilities/UserDefaults (~> 7.13) - GoogleUtilities/UserDefaults (~> 7.13)
- nanopb (< 2.30911.0, >= 2.30908.0) - nanopb (< 2.30911.0, >= 2.30908.0)
- PromisesSwift (~> 2.1) - PromisesSwift (~> 2.1)
- FirebaseSharedSwift (10.29.0)
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_localization (0.0.1): - flutter_localization (0.0.1):
- Flutter - Flutter
- flutter_secure_storage (6.0.0): - flutter_secure_storage (6.0.0):
- Flutter - Flutter
- GoogleAppMeasurement (10.20.0): - GoogleAppMeasurement (10.25.0):
- GoogleAppMeasurement/AdIdSupport (= 10.20.0) - GoogleAppMeasurement/AdIdSupport (= 10.25.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.11) - GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11) - GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11) - GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)" - "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0) - nanopb (< 2.30911.0, >= 2.30908.0)
- GoogleAppMeasurement/AdIdSupport (10.20.0): - GoogleAppMeasurement/AdIdSupport (10.25.0):
- GoogleAppMeasurement/WithoutAdIdSupport (= 10.20.0) - GoogleAppMeasurement/WithoutAdIdSupport (= 10.25.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.11) - GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11) - GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11) - GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)" - "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0) - nanopb (< 2.30911.0, >= 2.30908.0)
- GoogleAppMeasurement/WithoutAdIdSupport (10.20.0): - GoogleAppMeasurement/WithoutAdIdSupport (10.25.0):
- GoogleUtilities/AppDelegateSwizzler (~> 7.11) - GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11) - GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11) - GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)" - "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0) - nanopb (< 2.30911.0, >= 2.30908.0)
- GoogleDataTransport (9.4.1): - GoogleDataTransport (9.4.1):
- GoogleUtilities/Environment (~> 7.7) - GoogleUtilities/Environment (~> 7.7)
- nanopb (< 2.30911.0, >= 2.30908.0) - nanopb (< 2.30911.0, >= 2.30908.0)
@ -129,11 +146,12 @@ PODS:
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- image_picker_ios (0.0.1): - image_picker_ios (0.0.1):
- Flutter - Flutter
- nanopb (2.30909.1): - leveldb-library (1.22.5)
- nanopb/decode (= 2.30909.1) - nanopb (2.30910.0):
- nanopb/encode (= 2.30909.1) - nanopb/decode (= 2.30910.0)
- nanopb/decode (2.30909.1) - nanopb/encode (= 2.30910.0)
- nanopb/encode (2.30909.1) - nanopb/decode (2.30910.0)
- nanopb/encode (2.30910.0)
- onesignal_flutter (5.2.0): - onesignal_flutter (5.2.0):
- Flutter - Flutter
- OneSignalXCFramework (= 5.2.0) - OneSignalXCFramework (= 5.2.0)
@ -207,6 +225,7 @@ DEPENDENCIES:
- firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
- firebase_database (from `.symlinks/plugins/firebase_database/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_localization (from `.symlinks/plugins/flutter_localization/ios`) - flutter_localization (from `.symlinks/plugins/flutter_localization/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
@ -223,15 +242,20 @@ SPEC REPOS:
trunk: trunk:
- Firebase - Firebase
- FirebaseAnalytics - FirebaseAnalytics
- FirebaseAppCheckInterop
- FirebaseCore - FirebaseCore
- FirebaseCoreExtension - FirebaseCoreExtension
- FirebaseCoreInternal - FirebaseCoreInternal
- FirebaseCrashlytics - FirebaseCrashlytics
- FirebaseDatabase
- FirebaseInstallations - FirebaseInstallations
- FirebaseRemoteConfigInterop
- FirebaseSessions - FirebaseSessions
- FirebaseSharedSwift
- GoogleAppMeasurement - GoogleAppMeasurement
- GoogleDataTransport - GoogleDataTransport
- GoogleUtilities - GoogleUtilities
- leveldb-library
- nanopb - nanopb
- OneSignalXCFramework - OneSignalXCFramework
- PromisesObjC - PromisesObjC
@ -246,6 +270,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/firebase_core/ios" :path: ".symlinks/plugins/firebase_core/ios"
firebase_crashlytics: firebase_crashlytics:
:path: ".symlinks/plugins/firebase_crashlytics/ios" :path: ".symlinks/plugins/firebase_crashlytics/ios"
firebase_database:
:path: ".symlinks/plugins/firebase_database/ios"
Flutter: Flutter:
:path: Flutter :path: Flutter
flutter_localization: flutter_localization:
@ -271,25 +297,31 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
Firebase: 10c8cb12fb7ad2ae0c09ffc86cd9c1ab392a0031 Firebase: 0312a2352584f782ea56f66d91606891d4607f06
firebase_analytics: 2c1c3057d5da3bd3aab819f7e6ee153a4e46c59e firebase_analytics: 3a9263fedec72970e6bd30a7132bbdd386de2c14
firebase_core: c8628c7ce80f79439149549052bff22f6784fbf5 firebase_core: a626d00494efa398e7c54f25f1454a64c8abf197
firebase_crashlytics: 012078b4eec6fc9716f97ba3da0f0e44a04e95b1 firebase_crashlytics: 0b7cb41f5fb3b6889d0fb408cfce3cc7a4247061
FirebaseAnalytics: a2731bf3670747ce8f65368b118d18aa8e368246 firebase_database: 2713033e426b176d4fe5e7195f3d19aa1b549a91
FirebaseCore: 28045c1560a2600d284b9c45a904fe322dc890b6 FirebaseAnalytics: ec00fe8b93b41dc6fe4a28784b8e51da0647a248
FirebaseAppCheckInterop: 6a1757cfd4067d8e00fccd14fcc1b8fd78cfac07
FirebaseCore: 7ec4d0484817f12c3373955bc87762d96842d483
FirebaseCoreExtension: 705ca5b14bf71d2564a0ddc677df1fc86ffa600f FirebaseCoreExtension: 705ca5b14bf71d2564a0ddc677df1fc86ffa600f
FirebaseCoreInternal: df84dd300b561c27d5571684f389bf60b0a5c934 FirebaseCoreInternal: df84dd300b561c27d5571684f389bf60b0a5c934
FirebaseCrashlytics: 81530595edb6d99f1918f723a6c33766a24a4c86 FirebaseCrashlytics: 4b96efb0ce73b38b2a85e8b8bd1bd8f63f09d015
FirebaseDatabase: faa489a42f5f868d23a55dd442d6e2099348458e
FirebaseInstallations: 913cf60d0400ebd5d6b63a28b290372ab44590dd FirebaseInstallations: 913cf60d0400ebd5d6b63a28b290372ab44590dd
FirebaseRemoteConfigInterop: 6efda51fb5e2f15b16585197e26eaa09574e8a4d
FirebaseSessions: dbd14adac65ce996228652c1fc3a3f576bdf3ecc FirebaseSessions: dbd14adac65ce996228652c1fc3a3f576bdf3ecc
FirebaseSharedSwift: 20530f495084b8d840f78a100d8c5ee613375f6e
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_localization: f43b18844a2b3d2c71fd64f04ffd6b1e64dd54d4 flutter_localization: f43b18844a2b3d2c71fd64f04ffd6b1e64dd54d4
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
GoogleAppMeasurement: bb3c564c3efb933136af0e94899e0a46167466a8 GoogleAppMeasurement: 9abf64b682732fed36da827aa2a68f0221fd2356
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5 leveldb-library: e8eadf9008a61f9e1dde3978c086d2b6d9b9dc28
nanopb: 438bc412db1928dac798aa6fd75726007be04262
onesignal_flutter: 5ce68a29861960168e81101cb1bd685d264361de onesignal_flutter: 5ce68a29861960168e81101cb1bd685d264361de
OneSignalXCFramework: bdf74fdc06888f9466dc21e826fe1549ed143095 OneSignalXCFramework: bdf74fdc06888f9466dc21e826fe1549ed143095
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c

View File

@ -514,8 +514,8 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = 20;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = 48V27SBR8J;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Syncrow; INFOPLIST_KEY_CFBundleDisplayName = Syncrow;
@ -524,7 +524,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.0; MARKETING_VERSION = 1.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.example.syncrow.app; PRODUCT_BUNDLE_IDENTIFIER = com.example.syncrow.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -706,8 +706,8 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = 20;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = 48V27SBR8J;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Syncrow; INFOPLIST_KEY_CFBundleDisplayName = Syncrow;
@ -716,7 +716,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.0; MARKETING_VERSION = 1.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.example.syncrow.app; PRODUCT_BUNDLE_IDENTIFIER = com.example.syncrow.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -736,8 +736,8 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = 20;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = 48V27SBR8J;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Syncrow; INFOPLIST_KEY_CFBundleDisplayName = Syncrow;
@ -746,7 +746,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.0; MARKETING_VERSION = 1.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.example.syncrow.app; PRODUCT_BUNDLE_IDENTIFIER = com.example.syncrow.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";

View File

@ -1,115 +1,115 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "logo-20@2x.png", "filename" : "Syncrow Icon-20@2x.png",
"idiom" : "iphone", "idiom" : "iphone",
"scale" : "2x", "scale" : "2x",
"size" : "20x20" "size" : "20x20"
}, },
{ {
"filename" : "logo-20@3x.png", "filename" : "Syncrow Icon-20@3x.png",
"idiom" : "iphone", "idiom" : "iphone",
"scale" : "3x", "scale" : "3x",
"size" : "20x20" "size" : "20x20"
}, },
{ {
"filename" : "logo-29.png", "filename" : "Syncrow Icon-29.png",
"idiom" : "iphone", "idiom" : "iphone",
"scale" : "1x", "scale" : "1x",
"size" : "29x29" "size" : "29x29"
}, },
{ {
"filename" : "logo-29@2x.png", "filename" : "Syncrow Icon-29@2x.png",
"idiom" : "iphone", "idiom" : "iphone",
"scale" : "2x", "scale" : "2x",
"size" : "29x29" "size" : "29x29"
}, },
{ {
"filename" : "logo-29@3x.png", "filename" : "Syncrow Icon-29@3x.png",
"idiom" : "iphone", "idiom" : "iphone",
"scale" : "3x", "scale" : "3x",
"size" : "29x29" "size" : "29x29"
}, },
{ {
"filename" : "logo-40@2x.png", "filename" : "Syncrow Icon-40@2x.png",
"idiom" : "iphone", "idiom" : "iphone",
"scale" : "2x", "scale" : "2x",
"size" : "40x40" "size" : "40x40"
}, },
{ {
"filename" : "logo-40@3x.png", "filename" : "Syncrow Icon-40@3x.png",
"idiom" : "iphone", "idiom" : "iphone",
"scale" : "3x", "scale" : "3x",
"size" : "40x40" "size" : "40x40"
}, },
{ {
"filename" : "logo-60@2x.png", "filename" : "Syncrow Icon-60@2x.png",
"idiom" : "iphone", "idiom" : "iphone",
"scale" : "2x", "scale" : "2x",
"size" : "60x60" "size" : "60x60"
}, },
{ {
"filename" : "logo-60@3x.png", "filename" : "Syncrow Icon-60@3x.png",
"idiom" : "iphone", "idiom" : "iphone",
"scale" : "3x", "scale" : "3x",
"size" : "60x60" "size" : "60x60"
}, },
{ {
"filename" : "logo-20.png", "filename" : "Syncrow Icon-20.png",
"idiom" : "ipad", "idiom" : "ipad",
"scale" : "1x", "scale" : "1x",
"size" : "20x20" "size" : "20x20"
}, },
{ {
"filename" : "logo-20@2x 1.png", "filename" : "Syncrow Icon-20@2x 1.png",
"idiom" : "ipad", "idiom" : "ipad",
"scale" : "2x", "scale" : "2x",
"size" : "20x20" "size" : "20x20"
}, },
{ {
"filename" : "logo-29 1.png", "filename" : "Syncrow Icon-29 1.png",
"idiom" : "ipad", "idiom" : "ipad",
"scale" : "1x", "scale" : "1x",
"size" : "29x29" "size" : "29x29"
}, },
{ {
"filename" : "logo-29@2x 1.png", "filename" : "Syncrow Icon-29@2x 1.png",
"idiom" : "ipad", "idiom" : "ipad",
"scale" : "2x", "scale" : "2x",
"size" : "29x29" "size" : "29x29"
}, },
{ {
"filename" : "logo-40.png", "filename" : "Syncrow Icon-40.png",
"idiom" : "ipad", "idiom" : "ipad",
"scale" : "1x", "scale" : "1x",
"size" : "40x40" "size" : "40x40"
}, },
{ {
"filename" : "logo-40@2x 1.png", "filename" : "Syncrow Icon-40@2x 1.png",
"idiom" : "ipad", "idiom" : "ipad",
"scale" : "2x", "scale" : "2x",
"size" : "40x40" "size" : "40x40"
}, },
{ {
"filename" : "logo-76.png", "filename" : "Syncrow Icon-76.png",
"idiom" : "ipad", "idiom" : "ipad",
"scale" : "1x", "scale" : "1x",
"size" : "76x76" "size" : "76x76"
}, },
{ {
"filename" : "logo-76@2x.png", "filename" : "Syncrow Icon-76@2x.png",
"idiom" : "ipad", "idiom" : "ipad",
"scale" : "2x", "scale" : "2x",
"size" : "76x76" "size" : "76x76"
}, },
{ {
"filename" : "logo-83.5@2x.png", "filename" : "Syncrow Icon-83.5@2x.png",
"idiom" : "ipad", "idiom" : "ipad",
"scale" : "2x", "scale" : "2x",
"size" : "83.5x83.5" "size" : "83.5x83.5"
}, },
{ {
"filename" : "logo-1024.png", "filename" : "Syncrow Icon-1024.png",
"idiom" : "ios-marketing", "idiom" : "ios-marketing",
"scale" : "1x", "scale" : "1x",
"size" : "1024x1024" "size" : "1024x1024"

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -11,10 +11,7 @@ import 'package:syncrow_app/features/app_layout/view/widgets/app_bar_home_dropdo
import 'package:syncrow_app/features/auth/model/user_model.dart'; import 'package:syncrow_app/features/auth/model/user_model.dart';
import 'package:syncrow_app/features/dashboard/view/dashboard_view.dart'; import 'package:syncrow_app/features/dashboard/view/dashboard_view.dart';
import 'package:syncrow_app/features/devices/bloc/devices_cubit.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/model/room_model.dart'; import 'package:syncrow_app/features/devices/model/room_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_view.dart';
import 'package:syncrow_app/features/devices/view/widgets/devices_view_body.dart'; import 'package:syncrow_app/features/devices/view/widgets/devices_view_body.dart';
import 'package:syncrow_app/features/menu/view/menu_view.dart'; import 'package:syncrow_app/features/menu/view/menu_view.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
@ -22,10 +19,7 @@ import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_b
import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart'; import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart';
import 'package:syncrow_app/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart';
import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart'; import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart';
import 'package:syncrow_app/features/scene/model/create_automation_model.dart';
import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments.dart'; import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments.dart';
import 'package:syncrow_app/features/scene/view/create_scene_view.dart';
import 'package:syncrow_app/features/scene/view/scene_tasks_view.dart';
import 'package:syncrow_app/features/scene/view/scene_view.dart'; import 'package:syncrow_app/features/scene/view/scene_view.dart';
import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/navigation/navigation_service.dart'; import 'package:syncrow_app/navigation/navigation_service.dart';
@ -33,10 +27,8 @@ import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/services/api/devices_api.dart'; import 'package:syncrow_app/services/api/devices_api.dart';
import 'package:syncrow_app/services/api/profile_api.dart'; import 'package:syncrow_app/services/api/profile_api.dart';
import 'package:syncrow_app/services/api/spaces_api.dart'; import 'package:syncrow_app/services/api/spaces_api.dart';
import 'package:syncrow_app/utils/helpers/custom_page_route.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart'; import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
part 'home_state.dart'; part 'home_state.dart';
@ -161,7 +153,7 @@ class HomeCubit extends Cubit<HomeState> {
} }
_sendSubscriptionId() async { _sendSubscriptionId() async {
String? subscriptionId = OneSignal.User.pushSubscription.id ?? ''; // String? subscriptionId = OneSignal.User.pushSubscription.id ?? '';
//TODO send the subscription id to BE //TODO send the subscription id to BE
} }
@ -349,47 +341,47 @@ class HomeCubit extends Cubit<HomeState> {
// ), // ),
// onPressed: () {}, // onPressed: () {},
// ), // ),
// IconButton( IconButton(
// icon: const Icon( icon: const Icon(
// Icons.add, Icons.add,
// size: 32, size: 32,
// ), ),
// style: ButtonStyle( style: ButtonStyle(
// foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor), foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor),
// ), ),
// onPressed: () { onPressed: () {
// Navigator.pushNamed( Navigator.pushNamed(
// NavigationService.navigatorKey.currentContext!, NavigationService.navigatorKey.currentContext!,
// Routes.sceneTasksRoute, Routes.sceneTasksRoute,
// arguments: SceneSettingsRouteArguments( arguments: SceneSettingsRouteArguments(
// sceneType: '', sceneType: '',
// sceneId: '', sceneId: '',
// sceneName: '', sceneName: '',
// ), ),
// ); );
// NavigationService.navigatorKey.currentContext! NavigationService.navigatorKey.currentContext!
// .read<CreateSceneBloc>() .read<CreateSceneBloc>()
// .add(const ClearTaskListEvent()); .add(const ClearTaskListEvent());
// NavigationService.navigatorKey.currentContext! NavigationService.navigatorKey.currentContext!
// .read<CreateSceneBloc>() .read<CreateSceneBloc>()
// .add(const SceneTypeEvent(CreateSceneEnum.none)); .add(const SceneTypeEvent(CreateSceneEnum.none));
// NavigationService.navigatorKey.currentContext! NavigationService.navigatorKey.currentContext!
// .read<SmartSceneSelectBloc>() .read<SmartSceneSelectBloc>()
// .add(const SmartSceneClearEvent()); .add(const SmartSceneClearEvent());
// BlocProvider.of<EffectPeriodBloc>(NavigationService.navigatorKey.currentState!.context) BlocProvider.of<EffectPeriodBloc>(NavigationService.navigatorKey.currentState!.context)
// .add(ResetEffectivePeriod()); .add(ResetEffectivePeriod());
// }, },
// ), ),
// IconButton( IconButton(
// icon: const Icon( icon: const Icon(
// Icons.more_vert, Icons.more_vert,
// size: 28, size: 28,
// ), ),
// style: ButtonStyle( style: ButtonStyle(
// foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor), foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor),
// ), ),
// onPressed: () {}, onPressed: () {},
// ), ),
], ],
'Menu': [ 'Menu': [
IconButton( IconButton(

View File

@ -1,4 +1,3 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
@ -59,18 +58,30 @@ class AuthCubit extends Cubit<AuthState> {
/////////////////////////////////////VALIDATORS///////////////////////////////////// /////////////////////////////////////VALIDATORS/////////////////////////////////////
String? passwordValidator(String? value) { String? passwordValidator(String? value) {
if (value != null) { if (value == null || value.isEmpty) {
if (value.isEmpty) { return "Please enter your password";
return 'Please enter your password';
}
if (value.isNotEmpty) {
if (!RegExp(
r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!"#$%&()*+,-./:;<=>?@[\]^_`{|}~])[A-Za-z\d!"#$%&()*+,-./:;<=>?@[\]^_`{|}~]{8,}$')
.hasMatch(value)) {
return 'Password must contain at least:\n - one uppercase letter.\n - one lowercase letter.\n - one number. \n - special character';
}
}
} }
if (value.length < 8) {
return 'Password must be at least 8 characters long';
}
if (!RegExp(r'[a-z]').hasMatch(value)) {
return 'Password must contain at least one lowercase letter';
}
if (!RegExp(r'[A-Z]').hasMatch(value)) {
return 'Password must contain at least one uppercase letter';
}
if (!RegExp(r'\d').hasMatch(value)) {
return 'Password must contain at least one number';
}
if (!RegExp(r'[!"#$%&()*+,-./:;<=>?@[\]^_`{|}~]').hasMatch(value)) {
return 'Password must contain at least one special character';
}
return null; return null;
} }
@ -182,13 +193,10 @@ class AuthCubit extends Cubit<AuthState> {
debugPrint('token: ${token.accessToken}'); debugPrint('token: ${token.accessToken}');
FlutterSecureStorage storage = const FlutterSecureStorage(); FlutterSecureStorage storage = const FlutterSecureStorage();
await storage.write( await storage.write(
key: Token.loginAccessTokenKey, key: Token.loginAccessTokenKey, value: token.accessToken);
value: token.accessToken
);
const FlutterSecureStorage().write( const FlutterSecureStorage().write(
key: UserModel.userUuidKey, key: UserModel.userUuidKey,
value: Token.decodeToken(token.accessToken)['uuid'].toString() value: Token.decodeToken(token.accessToken)['uuid'].toString());
);
user = UserModel.fromToken(token); user = UserModel.fromToken(token);
emailController.clear(); emailController.clear();
passwordController.clear(); passwordController.clear();
@ -225,7 +233,8 @@ class AuthCubit extends Cubit<AuthState> {
sendOtp() async { sendOtp() async {
try { try {
emit(AuthLoading()); emit(AuthLoading());
await AuthenticationAPI.sendOtp(body: {'email': email, 'type': 'VERIFICATION'}); await AuthenticationAPI.sendOtp(
body: {'email': email, 'type': 'VERIFICATION'});
emit(AuthSignUpSuccess()); emit(AuthSignUpSuccess());
} catch (_) { } catch (_) {
emit(AuthLoginError(message: 'Something went wrong')); emit(AuthLoginError(message: 'Something went wrong'));
@ -235,7 +244,8 @@ class AuthCubit extends Cubit<AuthState> {
reSendOtp() async { reSendOtp() async {
try { try {
emit(AuthLoading()); emit(AuthLoading());
await AuthenticationAPI.sendOtp(body: {'email': email, 'type': 'VERIFICATION'}); await AuthenticationAPI.sendOtp(
body: {'email': email, 'type': 'VERIFICATION'});
emit(ResendOtpSuccess()); emit(ResendOtpSuccess());
} catch (_) { } catch (_) {
emit(AuthLoginError(message: 'Something went wrong')); emit(AuthLoginError(message: 'Something went wrong'));
@ -282,13 +292,16 @@ class AuthCubit extends Cubit<AuthState> {
try { try {
emit(AuthTokenLoading()); emit(AuthTokenLoading());
const storage = FlutterSecureStorage(); const storage = FlutterSecureStorage();
final firstLaunch = await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true; final firstLaunch = await SharedPreferencesHelper.readBoolFromSP(
StringsManager.firstLaunch) ??
true;
if (firstLaunch) { if (firstLaunch) {
storage.deleteAll(); storage.deleteAll();
} }
await SharedPreferencesHelper.saveBoolToSP(StringsManager.firstLaunch, false); await SharedPreferencesHelper.saveBoolToSP(
StringsManager.firstLaunch, false);
final value = await storage.read(key: Token.loginAccessTokenKey) ?? ''; final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
if (value.isEmpty) { if (value.isEmpty) {
@ -315,7 +328,6 @@ class AuthCubit extends Cubit<AuthState> {
} }
} }
sendToForgetPassword({required String password}) async { sendToForgetPassword({required String password}) async {
try { try {
emit(AuthForgetPassLoading()); emit(AuthForgetPassLoading());
@ -325,8 +337,4 @@ class AuthCubit extends Cubit<AuthState> {
emit(AuthForgetPassError(message: 'Something went wrong')); emit(AuthForgetPassError(message: 'Something went wrong'));
} }
} }
} }

View File

@ -1,5 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.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/bloc/home_cubit.dart';
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_event.dart'; import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_event.dart';
@ -40,6 +40,7 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
on<ChangeAllSwitch>(_changeAllAcSwitch); on<ChangeAllSwitch>(_changeAllAcSwitch);
on<IncreaseAllTemp>(_increaseAllTemp); on<IncreaseAllTemp>(_increaseAllTemp);
on<DecreaseAllTemp>(_decreaseAllTemp); on<DecreaseAllTemp>(_decreaseAllTemp);
on<AcUpdated>(_onAcUpdated);
} }
void _fetchAcsStatus(AcsInitial event, Emitter<AcsState> emit) async { void _fetchAcsStatus(AcsInitial event, Emitter<AcsState> emit) async {
@ -62,6 +63,8 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
} }
deviceStatus = AcStatusModel.fromJson(response['productUuid'], statusModelList); deviceStatus = AcStatusModel.fromJson(response['productUuid'], statusModelList);
emit(GetAcStatusState(acStatusModel: deviceStatus)); emit(GetAcStatusState(acStatusModel: deviceStatus));
Future.delayed(const Duration(milliseconds: 500));
_listenToChanges();
} }
} catch (e) { } catch (e) {
emit(AcsFailedState(errorMessage: e.toString())); emit(AcsFailedState(errorMessage: e.toString()));
@ -69,6 +72,29 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
} }
} }
_listenToChanges() {
try {
DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$acId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
List<StatusModel> statusList = [];
usersMap['status'].forEach((element) {
statusList.add(StatusModel(code: element['code'], value: element['value']));
});
deviceStatus = AcStatusModel.fromJson(usersMap['productUuid'], statusList);
add(AcUpdated());
});
} catch (_) {}
}
_onAcUpdated(AcUpdated event, Emitter<AcsState> emit) {
emit(GetAcStatusState(acStatusModel: deviceStatus));
}
_getAllAcs() async { _getAllAcs() async {
deviceStatusList = []; deviceStatusList = [];
devicesList = []; devicesList = [];
@ -164,7 +190,7 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
deviceStatus.childLock = lockValue; deviceStatus.childLock = lockValue;
emit(AcModifyingState(acStatusModel: deviceStatus)); emit(AcModifyingState(acStatusModel: deviceStatus));
_runDeBouncerForOneDevice(deviceId: acId, code: 'child_lock', value: lockValue); await _runDeBouncerForOneDevice(deviceId: acId, code: 'child_lock', value: lockValue);
} }
void _increaseCoolTo(IncreaseCoolToTemp event, Emitter<AcsState> emit) async { void _increaseCoolTo(IncreaseCoolToTemp event, Emitter<AcsState> emit) async {

View File

@ -20,6 +20,8 @@ class AcSwitch extends AcsEvent {
List<Object> get props => [acSwitch, deviceId, productId]; List<Object> get props => [acSwitch, deviceId, productId];
} }
class AcUpdated extends AcsEvent {}
class AcsInitial extends AcsEvent { class AcsInitial extends AcsEvent {
final bool allAcs; final bool allAcs;
const AcsInitial({required this.allAcs}); const AcsInitial({required this.allAcs});

View File

@ -1,3 +1,4 @@
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/ceiling_bloc/ceiling_sensor_event.dart'; import 'package:syncrow_app/features/devices/bloc/ceiling_bloc/ceiling_sensor_event.dart';
import 'package:syncrow_app/features/devices/bloc/ceiling_bloc/ceiling_sensor_state.dart'; import 'package:syncrow_app/features/devices/bloc/ceiling_bloc/ceiling_sensor_state.dart';
@ -15,6 +16,7 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
CeilingSensorBloc({required this.deviceId}) : super(InitialState()) { CeilingSensorBloc({required this.deviceId}) : super(InitialState()) {
on<InitialEvent>(_fetchCeilingSensorStatus); on<InitialEvent>(_fetchCeilingSensorStatus);
on<ChangeValueEvent>(_changeValue); on<ChangeValueEvent>(_changeValue);
on<CeilingSensorUpdated>(_onCeilingSensorUpdated);
} }
void _fetchCeilingSensorStatus(InitialEvent event, Emitter<CeilingSensorState> emit) async { void _fetchCeilingSensorStatus(InitialEvent event, Emitter<CeilingSensorState> emit) async {
@ -27,12 +29,36 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
} }
deviceStatus = CeilingSensorModel.fromJson(statusModelList); deviceStatus = CeilingSensorModel.fromJson(statusModelList);
emit(UpdateState(ceilingSensorModel: deviceStatus)); emit(UpdateState(ceilingSensorModel: deviceStatus));
_listenToChanges();
} catch (e) { } catch (e) {
emit(FailedState(error: e.toString())); emit(FailedState(error: e.toString()));
return; return;
} }
} }
_listenToChanges() {
try {
DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
List<StatusModel> statusList = [];
usersMap['status'].forEach((element) {
statusList.add(StatusModel(code: element['code'], value: element['value']));
});
deviceStatus = CeilingSensorModel.fromJson(statusList);
add(CeilingSensorUpdated());
});
} catch (_) {}
}
_onCeilingSensorUpdated(CeilingSensorUpdated event, Emitter<CeilingSensorState> emit) {
emit(UpdateState(ceilingSensorModel: deviceStatus));
}
void _changeValue(ChangeValueEvent event, Emitter<CeilingSensorState> emit) async { void _changeValue(ChangeValueEvent event, Emitter<CeilingSensorState> emit) async {
emit(LoadingNewSate(ceilingSensorModel: deviceStatus)); emit(LoadingNewSate(ceilingSensorModel: deviceStatus));
try { try {

View File

@ -11,6 +11,8 @@ class LoadingEvent extends CeilingSensorEvent {}
class InitialEvent extends CeilingSensorEvent {} class InitialEvent extends CeilingSensorEvent {}
class CeilingSensorUpdated extends CeilingSensorEvent {}
class ChangeValueEvent extends CeilingSensorEvent { class ChangeValueEvent extends CeilingSensorEvent {
final int value; final int value;
final String code; final String code;

View File

@ -0,0 +1,184 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_event.dart';
import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_state.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart';
import 'package:syncrow_app/services/api/devices_api.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
double curtainWidth = 270;
double curtainOpeningSpace = 195;
double blindHeight = 310;
double blindOpeningSpace = 245;
double openPercentage = 0;
bool isMoving = false;
final String curtainId;
CurtainBloc(
this.curtainId,
) : super(CurtainInitial()) {
on<InitCurtain>(_fetchStatus);
on<OpenCurtain>(_onOpenCurtain);
on<CloseCurtain>(_onCloseCurtain);
on<PauseCurtain>(_onPauseCurtain);
}
Future<void> _onOpenCurtain(
OpenCurtain event,
Emitter<CurtainState> emit) async {
isMoving = true;
while (openPercentage < 100.0) {
if (state is CurtainsClosing) {
_pauseCurtain(emit);
break;
}
emit(CurtainsOpening(
curtainWidth: curtainWidth,
blindHeight: blindHeight,
openPercentage: openPercentage,
));
if (isMoving) {
await Future.delayed(const Duration(milliseconds: 200), () async {
openPercentage += 10.0;
event.deviceType == DeviceType.Curtain
? curtainWidth -= curtainOpeningSpace / 10
: blindHeight -= blindOpeningSpace / 10;
if (openPercentage >= 100.0) {
_pauseCurtain(emit);
}
});
if (openPercentage >=100.0) {
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: curtainId,
code: 'control',
value: 'close',
),
curtainId,
);
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: curtainId,
code: 'percent_control',
value: 100,
),
curtainId,
);
}
} else {
_pauseCurtain(emit);
break;
}
}
}
Future<void> _onCloseCurtain(
CloseCurtain event, Emitter<CurtainState> emit) async {
isMoving = true;
while (openPercentage > 0.0) {
if (state is CurtainsOpening) {
_pauseCurtain(emit);
break;
}
emit(CurtainsClosing(
curtainWidth: curtainWidth,
blindHeight: blindHeight,
openPercentage: openPercentage,
));
if (isMoving) {
await Future.delayed(const Duration(milliseconds: 200), () async {
openPercentage -= 10.0;
event.deviceType == DeviceType.Curtain
? curtainWidth += curtainOpeningSpace / 10
: blindHeight += blindOpeningSpace / 10;
if (openPercentage <= 0.0) {
_pauseCurtain(emit);
}
});
if (openPercentage == 0.0) {
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: curtainId,
code: 'percent_control',
value: 0,
),
curtainId,
);
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: curtainId,
code: 'control',
value: 'open',
),
curtainId,
);
}
} else {
_pauseCurtain(emit);
break;
}
}
}
Future<void> _onPauseCurtain(
PauseCurtain event, Emitter<CurtainState> emit) async {
_pauseCurtain(emit);
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: curtainId,
code: 'control',
value: 'stop',
),
curtainId,
);
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: curtainId,
code: 'percent_control',
value: openPercentage.ceil(),
),
curtainId,
);
}
Future<void> _pauseCurtain(Emitter<CurtainState> emit) async {
isMoving = false;
emit(CurtainsPaused(
curtainWidth: curtainWidth,
blindHeight: blindHeight,
openPercentage: openPercentage,
));
}
void _fetchStatus(InitCurtain event, Emitter<CurtainState> emit) async {
try {
emit(CurtainLoadingState());
// Fetch the status from the API
var response = await DevicesAPI.getDeviceStatus(curtainId);
List<StatusModel> statusModelList = [];
for (var status in response['status']) {
statusModelList.add(StatusModel.fromJson(status));
}
// Get the open percentage from the response
openPercentage = double.tryParse(statusModelList[1].value.toString())!;
// Calculate curtain width and blind height based on the open percentage
if (openPercentage != null) {
curtainWidth = 270 - (openPercentage / 100) * curtainOpeningSpace;
blindHeight = 310 - (openPercentage / 100) * blindOpeningSpace;
}
emit(CurtainsOpening(
curtainWidth: curtainWidth,
blindHeight: blindHeight,
openPercentage: openPercentage,
));
} catch (e) {
emit(FailedState());
return;
}
}
}

View File

@ -0,0 +1,34 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
abstract class CurtainEvent extends Equatable {
const CurtainEvent();
@override
List<Object?> get props => [];
}
class OpenCurtain extends CurtainEvent {
final DeviceType deviceType;
const OpenCurtain(this.deviceType);
@override
List<Object?> get props => [deviceType];
}
class CloseCurtain extends CurtainEvent {
final DeviceType deviceType;
const CloseCurtain(this.deviceType);
@override
List<Object?> get props => [deviceType];
}
class InitCurtain extends CurtainEvent {}
class PauseCurtain extends CurtainEvent {}
class useCurtainEvent extends CurtainEvent {}

View File

@ -0,0 +1,76 @@
// curtain_state.dart
import 'package:equatable/equatable.dart';
abstract class CurtainState extends Equatable {
const CurtainState();
@override
List<Object?> get props => [];
}
class CurtainInitial extends CurtainState {}
class UpdateCurtain extends CurtainState {
final double curtainWidth;
final double blindHeight;
final double openPercentage;
const UpdateCurtain({
required this.curtainWidth,
required this.blindHeight,
required this.openPercentage,
});
@override
List<Object?> get props => [curtainWidth, blindHeight, openPercentage];
}
class FailedState extends CurtainState {}
class CurtainLoadingState extends CurtainState {}
class CurtainsOpening extends CurtainState {
final double curtainWidth;
final double blindHeight;
final double openPercentage;
const CurtainsOpening({
required this.curtainWidth,
required this.blindHeight,
required this.openPercentage,
});
@override
List<Object?> get props => [curtainWidth, blindHeight, openPercentage];
}
class CurtainsClosing extends CurtainState {
final double curtainWidth;
final double blindHeight;
final double openPercentage;
const CurtainsClosing({
required this.curtainWidth,
required this.blindHeight,
required this.openPercentage,
});
@override
List<Object?> get props => [curtainWidth, blindHeight, openPercentage];
}
class CurtainsPaused extends CurtainState {
final double curtainWidth;
final double blindHeight;
final double openPercentage;
const CurtainsPaused({
required this.curtainWidth,
required this.blindHeight,
required this.openPercentage,
});
@override
List<Object?> get props => [curtainWidth, blindHeight, openPercentage];
}

View File

@ -87,10 +87,10 @@ class DevicesCubit extends Cubit<DevicesState> {
return const DoorsListView(); return const DoorsListView();
case DeviceType.Curtain: case DeviceType.Curtain:
return const CurtainListView(); return const CurtainListView();
// case DeviceType.ThreeGang: // case DeviceType.ThreeGang:
// return const ThreeGangSwitchesView(); // return const ThreeGangSwitchesView();
// case DeviceType.Gateway: // case DeviceType.Gateway:
// return const GateWayView(); // return const GateWayView();
default: default:
return null; return null;
} }
@ -308,10 +308,10 @@ class DevicesCubit extends Cubit<DevicesState> {
emitSafe(GetDevicesLoading()); emitSafe(GetDevicesLoading());
int roomIndex = int roomIndex =
HomeCubit.getInstance().selectedSpace!.rooms!.indexWhere((element) => element.id == roomId); HomeCubit.getInstance().selectedSpace!.rooms!.indexWhere((element) => element.id == roomId);
try { try {
HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices = HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices =
await DevicesAPI.getDevicesByRoomId(roomId); await DevicesAPI.getDevicesByRoomId(roomId);
} catch (e) { } catch (e) {
emitSafe(GetDevicesError(e.toString())); emitSafe(GetDevicesError(e.toString()));
return; return;
@ -397,87 +397,7 @@ class DevicesCubit extends Cubit<DevicesState> {
// } // }
// } // }
//////////////////////////////////CURTAINS//////////////////////////////////////
double curtainWidth = 270;
double curtainOpeningSpace = 195;
double blindWindowHight = 310;
double blindOpeningSpace = 245;
double _openPercentage = 0;
bool isMoving = false;
openCurtain(DeviceType type) async {
if (state is CurtainsIsOpening) {
return;
}
isMoving = true;
while (_openPercentage < 100.0) {
if (state is CurtainsIsClosing) {
//listen to interruption by the closing process
pauseCurtain();
break;
}
emit(CurtainsIsOpening());
if (isMoving) {
await Future.delayed(const Duration(milliseconds: 200), () {
_openPercentage += 10.0;
//25.5 is the 10% of the curtain opening space, its used to update the
// animated container of thecurtain
type == DeviceType.Curtain
? curtainWidth -= curtainOpeningSpace / 10
: blindWindowHight -= blindOpeningSpace / 10;
if (_openPercentage >= 100.0) {
pauseCurtain();
}
});
} else {
pauseCurtain();
break;
}
}
}
closeCurtain(DeviceType type) async {
if (state is CurtainsIsClosing) {
return;
}
isMoving = true;
while (_openPercentage > 0.0) {
if (state is CurtainsIsOpening) {
// interrupted by the opening process
pauseCurtain();
break;
}
emit(CurtainsIsClosing());
if (isMoving) {
await Future.delayed(const Duration(milliseconds: 200), () {
_openPercentage -= 10.0;
//25.5 is the 10% of the curtain opening space, its used to update the
type == DeviceType.Curtain
? curtainWidth += curtainOpeningSpace / 10
: blindWindowHight += 24.5;
if (_openPercentage <= 0.0) {
pauseCurtain();
}
});
} else {
pauseCurtain();
break;
}
}
}
pauseCurtain() {
isMoving = false;
emit(CurtainsStopped());
}
} }
enum LightMode { enum LightMode {

View File

@ -1,15 +1,18 @@
import 'dart:math'; import 'dart:math';
import 'package:day_picker/model/day_in_week.dart'; import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart'; import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart'; import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/offline_password_model.dart';
import 'package:syncrow_app/features/devices/model/offline_temporary_password.dart';
import 'package:syncrow_app/features/devices/model/smart_door_model.dart'; import 'package:syncrow_app/features/devices/model/smart_door_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart'; import 'package:syncrow_app/features/devices/model/status_model.dart';
import 'package:syncrow_app/features/devices/model/create_temporary_password_model.dart'; import 'package:syncrow_app/features/devices/model/create_temporary_password_model.dart';
import 'package:syncrow_app/features/devices/model/temporary_password_model.dart'; import 'package:syncrow_app/features/devices/model/temporary_password_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/hour_picker_dialog.dart';
import 'package:syncrow_app/services/api/devices_api.dart'; import 'package:syncrow_app/services/api/devices_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart'; import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
@ -17,21 +20,78 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> { class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
final String deviceId; final String deviceId;
late SmartDoorModel deviceStatus; late SmartDoorModel deviceStatus;
static String pageType = '';
bool isSavingPassword = false; bool isSavingPassword = false;
SmartDoorBloc({required this.deviceId}) : super(InitialState()) { SmartDoorBloc({required this.deviceId}) : super(InitialState()) {
on<InitialEvent>(_fetchSmartDoorStatus); on<InitialEvent>(_fetchSmartDoorStatus);
on<DoorLockUpdated>(_doorLockUpdated);
on<InitialPasswordsPage>(getTemporaryPasswords); on<InitialPasswordsPage>(getTemporaryPasswords);
on<InitialOneTimePassword>(getOneTimePasswords);
on<InitialTimeLimitPassword>(getTimeLimitPasswords);
on<UpdateLockEvent>(_updateLock); on<UpdateLockEvent>(_updateLock);
on<SavePasswordEvent>(savePassword); on<SavePasswordEvent>(savePassword);
on<ToggleRepeatEvent>(toggleRepeat); on<ToggleRepeatEvent>(toggleRepeat);
on<SetStartEndTimeEvent>(setStartEndTime); on<SetStartEndTimeEvent>(setStartEndTime);
on<ChangeTimeEvent>(changeTime); on<ChangeTimeEvent>(changeTime);
on<GeneratePasswordEvent>(generate7DigitNumber); on<GeneratePasswordEvent>(generate7DigitNumber);
on<SelectTimeEvent>(selectTime); on<SelectTimeEvent>(selectTimeOfLinePassword);
on<SelectTimeOnlinePasswordEvent>(selectTimeOnlinePassword);
on<DeletePasswordEvent>(deletePassword); on<DeletePasswordEvent>(deletePassword);
on<GenerateAndSavePasswordTimeLimitEvent>(generateAndSavePasswordTimeLimited);
on<GenerateAndSavePasswordOneTimeEvent>(generateAndSavePasswordOneTime);
on<ToggleDaySelectionEvent>(toggleDaySelection);
on<RenamePasswordEvent>(_renamePassword);
} }
TextEditingController passwordController = TextEditingController();
TextEditingController passwordNameController = TextEditingController();
String effectiveTime = 'Select Time';
String passwordId = '';
int? effectiveTimeTimeStamp;
String expirationTime = 'Select Time';
int? expirationTimeTimeStamp;
bool repeat = false;
bool isStartEndTime = true;
DateTime? startTime;
DateTime? endTime;
int unlockRequest = 0;
List<TemporaryPassword>? temporaryPasswords = [];
List<OfflinePasswordModel>? oneTimePasswords = [];
List<OfflinePasswordModel>? timeLimitPasswords = [];
Future generate7DigitNumber(GeneratePasswordEvent event, Emitter<SmartDoorState> emit) async {
emit(LoadingInitialState());
passwordController.clear();
Random random = Random();
int min = 1000000;
int max = 9999999;
passwordController.text = (min + random.nextInt(max - min + 1)).toString();
emit(GeneratePasswordState());
return passwordController.text;
}
Future generateAndSavePasswordOneTime(
GenerateAndSavePasswordOneTimeEvent event, Emitter<SmartDoorState> emit) async {
try {
if (isSavingPassword) return;
isSavingPassword = true;
emit(LoadingInitialState());
var res = await DevicesAPI.generateOneTimePassword(deviceId: deviceId);
ApiResponse pass = ApiResponse.fromJson(res);
passwordController.text = pass.data.offlineTempPassword;
passwordId = pass.data.offlineTempPasswordId;
Future.delayed(const Duration(seconds: 1), () {
Clipboard.setData(ClipboardData(text: passwordController.text));
});
emit(const GeneratePasswordOneTimestate(generated: true));
} catch (_) {
emit(FailedState(errorMessage: _.toString()));
} finally {
isSavingPassword = false;
}
}
void _fetchSmartDoorStatus(InitialEvent event, Emitter<SmartDoorState> emit) async { void _fetchSmartDoorStatus(InitialEvent event, Emitter<SmartDoorState> emit) async {
try { try {
emit(LoadingInitialState()); emit(LoadingInitialState());
@ -42,6 +102,45 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
} }
deviceStatus = SmartDoorModel.fromJson(statusModelList); deviceStatus = SmartDoorModel.fromJson(statusModelList);
emit(UpdateState(smartDoorModel: deviceStatus)); emit(UpdateState(smartDoorModel: deviceStatus));
_listenToChanges();
} catch (e) {
emit(FailedState(errorMessage: e.toString()));
return;
}
}
_listenToChanges() {
try {
DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
List<StatusModel> statusList = [];
usersMap['status'].forEach((element) {
statusList.add(StatusModel(code: element['code'], value: element['value']));
});
deviceStatus = SmartDoorModel.fromJson(statusList);
add(DoorLockUpdated());
});
} catch (_) {}
}
_doorLockUpdated(DoorLockUpdated event, Emitter<SmartDoorState> emit) {
unlockRequest = deviceStatus.unlockRequest;
emit(UpdateState(smartDoorModel: deviceStatus));
}
void _renamePassword(RenamePasswordEvent event, Emitter<SmartDoorState> emit) async {
try {
emit(LoadingInitialState());
await DevicesAPI.renamePass(
name: passwordNameController.text, doorLockUuid: deviceId, passwordId: passwordId);
add(InitialOneTimePassword());
add(InitialTimeLimitPassword());
emit(UpdateState(smartDoorModel: deviceStatus));
} catch (e) { } catch (e) {
emit(FailedState(errorMessage: e.toString())); emit(FailedState(errorMessage: e.toString()));
return; return;
@ -51,15 +150,14 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
void getTemporaryPasswords(InitialPasswordsPage event, Emitter<SmartDoorState> emit) async { void getTemporaryPasswords(InitialPasswordsPage event, Emitter<SmartDoorState> emit) async {
try { try {
emit(LoadingInitialState()); emit(LoadingInitialState());
pageType = event.type!; var response = await DevicesAPI.getTemporaryPasswords(
var response = await DevicesAPI.getTemporaryPasswords(deviceId, pageType); deviceId,
);
if (response is List) { if (response is List) {
temporaryPasswords = response.map((item) => TemporaryPassword.fromJson(item)).toList(); temporaryPasswords = response.map((item) => TemporaryPassword.fromJson(item)).toList();
} else if (response is Map && response.containsKey('data')) { } else if (response is Map && response.containsKey('data')) {
temporaryPasswords = temporaryPasswords =
(response['data'] as List).map((item) => TemporaryPassword.fromJson(item)).toList(); (response['data'] as List).map((item) => TemporaryPassword.fromJson(item)).toList();
} else {
throw Exception("Unexpected response format");
} }
emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!)); emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!));
} catch (e) { } catch (e) {
@ -67,69 +165,125 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
} }
} }
TextEditingController passwordController = TextEditingController(); void getOneTimePasswords(InitialOneTimePassword event, Emitter<SmartDoorState> emit) async {
TextEditingController passwordNameController = TextEditingController(); try {
String effectiveTime = 'Select Time'; emit(LoadingInitialState());
int? effectiveTimeTimeStamp; var response = await DevicesAPI.getOneTimePasswords(deviceId);
String expirationTime = 'Select Time'; if (response is List) {
int? expirationTimeTimeStamp; oneTimePasswords = response.map((item) => OfflinePasswordModel.fromJson(item)).toList();
bool repeat = false; }
bool isStartEndTime = true; emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!));
List<String>? selectedDay; } catch (e) {
DateTime? startTime; emit(FailedState(errorMessage: e.toString()));
DateTime? endTime; }
List<TemporaryPassword>? temporaryPasswords = []; }
List<TemporaryPassword>? oneTimePasswords = [];
void getTimeLimitPasswords(InitialTimeLimitPassword event, Emitter<SmartDoorState> emit) async {
try {
emit(LoadingInitialState());
var response = await DevicesAPI.getTimeLimitPasswords(deviceId);
if (response is List) {
timeLimitPasswords = response.map((item) => OfflinePasswordModel.fromJson(item)).toList();
}
emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!));
} catch (e) {
emit(FailedState(errorMessage: e.toString()));
}
}
changeTime(ChangeTimeEvent event, Emitter<SmartDoorState> emit) { changeTime(ChangeTimeEvent event, Emitter<SmartDoorState> emit) {
emit(LoadingInitialState());
if (event.isStartEndTime == true) { if (event.isStartEndTime == true) {
startTime = event.val; startTime = event.val;
} else { } else {
endTime = event.val; endTime = event.val;
} }
emit(ChangeTimeState());
} }
bool toggleRepeat(ToggleRepeatEvent event, Emitter<SmartDoorState> emit) { bool toggleRepeat(ToggleRepeatEvent event, Emitter<SmartDoorState> emit) {
emit(LoadingInitialState()); emit(LoadingInitialState());
repeat = !repeat; repeat = !repeat;
emit(IsRepeatState()); emit(IsRepeatState(repeat: repeat));
return repeat; return repeat;
} }
bool setStartEndTime(SetStartEndTimeEvent event, Emitter<SmartDoorState> emit) { bool setStartEndTime(SetStartEndTimeEvent event, Emitter<SmartDoorState> emit) {
emit(LoadingInitialState()); emit(LoadingInitialState());
isStartEndTime = event.val; isStartEndTime = event.val;
emit(IsStartEndState()); emit(IsStartEndState(isStartEndTime: isStartEndTime));
return isStartEndTime; return isStartEndTime;
} }
void _updateLock(UpdateLockEvent event, Emitter<SmartDoorState> emit) async { void _updateLock(UpdateLockEvent event, Emitter<SmartDoorState> emit) async {
emit(LoadingNewSate(smartDoorModel: deviceStatus)); emit(LoadingNewSate(smartDoorModel: deviceStatus));
try { try {
final response = await DevicesAPI.controlDevice( // final response = await DevicesAPI.controlDevice(
DeviceControlModel(deviceId: deviceId, code: 'normal_open_switch', value: !event.value), // DeviceControlModel(deviceId: deviceId, code: 'normal_open_switch', value: !event.value),
deviceId); // deviceId);
if (response['success'] ?? false) { final response = await DevicesAPI.openDoorLock(deviceId);
if (response) {
deviceStatus.normalOpenSwitch = !event.value; deviceStatus.normalOpenSwitch = !event.value;
} }
} catch (_) {} } catch (_) {}
emit(UpdateState(smartDoorModel: deviceStatus)); emit(UpdateState(smartDoorModel: deviceStatus));
} }
void generate7DigitNumber(GeneratePasswordEvent event, Emitter<SmartDoorState> emit) async { Future<void> selectTimeOfLinePassword(SelectTimeEvent event, Emitter<SmartDoorState> emit) async {
emit(LoadingInitialState()); emit(ChangeTimeState());
passwordController.clear(); final DateTime? picked = await showDatePicker(
Random random = Random(); context: event.context,
int min = 1000000; initialDate: DateTime.now(),
int max = 9999999; firstDate: DateTime.now(),
passwordController.text = (min + random.nextInt(max - min + 1)).toString(); lastDate: DateTime(2101),
emit(GeneratePasswordState()); );
if (picked != null) {
final TimeOfDay? timePicked = await showHourPicker(
context: event.context,
initialTime: TimeOfDay.now(),
);
if (timePicked != null) {
final selectedDateTime = DateTime(
picked.year,
picked.month,
picked.day,
timePicked.hour,
0,
);
final selectedTimestamp = DateTime(
selectedDateTime.year,
selectedDateTime.month,
selectedDateTime.day,
selectedDateTime.hour,
selectedDateTime.minute,
).millisecondsSinceEpoch ~/
1000; // Divide by 1000 to remove milliseconds
if (event.isEffective) {
if (expirationTimeTimeStamp != null && selectedTimestamp > expirationTimeTimeStamp!) {
CustomSnackBar.displaySnackBar('Effective Time cannot be later than Expiration Time.');
} else {
effectiveTime =
selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds
effectiveTimeTimeStamp = selectedTimestamp;
}
} else {
if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) {
CustomSnackBar.displaySnackBar(
'Expiration Time cannot be earlier than Effective Time.');
} else {
expirationTime =
selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds
expirationTimeTimeStamp = selectedTimestamp;
}
}
emit(TimeSelectedState());
}
}
} }
Future<void> selectTime(SelectTimeEvent event, Emitter<SmartDoorState> emit) async { Future<void> selectTimeOnlinePassword(
SelectTimeOnlinePasswordEvent event, Emitter<SmartDoorState> emit) async {
emit(ChangeTimeState()); emit(ChangeTimeState());
final DateTime? picked = await showDatePicker( final DateTime? picked = await showDatePicker(
context: event.context, context: event.context,
@ -166,7 +320,6 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
timePicked.hour, timePicked.hour,
timePicked.minute, timePicked.minute,
); );
// Convert selectedDateTime to a timestamp without seconds and milliseconds
final selectedTimestamp = DateTime( final selectedTimestamp = DateTime(
selectedDateTime.year, selectedDateTime.year,
selectedDateTime.month, selectedDateTime.month,
@ -185,7 +338,8 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
} }
} else { } else {
if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) { if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) {
CustomSnackBar.displaySnackBar('Expiration Time cannot be earlier than Effective Time.'); CustomSnackBar.displaySnackBar(
'Expiration Time cannot be earlier than Effective Time.');
} else { } else {
expirationTime = expirationTime =
selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds
@ -202,8 +356,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
try { try {
isSavingPassword = true; isSavingPassword = true;
emit(LoadingSaveState()); emit(LoadingSaveState());
var res = await DevicesAPI.createPassword( await DevicesAPI.createPassword(
pageType: pageType,
deviceId: deviceId, deviceId: deviceId,
effectiveTime: effectiveTimeTimeStamp.toString(), effectiveTime: effectiveTimeTimeStamp.toString(),
invalidTime: expirationTimeTimeStamp.toString(), invalidTime: expirationTimeTimeStamp.toString(),
@ -214,24 +367,52 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
Schedule( Schedule(
effectiveTime: getTimeOnly(startTime), effectiveTime: getTimeOnly(startTime),
invalidTime: getTimeOnly(endTime).toString(), invalidTime: getTimeOnly(endTime).toString(),
workingDay: selectedDay!, workingDay: selectedDays,
), ),
], ],
); );
Navigator.of(event.context).pop(true); Navigator.of(event.context).pop(true);
CustomSnackBar.displaySnackBar('Save Successfully'); CustomSnackBar.displaySnackBar('Save Successfully');
emit(SaveState()); emit(SaveState());
} catch (_) {}finally { } catch (_) {
} finally {
isSavingPassword = false; isSavingPassword = false;
} }
} }
Future<void> generateAndSavePasswordTimeLimited(
GenerateAndSavePasswordTimeLimitEvent event, Emitter<SmartDoorState> emit) async {
if (timeLimitValidate() || isSavingPassword) return;
try {
isSavingPassword = true;
emit(LoadingInitialState());
var res = await DevicesAPI.generateMultiTimePassword(
deviceId: deviceId,
effectiveTime: effectiveTimeTimeStamp.toString(),
invalidTime: expirationTimeTimeStamp.toString(),
);
ApiResponse pass = ApiResponse.fromJson(res);
passwordController.text = pass.data.offlineTempPassword;
passwordId = pass.data.offlineTempPasswordId;
CustomSnackBar.displaySnackBar('Save Successfully');
add(InitialTimeLimitPassword());
Future.delayed(const Duration(seconds: 1), () {
Clipboard.setData(ClipboardData(text: passwordController.text));
});
emit(const GeneratePasswordOneTimestate(generated: true));
} catch (_) {
add(InitialPasswordsPage());
} finally {
isSavingPassword = false;
}
}
Future<void> deletePassword(DeletePasswordEvent event, Emitter<SmartDoorState> emit) async { Future<void> deletePassword(DeletePasswordEvent event, Emitter<SmartDoorState> emit) async {
try { try {
emit(LoadingInitialState()); emit(LoadingInitialState());
var response = await DevicesAPI.deletePassword(deviceId: deviceId, passwordId: event.passwordId)
await DevicesAPI.deletePassword(deviceId: deviceId, passwordId: event.passwordId) .then((value) async {
.then((value) async { add(InitialPasswordsPage());
add(InitialPasswordsPage(type: pageType));
}); });
} catch (e) { } catch (e) {
emit(FailedState(errorMessage: e.toString())); emit(FailedState(errorMessage: e.toString()));
@ -243,6 +424,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
CustomSnackBar.displaySnackBar('Password less than 7'); CustomSnackBar.displaySnackBar('Password less than 7');
return true; return true;
} }
if (passwordController.text.isEmpty) { if (passwordController.text.isEmpty) {
CustomSnackBar.displaySnackBar('Password required'); CustomSnackBar.displaySnackBar('Password required');
return true; return true;
@ -251,6 +433,25 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
CustomSnackBar.displaySnackBar('Password name required'); CustomSnackBar.displaySnackBar('Password name required');
return true; return true;
} }
if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) {
CustomSnackBar.displaySnackBar('Select effective time');
return true;
}
if (expirationTime == 'Select Time' || expirationTimeTimeStamp == null) {
CustomSnackBar.displaySnackBar('Select expiration time');
return true;
}
if (repeat == true && (endTime == null || startTime == null)) {
CustomSnackBar.displaySnackBar('Start Time and End time and the days required ');
return true;
}
return false;
}
bool timeLimitValidate() {
if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) { if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) {
CustomSnackBar.displaySnackBar('Select effective time'); CustomSnackBar.displaySnackBar('Select effective time');
return true; return true;
@ -259,23 +460,34 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
CustomSnackBar.displaySnackBar('Select expiration time'); CustomSnackBar.displaySnackBar('Select expiration time');
return true; return true;
} }
if (repeat == true && (endTime == null || startTime == null || selectedDay == null)) {
CustomSnackBar.displaySnackBar('Start Time and End time and the days required ');
return true;
}
return false; return false;
} }
List<DayInWeek> days = [ List<Map<String, String>> days = [
DayInWeek("S", dayKey: 'Sun'), {"day": "Sun", "key": "Sun"},
DayInWeek("M", dayKey: 'Mon'), {"day": "Mon", "key": "Mon"},
DayInWeek("T", dayKey: 'Tue'), {"day": "Tue", "key": "Tue"},
DayInWeek("W", dayKey: 'Wed'), {"day": "Wed", "key": "Wed"},
DayInWeek("T", dayKey: 'Thu'), {"day": "Thu", "key": "Thu"},
DayInWeek("F", dayKey: 'Fri'), {"day": "Fri", "key": "Fri"},
DayInWeek("S", dayKey: 'Sat'), {"day": "Sat", "key": "Sat"},
]; ];
List<String> selectedDays = [];
Future<void> toggleDaySelection(
ToggleDaySelectionEvent event,
Emitter<SmartDoorState> emit,
) async {
emit(LoadingInitialState());
if (selectedDays.contains(event.key)) {
selectedDays.remove(event.key);
} else {
selectedDays.add(event.key);
}
emit(ChangeTimeState());
}
String getTimeOnly(DateTime? dateTime) { String getTimeOnly(DateTime? dateTime) {
if (dateTime == null) return ''; if (dateTime == null) return '';
return DateFormat('HH:mm').format(dateTime); return DateFormat('HH:mm').format(dateTime);

View File

@ -9,12 +9,15 @@ abstract class SmartDoorEvent extends Equatable {
} }
class InitialEvent extends SmartDoorEvent {} class InitialEvent extends SmartDoorEvent {}
class InitialPasswordsPage extends SmartDoorEvent {
final String? type; class InitialPasswordsPage extends SmartDoorEvent {}
const InitialPasswordsPage({ this.type});
}
class InitialOneTimePassword extends SmartDoorEvent {} class InitialOneTimePassword extends SmartDoorEvent {}
class InitialTimeLimitPassword extends SmartDoorEvent {}
class DoorLockUpdated extends SmartDoorEvent {}
class UpdateLockEvent extends SmartDoorEvent { class UpdateLockEvent extends SmartDoorEvent {
final bool value; final bool value;
const UpdateLockEvent({required this.value}); const UpdateLockEvent({required this.value});
@ -29,39 +32,70 @@ class SavePasswordEvent extends SmartDoorEvent {
List<Object> get props => [context]; List<Object> get props => [context];
} }
class GeneratePasswordEvent extends SmartDoorEvent {} class GenerateAndSavePasswordTimeLimitEvent extends SmartDoorEvent {
class SelectTimeEvent extends SmartDoorEvent { final BuildContext context;
final BuildContext context; const GenerateAndSavePasswordTimeLimitEvent({required this.context});
final bool isEffective;
const SelectTimeEvent({required this.context,required this.isEffective});
@override @override
List<Object> get props => [context,isEffective]; List<Object> get props => [context];
}
class GenerateAndSavePasswordOneTimeEvent extends SmartDoorEvent {
final BuildContext context;
const GenerateAndSavePasswordOneTimeEvent({required this.context});
@override
List<Object> get props => [context];
}
class GeneratePasswordEvent extends SmartDoorEvent {}
class SelectTimeEvent extends SmartDoorEvent {
final BuildContext context;
final bool isEffective;
const SelectTimeEvent({required this.context, required this.isEffective});
@override
List<Object> get props => [context, isEffective];
}
class SelectTimeOnlinePasswordEvent extends SmartDoorEvent {
final BuildContext context;
final bool isEffective;
const SelectTimeOnlinePasswordEvent({required this.context, required this.isEffective});
@override
List<Object> get props => [context, isEffective];
} }
class ToggleRepeatEvent extends SmartDoorEvent {} class ToggleRepeatEvent extends SmartDoorEvent {}
class SetStartEndTimeEvent extends SmartDoorEvent { class SetStartEndTimeEvent extends SmartDoorEvent {
final bool val; final bool val;
const SetStartEndTimeEvent({required this.val}); const SetStartEndTimeEvent({required this.val});
@override @override
List<Object> get props => [val]; List<Object> get props => [val];
} }
class DeletePasswordEvent extends SmartDoorEvent { class DeletePasswordEvent extends SmartDoorEvent {
final String passwordId; final String passwordId;
const DeletePasswordEvent({required this.passwordId}); const DeletePasswordEvent({required this.passwordId});
@override @override
List<Object> get props => [passwordId]; List<Object> get props => [passwordId];
} }
class ToggleDaySelectionEvent extends SmartDoorEvent {
final String key;
const ToggleDaySelectionEvent({required this.key});
@override
List<Object> get props => [key];
}
class ChangeTimeEvent extends SmartDoorEvent { class ChangeTimeEvent extends SmartDoorEvent {
final dynamic val; final dynamic val;
final bool isStartEndTime; final bool isStartEndTime;
const ChangeTimeEvent({required this.val,required this.isStartEndTime}); const ChangeTimeEvent({required this.val, required this.isStartEndTime});
@override @override
List<Object> get props => [val,isStartEndTime]; List<Object> get props => [val, isStartEndTime];
} }
class RenamePasswordEvent extends SmartDoorEvent {}

View File

@ -38,13 +38,27 @@ class FailedState extends SmartDoorState {
List<Object> get props => [errorMessage]; List<Object> get props => [errorMessage];
} }
class GeneratePasswordState extends SmartDoorState {} class GeneratePasswordState extends SmartDoorState {
}
class TimeSelectedState extends SmartDoorState {} class TimeSelectedState extends SmartDoorState {}
class IsRepeatState extends SmartDoorState {} class IsRepeatState extends SmartDoorState {
final bool repeat;
const IsRepeatState({required this.repeat});
class IsStartEndState extends SmartDoorState {} @override
List<Object> get props => [repeat];
}
class IsStartEndState extends SmartDoorState {
final bool isStartEndTime;
const IsStartEndState({required this.isStartEndTime});
@override
List<Object> get props => [isStartEndTime];
}
class ChangeStartTimeState extends SmartDoorState {} class ChangeStartTimeState extends SmartDoorState {}
@ -56,6 +70,12 @@ class SaveState extends SmartDoorState {}
class LoadingSaveState extends SmartDoorState {} class LoadingSaveState extends SmartDoorState {}
class GeneratePasswordOneTimestate extends SmartDoorState {
final bool generated;
const GeneratePasswordOneTimestate({required this.generated});
List<Object> get props => [generated];
}
class TemporaryPasswordsLoadedState extends SmartDoorState { class TemporaryPasswordsLoadedState extends SmartDoorState {
final List<TemporaryPassword> temporaryPassword; final List<TemporaryPassword> temporaryPassword;
const TemporaryPasswordsLoadedState({required this.temporaryPassword}); const TemporaryPasswordsLoadedState({required this.temporaryPassword});

View File

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.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/bloc/home_cubit.dart';
import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_event.dart'; import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_event.dart';
@ -20,6 +21,10 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
secondCountDown: 0, secondCountDown: 0,
thirdCountDown: 0); thirdCountDown: 0);
Timer? _timer; Timer? _timer;
// Timer? _firstSwitchTimer;
// Timer? _secondSwitchTimer;
// Timer? _thirdSwitchTimer;
bool threeGangGroup = false; bool threeGangGroup = false;
List<DeviceModel> devicesList = []; List<DeviceModel> devicesList = [];
List<GroupThreeGangModel> groupThreeGangList = []; List<GroupThreeGangModel> groupThreeGangList = [];
@ -27,6 +32,7 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
ThreeGangBloc({required this.threeGangId}) : super(InitialState()) { ThreeGangBloc({required this.threeGangId}) : super(InitialState()) {
on<InitialEvent>(_fetchThreeGangStatus); on<InitialEvent>(_fetchThreeGangStatus);
on<ThreeGangUpdated>(_threeGangUpdated);
on<ChangeFirstSwitchStatusEvent>(_changeFirstSwitch); on<ChangeFirstSwitchStatusEvent>(_changeFirstSwitch);
on<ChangeSecondSwitchStatusEvent>(_changeSecondSwitch); on<ChangeSecondSwitchStatusEvent>(_changeSecondSwitch);
on<ChangeThirdSwitchStatusEvent>(_changeThirdSwitch); on<ChangeThirdSwitchStatusEvent>(_changeThirdSwitch);
@ -85,6 +91,7 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
} }
deviceStatus = ThreeGangModel.fromJson(statusModelList); deviceStatus = ThreeGangModel.fromJson(statusModelList);
emit(UpdateState(threeGangModel: deviceStatus)); emit(UpdateState(threeGangModel: deviceStatus));
_listenToChanges();
} }
} catch (e) { } catch (e) {
emit(FailedState(error: e.toString())); emit(FailedState(error: e.toString()));
@ -92,6 +99,34 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
} }
} }
_listenToChanges() {
try {
DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$threeGangId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) async {
if (_timer != null) {
await Future.delayed(const Duration(seconds: 2));
}
Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
List<StatusModel> statusList = [];
usersMap['status'].forEach((element) {
statusList.add(StatusModel(code: element['code'], value: element['value']));
});
deviceStatus = ThreeGangModel.fromJson(statusList);
if (!isClosed) {
add(ThreeGangUpdated());
}
});
} catch (_) {}
}
_threeGangUpdated(ThreeGangUpdated event, Emitter<ThreeGangState> emit) {
emit(UpdateState(threeGangModel: deviceStatus));
}
void _changeFirstSwitch(ChangeFirstSwitchStatusEvent event, Emitter<ThreeGangState> emit) async { void _changeFirstSwitch(ChangeFirstSwitchStatusEvent event, Emitter<ThreeGangState> emit) async {
emit(LoadingNewSate(threeGangModel: deviceStatus)); emit(LoadingNewSate(threeGangModel: deviceStatus));
try { try {
@ -111,16 +146,22 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
emit(UpdateState(threeGangModel: deviceStatus)); emit(UpdateState(threeGangModel: deviceStatus));
} }
final response = await DevicesAPI.controlDevice( if (_timer != null) {
DeviceControlModel( _timer!.cancel();
deviceId: threeGangGroup ? event.deviceId : threeGangId,
code: 'switch_1',
value: !event.value),
threeGangGroup ? event.deviceId : threeGangId);
if (!response['success']) {
add(InitialEvent(groupScreen: threeGangGroup));
} }
_timer = Timer(const Duration(milliseconds: 500), () async {
final response = await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: threeGangGroup ? event.deviceId : threeGangId,
code: 'switch_1',
value: !event.value),
threeGangGroup ? event.deviceId : threeGangId);
if (!response['success']) {
add(InitialEvent(groupScreen: threeGangGroup));
}
});
} catch (_) { } catch (_) {
add(InitialEvent(groupScreen: threeGangGroup)); add(InitialEvent(groupScreen: threeGangGroup));
} }
@ -146,16 +187,22 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
emit(UpdateState(threeGangModel: deviceStatus)); emit(UpdateState(threeGangModel: deviceStatus));
} }
final response = await DevicesAPI.controlDevice( if (_timer != null) {
DeviceControlModel( _timer!.cancel();
deviceId: threeGangGroup ? event.deviceId : threeGangId,
code: 'switch_2',
value: !event.value),
threeGangGroup ? event.deviceId : threeGangId);
if (!response['success']) {
add(InitialEvent(groupScreen: threeGangGroup));
} }
_timer = Timer(const Duration(milliseconds: 500), () async {
final response = await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: threeGangGroup ? event.deviceId : threeGangId,
code: 'switch_2',
value: !event.value),
threeGangGroup ? event.deviceId : threeGangId);
if (!response['success']) {
add(InitialEvent(groupScreen: threeGangGroup));
}
});
} catch (_) { } catch (_) {
add(InitialEvent(groupScreen: threeGangGroup)); add(InitialEvent(groupScreen: threeGangGroup));
} }
@ -180,16 +227,22 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
emit(UpdateState(threeGangModel: deviceStatus)); emit(UpdateState(threeGangModel: deviceStatus));
} }
final response = await DevicesAPI.controlDevice( if (_timer != null) {
DeviceControlModel( _timer!.cancel();
deviceId: threeGangGroup ? event.deviceId : threeGangId,
code: 'switch_3',
value: !event.value),
threeGangGroup ? event.deviceId : threeGangId);
if (!response['success']) {
add(InitialEvent(groupScreen: threeGangGroup));
} }
_timer = Timer(const Duration(milliseconds: 500), () async {
final response = await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: threeGangGroup ? event.deviceId : threeGangId,
code: 'switch_3',
value: !event.value),
threeGangGroup ? event.deviceId : threeGangId);
if (!response['success']) {
add(InitialEvent(groupScreen: threeGangGroup));
}
});
} catch (_) { } catch (_) {
add(InitialEvent(groupScreen: threeGangGroup)); add(InitialEvent(groupScreen: threeGangGroup));
} }
@ -407,6 +460,9 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
void _onClose(OnClose event, Emitter<ThreeGangState> emit) { void _onClose(OnClose event, Emitter<ThreeGangState> emit) {
_timer?.cancel(); _timer?.cancel();
// _firstSwitchTimer?.cancel();
// _secondSwitchTimer?.cancel();
// _thirdSwitchTimer?.cancel();
} }
void _onStartTimer(int seconds) { void _onStartTimer(int seconds) {

View File

@ -9,6 +9,8 @@ abstract class ThreeGangEvent extends Equatable {
class LoadingEvent extends ThreeGangEvent {} class LoadingEvent extends ThreeGangEvent {}
class ThreeGangUpdated extends ThreeGangEvent {}
class InitialEvent extends ThreeGangEvent { class InitialEvent extends ThreeGangEvent {
final bool groupScreen; final bool groupScreen;
const InitialEvent({required this.groupScreen}); const InitialEvent({required this.groupScreen});

View File

@ -1,3 +1,4 @@
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/wall_sensor_bloc/wall_sensor_state.dart'; import 'package:syncrow_app/features/devices/bloc/wall_sensor_bloc/wall_sensor_state.dart';
import 'package:syncrow_app/features/devices/bloc/wall_sensor_bloc/wall_sensor_event.dart'; import 'package:syncrow_app/features/devices/bloc/wall_sensor_bloc/wall_sensor_event.dart';
@ -16,6 +17,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
on<InitialEvent>(_fetchCeilingSensorStatus); on<InitialEvent>(_fetchCeilingSensorStatus);
on<ChangeIndicatorEvent>(_changeIndicator); on<ChangeIndicatorEvent>(_changeIndicator);
on<ChangeValueEvent>(_changeValue); on<ChangeValueEvent>(_changeValue);
on<WallSensorUpdatedEvent>(_wallSensorUpdated);
} }
void _fetchCeilingSensorStatus(InitialEvent event, Emitter<WallSensorState> emit) async { void _fetchCeilingSensorStatus(InitialEvent event, Emitter<WallSensorState> emit) async {
@ -28,12 +30,36 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
} }
deviceStatus = WallSensorModel.fromJson(statusModelList); deviceStatus = WallSensorModel.fromJson(statusModelList);
emit(UpdateState(wallSensorModel: deviceStatus)); emit(UpdateState(wallSensorModel: deviceStatus));
_listenToChanges();
} catch (e) { } catch (e) {
emit(FailedState(error: e.toString())); emit(FailedState(error: e.toString()));
return; return;
} }
} }
_listenToChanges() {
try {
DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
List<StatusModel> statusList = [];
usersMap['status'].forEach((element) {
statusList.add(StatusModel(code: element['code'], value: element['value']));
});
deviceStatus = WallSensorModel.fromJson(statusList);
add(WallSensorUpdatedEvent());
});
} catch (_) {}
}
_wallSensorUpdated(WallSensorUpdatedEvent event, Emitter<WallSensorState> emit) {
emit(UpdateState(wallSensorModel: deviceStatus));
}
void _changeIndicator(ChangeIndicatorEvent event, Emitter<WallSensorState> emit) async { void _changeIndicator(ChangeIndicatorEvent event, Emitter<WallSensorState> emit) async {
emit(LoadingNewSate(wallSensorModel: deviceStatus)); emit(LoadingNewSate(wallSensorModel: deviceStatus));
try { try {

View File

@ -11,6 +11,8 @@ class LoadingEvent extends WallSensorEvent {}
class InitialEvent extends WallSensorEvent {} class InitialEvent extends WallSensorEvent {}
class WallSensorUpdatedEvent extends WallSensorEvent {}
class ChangeIndicatorEvent extends WallSensorEvent { class ChangeIndicatorEvent extends WallSensorEvent {
final bool value; final bool value;
const ChangeIndicatorEvent({required this.value}); const ChangeIndicatorEvent({required this.value});

View File

@ -4,28 +4,47 @@ class CeilingSensorModel {
String presenceState; String presenceState;
int sensitivity; int sensitivity;
String checkingResult; String checkingResult;
int presenceRange;
int sportsPara;
String bodyMovement;
CeilingSensorModel({ CeilingSensorModel(
required this.presenceState, {required this.presenceState,
required this.sensitivity, required this.sensitivity,
required this.checkingResult, required this.checkingResult,
}); required this.presenceRange,
required this.sportsPara,
required this.bodyMovement});
factory CeilingSensorModel.fromJson(List<StatusModel> jsonList) { factory CeilingSensorModel.fromJson(List<StatusModel> jsonList) {
late String _presenceState; late String _presenceState;
late int _sensitivity; late int _sensitivity;
late String _checkingResult; late String _checkingResult;
int _presenceRange = 1;
int _sportsPara = 1;
String _bodyMovement = 'none';
for (int i = 0; i < jsonList.length; i++) { for (int i = 0; i < jsonList.length; i++) {
if (jsonList[i].code == 'presence_state') { if (jsonList[i].code == 'presence_state') {
_presenceState = jsonList[i].value ?? 'none'; _presenceState = jsonList[i].value ?? 'none';
} else if (jsonList[i].code == 'sensitivity') { } else if (jsonList[i].code == 'sensitivity') {
_sensitivity = jsonList[i].value ?? false; _sensitivity = jsonList[i].value ?? 1;
} else if (jsonList[i].code == 'checking_result') { } else if (jsonList[i].code == 'checking_result') {
_checkingResult = jsonList[i].value ?? false; _checkingResult = jsonList[i].value ?? '';
} else if (jsonList[i].code == 'presence_range') {
_presenceRange = jsonList[i].value ?? 0;
} else if (jsonList[i].code == 'sports_para') {
_sportsPara = jsonList[i].value ?? 0;
} else if (jsonList[i].code == 'body_movement') {
_bodyMovement = jsonList[i].value ?? '';
} }
} }
return CeilingSensorModel( return CeilingSensorModel(
presenceState: _presenceState, sensitivity: _sensitivity, checkingResult: _checkingResult); presenceState: _presenceState,
sensitivity: _sensitivity,
checkingResult: _checkingResult,
presenceRange: _presenceRange,
sportsPara: _sportsPara,
bodyMovement: _bodyMovement);
} }
} }

View File

@ -0,0 +1,63 @@
class OfflinePasswordModel {
final dynamic gmtCreate;
final dynamic gmtExpired;
final dynamic gmtStart;
final bool hasClearPwd;
final String optUid;
final String pwd;
final dynamic pwdId;
final String pwdName;
final String pwdTypeCode;
final String revokedPwdName;
final dynamic status;
OfflinePasswordModel({
required this.gmtCreate,
required this.gmtExpired,
required this.gmtStart,
required this.hasClearPwd,
required this.optUid,
required this.pwd,
required this.pwdId,
required this.pwdName,
required this.pwdTypeCode,
required this.revokedPwdName,
required this.status,
});
// Factory method to create a Password from a JSON map
factory OfflinePasswordModel.fromJson(Map<String, dynamic> json) {
return OfflinePasswordModel(
gmtCreate: json['gmtCreate'],
gmtExpired: json['gmtExpired'],
gmtStart: json['gmtStart'],
hasClearPwd: json['hasClearPwd'],
optUid: json['optUid'],
pwd: json['pwd'],
pwdId: json['pwdId'],
pwdName: json['pwdName'],
pwdTypeCode: json['pwdTypeCode'],
revokedPwdName: json['revokedPwdName'],
status: json['status'],
);
}
// Method to convert a Password object to a JSON map
Map<String, dynamic> toJson() {
return {
'gmtCreate': gmtCreate,
'gmtExpired': gmtExpired,
'gmtStart': gmtStart,
'hasClearPwd': hasClearPwd,
'optUid': optUid,
'pwd': pwd,
'pwdId': pwdId,
'pwdName': pwdName,
'pwdTypeCode': pwdTypeCode,
'revokedPwdName': revokedPwdName,
'status': status,
};
}
}

View File

@ -0,0 +1,69 @@
class OfflineTemporaryPassword {
dynamic effectiveTime;
dynamic invalidTime;
dynamic offlineTempPassword;
dynamic offlineTempPasswordId;
dynamic offlineTempPasswordName;
OfflineTemporaryPassword({
required this.effectiveTime,
required this.invalidTime,
required this.offlineTempPassword,
required this.offlineTempPasswordId,
required this.offlineTempPasswordName,
});
factory OfflineTemporaryPassword.fromJson(Map<String, dynamic> json) {
return OfflineTemporaryPassword(
effectiveTime: json['effective_time'],
invalidTime: json['invalid_time'],
offlineTempPassword: json['offline_temp_password'],
offlineTempPasswordId: json['offline_temp_password_id'],
offlineTempPasswordName: json['offline_temp_password_name'],
);
}
Map<String, dynamic> toJson() {
return {
'effective_time': effectiveTime,
'invalid_time': invalidTime,
'offline_temp_password': offlineTempPassword,
'offline_temp_password_id': offlineTempPasswordId,
'offline_temp_password_name': offlineTempPasswordName,
};
}
}
class ApiResponse {
int statusCode;
bool success;
String message;
OfflineTemporaryPassword data;
ApiResponse({
required this.statusCode,
required this.success,
required this.message,
required this.data,
});
factory ApiResponse.fromJson(Map<String, dynamic> json) {
return ApiResponse(
statusCode: json['statusCode'],
success: json['success'],
message: json['message'],
data: OfflineTemporaryPassword.fromJson(json['data']['result']),
);
}
Map<String, dynamic> toJson() {
return {
'statusCode': statusCode,
'success': success,
'message': message,
'data': {
'result': data.toJson(),
},
};
}
}

View File

@ -35,9 +35,9 @@ class WallSensorModel {
if (jsonList[i].code == 'presence_state') { if (jsonList[i].code == 'presence_state') {
_presenceState = jsonList[i].value ?? 'none'; _presenceState = jsonList[i].value ?? 'none';
} else if (jsonList[i].code == 'far_detection') { } else if (jsonList[i].code == 'far_detection') {
_farDetection = jsonList[i].value ?? false; _farDetection = jsonList[i].value ?? 0;
} else if (jsonList[i].code == 'presence_time') { } else if (jsonList[i].code == 'presence_time') {
_presenceTime = jsonList[i].value ?? false; _presenceTime = jsonList[i].value ?? 0;
} else if (jsonList[i].code == 'motion_sensitivity_value') { } else if (jsonList[i].code == 'motion_sensitivity_value') {
_motionSensitivity = jsonList[i].value ?? 0; _motionSensitivity = jsonList[i].value ?? 0;
} else if (jsonList[i].code == 'motionless_sensitivity') { } else if (jsonList[i].code == 'motionless_sensitivity') {
@ -47,7 +47,7 @@ class WallSensorModel {
} else if (jsonList[i].code == 'illuminance_value') { } else if (jsonList[i].code == 'illuminance_value') {
_illuminance = jsonList[i].value ?? 0; _illuminance = jsonList[i].value ?? 0;
} else if (jsonList[i].code == 'indicator') { } else if (jsonList[i].code == 'indicator') {
_indicator = jsonList[i].value ?? 0; _indicator = jsonList[i].value ?? false;
} }
} }
return WallSensorModel( return WallSensorModel(

View File

@ -34,8 +34,13 @@ class CeilingSensorInterface extends StatelessWidget {
create: (context) => create: (context) =>
CeilingSensorBloc(deviceId: ceilingSensor.uuid ?? '')..add(InitialEvent()), CeilingSensorBloc(deviceId: ceilingSensor.uuid ?? '')..add(InitialEvent()),
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(builder: (context, state) { child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(builder: (context, state) {
CeilingSensorModel ceilingSensorModel = CeilingSensorModel ceilingSensorModel = CeilingSensorModel(
CeilingSensorModel(presenceState: 'none', sensitivity: 1, checkingResult: ''); presenceState: 'none',
sensitivity: 1,
checkingResult: '',
presenceRange: 1,
sportsPara: 1,
bodyMovement: 'none');
if (state is UpdateState) { if (state is UpdateState) {
ceilingSensorModel = state.ceilingSensorModel; ceilingSensorModel = state.ceilingSensorModel;
} else if (state is LoadingNewSate) { } else if (state is LoadingNewSate) {
@ -178,7 +183,8 @@ class CeilingSensorInterface extends StatelessWidget {
children: [ children: [
const BodySmall(text: 'Sports Para'), const BodySmall(text: 'Sports Para'),
BodyLarge( BodyLarge(
text: '0', text: ceilingSensorModel.sportsPara
.toString(),
style: context.bodyLarge.copyWith( style: context.bodyLarge.copyWith(
fontWeight: FontsManager.bold, fontWeight: FontsManager.bold,
), ),
@ -204,7 +210,8 @@ class CeilingSensorInterface extends StatelessWidget {
textOverflow: TextOverflow.ellipsis, textOverflow: TextOverflow.ellipsis,
), ),
BodyLarge( BodyLarge(
text: '0.0M', text:
'${ceilingSensorModel.presenceRange}M',
textOverflow: TextOverflow.ellipsis, textOverflow: TextOverflow.ellipsis,
style: context.bodyLarge.copyWith( style: context.bodyLarge.copyWith(
fontWeight: FontsManager.bold, fontWeight: FontsManager.bold,
@ -231,7 +238,7 @@ class CeilingSensorInterface extends StatelessWidget {
textOverflow: TextOverflow.ellipsis, textOverflow: TextOverflow.ellipsis,
), ),
BodyLarge( BodyLarge(
text: 'none', text: ceilingSensorModel.bodyMovement,
textOverflow: TextOverflow.ellipsis, textOverflow: TextOverflow.ellipsis,
style: context.bodyLarge.copyWith( style: context.bodyLarge.copyWith(
fontWeight: FontsManager.bold, fontWeight: FontsManager.bold,

View File

@ -1,163 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
class BlindsView extends StatelessWidget {
const BlindsView({
super.key,
this.blind,
});
final DeviceModel? blind;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => DevicesCubit.getInstance(),
child: BlocBuilder<DevicesCubit, DevicesState>(
builder: (context, state) {
return DefaultScaffold(
title: blind?.name ?? 'Blinds',
child: Column(
children: [
Column(
children: [
Stack(
alignment: Alignment.topCenter,
children: [
Container(
height: 340,
width: 365,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
Assets.assetsImagesWindow,
),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 15, bottom: 10),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
height: DevicesCubit.get(context).blindWindowHight,
width: 270,
child: Stack(
children: List.generate(
25,
(index) {
double spacing = DevicesCubit.get(context)
.blindWindowHight /
24;
double topPosition = index * spacing;
return AnimatedPositioned(
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
top: topPosition,
child: SizedBox(
height: 10,
width: 270,
child: Image.asset(
Assets.assetsImagesHorizintalBlade,
fit: BoxFit.fill,
),
),
);
},
),
),
),
),
],
),
const SizedBox(height: 80),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
DecoratedBox(
decoration:
BoxDecoration(shape: BoxShape.circle, boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(3, 3),
),
]),
child: InkWell(
overlayColor:
MaterialStateProperty.all(Colors.transparent),
onTap: () {
// DevicesCubit.get(context).setHight(false);
DevicesCubit.get(context).openCurtain(
blind?.productType! ?? DeviceType.Blind);
},
child: Image.asset(
Assets.assetsImagesUp,
width: 60,
height: 60,
),
),
),
DecoratedBox(
decoration:
BoxDecoration(shape: BoxShape.circle, boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(3, 3),
),
]),
child: InkWell(
overlayColor:
MaterialStateProperty.all(Colors.transparent),
onTap: () {
DevicesCubit.get(context).pauseCurtain();
},
child: Image.asset(
Assets.assetsImagesPause,
width: 60,
height: 60,
),
),
),
DecoratedBox(
decoration:
BoxDecoration(shape: BoxShape.circle, boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(3, 3),
),
]),
child: InkWell(
overlayColor:
MaterialStateProperty.all(Colors.transparent),
onTap: () {
DevicesCubit.get(context).closeCurtain(
blind?.productType! ?? DeviceType.Blind);
},
child: Image.asset(
Assets.assetsImagesDown,
width: 60,
height: 60,
),
),
),
],
),
],
),
],
),
);
},
),
);
}
}

View File

@ -1,83 +0,0 @@
// import 'package:flutter/material.dart';
// import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart';
// import 'package:syncrow_app/generated2/assets.dart';
// class BlindsBottons extends StatelessWidget {
// const BlindsBottons({
// super.key,
// });
// @override
// Widget build(BuildContext context) {
// return Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// DecoratedBox(
// decoration: BoxDecoration(shape: BoxShape.circle, boxShadow: [
// BoxShadow(
// color: Colors.grey.withOpacity(0.5),
// spreadRadius: 1,
// blurRadius: 5,
// offset: const Offset(3, 3),
// ),
// ]),
// child: InkWell(
// overlayColor: MaterialStateProperty.all(Colors.transparent),
// onTap: () {
// // DevicesCubit.get(context).setHight(false);
// DevicesCubit.get(context).closeCurtain();
// },
// child: Image.asset(
// Assets.assetsImagesUp,
// width: 60,
// height: 60,
// ),
// ),
// ),
// DecoratedBox(
// decoration: BoxDecoration(shape: BoxShape.circle, boxShadow: [
// BoxShadow(
// color: Colors.grey.withOpacity(0.5),
// spreadRadius: 1,
// blurRadius: 5,
// offset: const Offset(3, 3),
// ),
// ]),
// child: InkWell(
// overlayColor: MaterialStateProperty.all(Colors.transparent),
// onTap: () {
// // DevicesCubit.get(context).setHight(false);
// },
// child: Image.asset(
// Assets.assetsImagesPause,
// width: 60,
// height: 60,
// ),
// ),
// ),
// DecoratedBox(
// decoration: BoxDecoration(shape: BoxShape.circle, boxShadow: [
// BoxShadow(
// color: Colors.grey.withOpacity(0.5),
// spreadRadius: 1,
// blurRadius: 5,
// offset: const Offset(3, 3),
// ),
// ]),
// child: InkWell(
// overlayColor: MaterialStateProperty.all(Colors.transparent),
// onTap: () {
// // DevicesCubit.get(context).setHight(true);
// DevicesCubit.get(context).openCurtain();
// },
// child: Image.asset(
// Assets.assetsImagesDown,
// width: 60,
// height: 60,
// ),
// ),
// ),
// ],
// );
// }
// }

View File

@ -1,114 +1,73 @@
part of "curtain_view.dart";
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/curtain_bloc/curtain_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_event.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/generated/assets.dart';
class CurtainButtons extends StatelessWidget { class CurtainButtons extends StatelessWidget {
const CurtainButtons({super.key, required this.curtain}); const CurtainButtons({super.key, required this.curtain});
final DeviceModel curtain; final DeviceModel curtain;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
Stack( _buildButton(
alignment: Alignment.center, onTap: () => context.read<CurtainBloc>().add(OpenCurtain(curtain.productType!)),
children: [ iconPath: Assets.assetsIconsCurtainsIconOpenCurtain,
DecoratedBox(
decoration: BoxDecoration(shape: BoxShape.circle, boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(3, 3),
),
]),
child: InkWell(
overlayColor: MaterialStateProperty.all(Colors.transparent),
onTap: () {
DevicesCubit.get(context).openCurtain(curtain.productType!);
},
child: const SizedBox.square(
dimension: 60,
),
),
),
Padding(
padding: const EdgeInsets.only(right: 5, bottom: 5),
child: InkWell(
overlayColor: MaterialStateProperty.all(Colors.transparent),
onTap: () {
DevicesCubit.get(context).openCurtain(curtain.productType!);
},
child: SvgPicture.asset(
Assets.assetsIconsCurtainsIconOpenCurtain,
width: 110,
height: 110,
),
),
)
],
), ),
_buildButton(
onTap: () => context.read<CurtainBloc>().add(PauseCurtain()),
iconPath: Assets.assetsImagesPause,
isSvg: false,
),
_buildButton(
onTap: () => context.read<CurtainBloc>().add(CloseCurtain(curtain.productType!)),
iconPath: Assets.assetsIconsCurtainsIconCloseCurtain,
),
],
);
}
Widget _buildButton({
required VoidCallback onTap,
required String iconPath,
bool isSvg = true,
}) {
return Stack(
alignment: Alignment.center,
children: [
DecoratedBox( DecoratedBox(
decoration: BoxDecoration(shape: BoxShape.circle, boxShadow: [ decoration: BoxDecoration(
BoxShadow( shape: BoxShape.circle,
color: Colors.grey.withOpacity(0.5), boxShadow: [
spreadRadius: 1, BoxShadow(
blurRadius: 5, color: Colors.grey.withOpacity(0.5),
offset: const Offset(3, 3), spreadRadius: 1,
), blurRadius: 5,
]), offset: const Offset(3, 3),
),
],
),
child: InkWell( child: InkWell(
overlayColor: MaterialStateProperty.all(Colors.transparent), overlayColor: MaterialStateProperty.all(Colors.transparent),
onTap: () { onTap: onTap,
DevicesCubit.get(context).pauseCurtain(); child: const SizedBox.square(dimension: 60),
},
child: Image.asset(
Assets.assetsImagesPause,
width: 60,
height: 60,
),
), ),
), ),
Stack( Padding(
alignment: Alignment.center, padding: const EdgeInsets.only(right: 5, bottom: 5),
children: [ child: InkWell(
DecoratedBox( overlayColor: MaterialStateProperty.all(Colors.transparent),
decoration: BoxDecoration(shape: BoxShape.circle, boxShadow: [ onTap: onTap,
BoxShadow( child: isSvg
color: Colors.grey.withOpacity(0.5), ? SvgPicture.asset(iconPath, width: 110, height: 110)
spreadRadius: 1, : Image.asset(iconPath, width: 60, height: 60),
blurRadius: 5, ),
offset: const Offset(3, 3),
),
]),
child: const SizedBox.square(
dimension: 60,
),
// InkWell(
// overlayColor: MaterialStateProperty.all(Colors.transparent),
// onTap: () {
// DevicesCubit.get(context).closeCurtain(curtain.productType!);
// },
// child: SvgPicture.asset(
// Assets.assetsIconsCurtainsIconCloseCurtain,
// width: 60,
// height: 60,
// ),
// ),
),
Padding(
padding: const EdgeInsets.only(right: 5, bottom: 5),
child: InkWell(
overlayColor: MaterialStateProperty.all(Colors.transparent),
onTap: () {
DevicesCubit.get(context).closeCurtain(curtain.productType!);
},
child: SvgPicture.asset(
Assets.assetsIconsCurtainsIconCloseCurtain,
width: 110,
height: 110,
),
),
)
],
), ),
], ],
); );

View File

@ -1,86 +1,99 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_event.dart';
import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_buttons.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/generated/assets.dart';
part "curtain_buttons.dart";
class CurtainView extends StatelessWidget { class CurtainView extends StatelessWidget {
const CurtainView({super.key, required this.curtain}); const CurtainView({super.key, required this.curtain});
final DeviceModel curtain; final DeviceModel curtain;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => DevicesCubit.getInstance(), create: (context) => CurtainBloc(curtain.uuid!)..add(InitCurtain()),
child: BlocBuilder<DevicesCubit, DevicesState>( child: BlocBuilder<CurtainBloc, CurtainState>(
builder: (context, state) => DefaultScaffold( builder: (context, state) {
title: curtain.name, double curtainWidth = 270;
child: Column( double blindHeight = 310;
children: [ if (state is CurtainsOpening) {
Stack( curtainWidth = state.curtainWidth;
alignment: Alignment.centerLeft, blindHeight = state.blindHeight;
children: [ } else if (state is CurtainsClosing) {
Container( curtainWidth = state.curtainWidth;
height: 340, blindHeight = state.blindHeight;
width: 365, } else if (state is CurtainsPaused) {
decoration: const BoxDecoration( curtainWidth = state.curtainWidth;
image: DecorationImage( blindHeight = state.blindHeight;
image: AssetImage( }
Assets.assetsImagesWindow, return DefaultScaffold(
title: curtain.name,
child: Column(
children: [
Stack(
alignment: Alignment.centerLeft,
children: [
Container(
height: 340,
width: 365,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
Assets.assetsImagesWindow,
),
), ),
), ),
), ),
), Padding(
Padding( padding: const EdgeInsets.all(40),
padding: const EdgeInsets.all(40), child: AnimatedContainer(
child: AnimatedContainer( duration: const Duration(milliseconds: 200),
duration: const Duration(milliseconds: 200), curve: Curves.linear,
curve: Curves.linear, height: 310,
height: 310, width: curtainWidth,
width: DevicesCubit.getInstance().curtainWidth, child: Stack(
child: Stack( children: List.generate(
children: List.generate( 10, (index) {
10, double spacing = curtainWidth / 9;
(index) { double leftMostPosition = index * spacing;
double spacing = return AnimatedPositioned(
DevicesCubit.getInstance().curtainWidth / 9; duration: const Duration(milliseconds: 200),
double leftMostPosition = index * spacing; curve: Curves.linear,
return AnimatedPositioned( left: leftMostPosition,
duration: const Duration(milliseconds: 200), child: SizedBox(
curve: Curves.linear, height: 320,
left: leftMostPosition, width: 32,
child: SizedBox( child: SvgPicture.asset(
height: 320, Assets.assetsIconsCurtainsIconVerticalBlade,
width: 32, fit: BoxFit.fill,
child: SvgPicture.asset( ),
Assets.assetsIconsCurtainsIconVerticalBlade,
fit: BoxFit.fill,
), ),
), );
); },
}, ),
), ),
), ),
), ),
), Positioned(
Positioned(
top: 27, top: 27,
left: 43, left: 43,
child: SvgPicture.asset( child: SvgPicture.asset(
Assets.assetsIconsCurtainsIconCurtainHolder)), Assets.assetsIconsCurtainsIconCurtainHolder,
], ),
), ),
const SizedBox(height: 80), ],
CurtainButtons( ),
curtain: curtain, const SizedBox(height: 80),
), CurtainButtons(curtain: curtain),
], ],
), ),
), );
},
), ),
); );
} }

View File

@ -66,6 +66,7 @@ class DevicesViewBody extends StatelessWidget {
const SizedBox( const SizedBox(
height: 10, height: 10,
), ),
Expanded( Expanded(
child: PageView( child: PageView(
controller: HomeCubit.getInstance().devicesPageController, controller: HomeCubit.getInstance().devicesPageController,

View File

@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
class HourPickerDialog extends StatefulWidget {
final TimeOfDay initialTime;
HourPickerDialog({required this.initialTime});
@override
_HourPickerDialogState createState() => _HourPickerDialogState();
}
class _HourPickerDialogState extends State<HourPickerDialog> {
late int _selectedHour;
bool _isPm = false;
@override
void initState() {
super.initState();
_selectedHour = widget.initialTime.hour > 12 ? widget.initialTime.hour - 12 : widget.initialTime.hour;
_isPm = widget.initialTime.period == DayPeriod.pm;
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Select Hour'),
content: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton<int>(
value: _selectedHour,
items: List.generate(12, (index) {
int displayHour = index + 1;
return DropdownMenuItem(
value: displayHour,
child: Text(displayHour.toString()),
);
}),
onChanged: (value) {
setState(() {
_selectedHour = value!;
});
},
),
SizedBox(width: 16.0),
DropdownButton<bool>(
value: _isPm,
items: [
DropdownMenuItem(
value: false,
child: Text('AM'),
),
DropdownMenuItem(
value: true,
child: Text('PM'),
),
],
onChanged: (value) {
setState(() {
_isPm = value!;
});
},
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(null),
child: Text('Cancel'),
),
TextButton(
onPressed: () {
int hour = _isPm ? _selectedHour + 12 : _selectedHour;
Navigator.of(context).pop(TimeOfDay(hour: hour, minute: 0));
},
child: Text('OK'),
),
],
);
}
}
Future<TimeOfDay?> showHourPicker({
required BuildContext context,
required TimeOfDay initialTime,
}) {
return showDialog<TimeOfDay>(
context: context,
builder: (context) => HourPickerDialog(initialTime: initialTime),
);
}

View File

@ -0,0 +1,116 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class NameTimeWidget extends StatelessWidget {
const NameTimeWidget({super.key});
@override
Widget build(BuildContext context) {
return DefaultContainer(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(10.0),
child: const BodyMedium(
text: 'Password Name',
fontWeight: FontWeight.normal,
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 2.6,
child: TextFormField(
controller: BlocProvider.of<SmartDoorBloc>(context)
.passwordNameController,
decoration: const InputDecoration(
hintText: 'Enter The Name',
hintStyle: TextStyle(
fontSize: 14, color: ColorsManager.textGray)),
)),
],
),
Column(
children: [
const Divider(
color: ColorsManager.graysColor,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Expanded(
child: BodyMedium(
text: 'Effective Time',
fontWeight: FontWeight.normal,
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 3.5,
child: InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context).add(SelectTimeOnlinePasswordEvent(context: context, isEffective: true));
},
child: Text(
BlocProvider.of<SmartDoorBloc>(context).effectiveTime,
style: TextStyle(fontSize: 14,
color: BlocProvider.of<SmartDoorBloc>(context).effectiveTime ==
'Select Time'
? ColorsManager.textGray
: null),
),
)),
],),
),
const Divider(
color: ColorsManager.graysColor,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Expanded(
child: BodyMedium(
text: 'Expiration Time',
fontWeight: FontWeight.normal,
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 3.5,
child: InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context).add(
SelectTimeOnlinePasswordEvent(
context: context, isEffective: false));
},
child: Text(
BlocProvider.of<SmartDoorBloc>(context).expirationTime,
style: TextStyle(
fontSize: 14,
color: BlocProvider.of<SmartDoorBloc>(context).expirationTime == 'Select Time'
? ColorsManager.textGray
: null),
),
),
),
],
),
),
],
),
],
));
}
}

View File

@ -0,0 +1,224 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/repeat_widget.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/door_lock_button.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/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
class OfflineOneTimePasswordPage extends StatelessWidget {
final String? deviceId;
final String? type;
const OfflineOneTimePasswordPage({super.key, this.deviceId, this.type});
@override
Widget build(BuildContext context) {
bool isRepeat = false;
bool generated = false;
return BlocProvider(
create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!),
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(listener: (context, state) {
if (state is FailedState) {
CustomSnackBar.displaySnackBar(
state.errorMessage
);
}
if (state is IsRepeatState){
isRepeat = state.repeat;
}
if (state is GeneratePasswordOneTimestate ){
generated = state.generated;
}
}, builder: (context, state) {
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
return DefaultScaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
title: const BodyLarge(
text: 'Create One-Time Password',
fontColor: ColorsManager.primaryColor,
fontWeight: FontsManager.bold,
),
leading: IconButton(
onPressed: () {
Navigator.of(context).pop(true);
},
icon: const Icon(Icons.arrow_back)
),
),
child: state is LoadingInitialState
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const BodyMedium(
text: 'Save the password immediately. The password is not displayed in the app.',
fontWeight: FontWeight.normal,
fontColor: ColorsManager.grayColor,
),
const SizedBox(
height: 20,
),
const BodyMedium(
text: '7-Digit Password',
fontWeight: FontWeight.normal,
fontColor: ColorsManager.grayColor,
),
DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children:smartDoorBloc.passwordController.text.isEmpty?
List.generate(10, (index) {
return const Padding(
padding: EdgeInsets.symmetric(horizontal: 4.0,vertical: 15),
child: Icon(
Icons.circle,
size: 20.0,
color: Colors.black,
),
);
}) :[
Expanded(
child: Row(
children: [
Expanded(
child: BodyLarge(
style: const TextStyle(
color: ColorsManager.primaryColor,
fontWeight: FontWeight.bold,
letterSpacing: 8.0 ,
fontSize: 25,
wordSpacing: 2),
textAlign: TextAlign.center,
text: smartDoorBloc.passwordController.text,
fontSize: 23,
),),
IconButton(
onPressed: () async {
await Clipboard.setData(ClipboardData(
text: smartDoorBloc.passwordController.text));
},
icon: const Icon(Icons.copy)
),
],
),
),
],
)),
const SizedBox(
width: 10,
),
],
),
if(smartDoorBloc.passwordController.text.isNotEmpty)
Column(
children: [
const Divider(
color: ColorsManager.graysColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(10.0),
child: const BodyMedium(
text: 'Password Name',
fontWeight: FontWeight.normal,
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 2.6,
child: TextFormField(
controller: BlocProvider.of<SmartDoorBloc>(context).passwordNameController,
decoration: const InputDecoration(
hintText: 'Enter The Name',
hintStyle: TextStyle(
fontSize: 14, color: ColorsManager.textGray)),
)),
],
),
],
),
],
),
),
const SizedBox(
height: 20,
),
const BodyMedium(
textAlign: TextAlign.center,
text: 'Save the password immediately. The password is not displayed in the app.',
fontWeight: FontWeight.normal,
fontColor: ColorsManager.grayColor,
),
// NameTimeWidget(type:type!),
const SizedBox(
height: 20,
),
Center(
child: SizedBox(
width: MediaQuery.of(context).size.width/1.5,
child: DoorLockButton(
isDone: generated,
isLoading: smartDoorBloc.isSavingPassword ,
borderRadius: 30,
backgroundColor:ColorsManager.primaryColor ,
onPressed: () async {
if(generated==false){
smartDoorBloc.add(GenerateAndSavePasswordOneTimeEvent(context: context));
}else{
if(smartDoorBloc.passwordNameController.text.isNotEmpty){
smartDoorBloc.add(RenamePasswordEvent());
}
Navigator.of(context).pop(true);
}
},
child: const BodyMedium(
text: 'Obtain Password',
fontWeight: FontWeight.bold,
fontColor: Colors.white,
),),
),
),
const SizedBox(
height: 20,
),
isRepeat? const RepeatWidget():const SizedBox(),
const SizedBox(
height: 40,
)
],
),
),
);
}));
}
}

View File

@ -8,6 +8,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/devices_cubit.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/model/device_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.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/gateway/gateway_view.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/lights/light_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/wall_sensor/wall_sensor_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/wall_sensor/wall_sensor_interface.dart';
@ -102,6 +103,11 @@ void showDeviceInterface(DeviceModel device, BuildContext context) {
// navigateToInterface(CeilingSensorInterface(ceilingSensor: device), context); // navigateToInterface(CeilingSensorInterface(ceilingSensor: device), context);
break; break;
case DeviceType.Curtain: case DeviceType.Curtain:
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) =>
CurtainView(curtain: device,)));
break; break;
case DeviceType.Blind: case DeviceType.Blind:
break; break;

View File

@ -1,4 +1,3 @@
import 'package:day_picker/day_picker.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -7,6 +6,8 @@ import 'package:pin_code_fields/pin_code_fields.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart'; import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart'; import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart'; import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart';
import 'package:syncrow_app/features/devices/view/widgets/name_time_widget.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/repeat_widget.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.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/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_large.dart';
@ -14,15 +15,15 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dar
import 'package:syncrow_app/utils/helpers/snack_bar.dart'; import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
import 'package:time_picker_spinner/time_picker_spinner.dart';
class CreateTemporaryPassword extends StatelessWidget { class CreateTemporaryPassword extends StatelessWidget {
final String? deviceId; final String? deviceId;
final String? type; final String? type;
const CreateTemporaryPassword({super.key, this.deviceId, this.type}); const CreateTemporaryPassword({super.key, this.deviceId, this.type});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isRepeat = false;
bool generated = false;
return BlocProvider( return BlocProvider(
create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!), create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!),
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(listener: (context, state) { child: BlocConsumer<SmartDoorBloc, SmartDoorState>(listener: (context, state) {
@ -34,7 +35,14 @@ class CreateTemporaryPassword extends StatelessWidget {
), ),
); );
} }
if (state is IsRepeatState){
isRepeat = state.repeat;
}
if (state is GeneratePasswordOneTimestate ){
generated = state.generated;
}
}, builder: (context, state) { }, builder: (context, state) {
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
return DefaultScaffold( return DefaultScaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@ -46,360 +54,143 @@ class CreateTemporaryPassword extends StatelessWidget {
), ),
leading: IconButton( leading: IconButton(
onPressed: () { onPressed: () {
Navigator.of(context).pop('UpdatePage'); Navigator.of(context).pop(true);
}, },
icon: const Icon(Icons.arrow_back) icon: const Icon(Icons.arrow_back)
), ),
actions: [ actions:
type == 'Online Password'?[
TextButton( TextButton(
onPressed: () { onPressed: () {
BlocProvider.of<SmartDoorBloc>(context) smartDoorBloc.add(SavePasswordEvent(context: context));
.add(SavePasswordEvent(context: context));
}, },
child: const Text('Save') child: const Text('Save')
) )
], ]:null,
), ),
child: state is LoadingInitialState child: state is LoadingInitialState
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: SingleChildScrollView( : SingleChildScrollView(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const BodyMedium( const BodyMedium(
text: 'Save the password immediately. The password is not displayed in the app.', text: 'Save the password immediately. The password is not displayed in the app.',
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
fontColor: ColorsManager.grayColor, fontColor: ColorsManager.grayColor,
), ),
const SizedBox( const SizedBox(
height: 20, height: 20,
), ),
const BodyMedium( const BodyMedium(
text: '7-Digit Password', text: '7-Digit Password',
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
fontColor: ColorsManager.grayColor, fontColor: ColorsManager.grayColor,
), ),
DefaultContainer( DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
child: Row( child: Padding(
mainAxisAlignment: MainAxisAlignment.spaceBetween, padding: EdgeInsets.symmetric(horizontal: type == 'Online Password'?0:25),
mainAxisSize: MainAxisSize.max, child: Row(
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Flexible( mainAxisSize: MainAxisSize.max,
flex: 2, children: [
child: PinCodeTextField( Flexible(
onCompleted: (value) { flex: 2,
if (value.split('').every((char) => char == '1')) { child: PinCodeTextField(
BlocProvider.of<SmartDoorBloc>(context).passwordController.clear(); onCompleted: (value) {
CustomSnackBar.displaySnackBar('All characters cannot be 1.'); if (value.split('').every((char) => char == '1')) {
} smartDoorBloc.passwordController.clear();
}, CustomSnackBar.displaySnackBar('All characters cannot be 1.');
autoDisposeControllers: false, }
keyboardType: TextInputType.phone,
length: 7,
enabled: true,
obscureText: false,
animationType: AnimationType.fade,
pinTheme: PinTheme(
shape: PinCodeFieldShape.underline,
fieldHeight: 45,
fieldWidth: 20,
activeFillColor: Colors.white,
disabledColor: Colors.grey,
activeColor: Colors.grey,
errorBorderColor: Colors.grey,
inactiveColor: Colors.grey,
inactiveFillColor: Colors.grey,
selectedColor: Colors.grey),
animationDuration: const Duration(milliseconds: 300),
backgroundColor: Colors.white,
enableActiveFill: false,
controller: BlocProvider.of<SmartDoorBloc>(context).passwordController,
appContext: context,
)),
const SizedBox(
width: 10,
),
Flexible(
child: InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context)
.add(GeneratePasswordEvent());
},
child: const BodyMedium(
text: 'Generate Randomly',
fontWeight: FontWeight.bold,
fontColor: ColorsManager.primaryColor,
)),
)
],
),
),
BlocProvider.of<SmartDoorBloc>(context).passwordController.text.isNotEmpty
? TextButton(
onPressed: () async {
await Clipboard.setData(ClipboardData(
text: BlocProvider.of<SmartDoorBloc>(context)
.passwordController
.text));
}, },
child: const Text('Copy')) autoDisposeControllers: false,
: const SizedBox(), keyboardType: TextInputType.phone,
const SizedBox( length: 7,
height: 20, // enabled:type == 'Online Password'? true:false,
), obscureText: false,
DefaultContainer( animationType: AnimationType.fade,
padding: const EdgeInsets.all(20), pinTheme: PinTheme(
child: Column( shape: PinCodeFieldShape.underline,
children: [ fieldHeight: 45,
Row( fieldWidth: 20,
mainAxisAlignment: MainAxisAlignment.spaceBetween, activeFillColor: Colors.white,
children: [ disabledColor: Colors.grey,
Expanded( activeColor: Colors.grey,
child: Container( errorBorderColor: Colors.grey,
padding: const EdgeInsets.all(10.0), inactiveColor: Colors.grey,
child: const BodyMedium( inactiveFillColor: Colors.grey,
text: 'Password Name', selectedColor: Colors.grey),
fontWeight: FontWeight.normal, animationDuration: const Duration(milliseconds: 300),
), backgroundColor: Colors.white,
), enableActiveFill: false,
), controller: smartDoorBloc.passwordController,
SizedBox( appContext: context,
width: MediaQuery.of(context).size.width / 2.6, )),
child: TextFormField( const SizedBox(
controller: BlocProvider.of<SmartDoorBloc>(context) width: 10,
.passwordNameController,
decoration: const InputDecoration(
hintText: 'Enter The Name',
hintStyle: TextStyle(
fontSize: 14, color: ColorsManager.textGray)),
)),
],
),
const Divider(
color: ColorsManager.graysColor,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Expanded(
child: BodyMedium(
text: 'Effective Time',
fontWeight: FontWeight.normal,
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 3.5,
child: InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context).add(
SelectTimeEvent(
context: context, isEffective: true));
},
child: Text(
BlocProvider.of<SmartDoorBloc>(context).effectiveTime,
style: TextStyle(
fontSize: 14,
color: BlocProvider.of<SmartDoorBloc>(context)
.effectiveTime ==
'Select Time'
? ColorsManager.textGray
: null),
),
)),
],
),
),
const Divider(
color: ColorsManager.graysColor,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Expanded(
child: BodyMedium(
text: 'Expiration Time',
fontWeight: FontWeight.normal,
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 3.5,
child: InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context).add(
SelectTimeEvent(
context: context, isEffective: false));
},
child: Text(
BlocProvider.of<SmartDoorBloc>(context).expirationTime,
style: TextStyle(
fontSize: 14,
color: BlocProvider.of<SmartDoorBloc>(context)
.expirationTime ==
'Select Time'
? ColorsManager.textGray
: null),
),
),
),
],
),
),
],
)),
const SizedBox(
height: 20,
),
if (type == 'Online Password')
DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: const BodyMedium(
text: 'Repeat',
fontWeight: FontWeight.normal,
),
trailing: Transform.scale(
scale: .8,
child: CupertinoSwitch(
value: BlocProvider.of<SmartDoorBloc>(context).repeat,
onChanged: (value) {
BlocProvider.of<SmartDoorBloc>(context)
.add(ToggleRepeatEvent());
},
applyTheme: true,
)),
),
), ),
const SizedBox( if(type == 'Online Password')
height: 20, Flexible(
), child: InkWell(
BlocProvider.of<SmartDoorBloc>(context).repeat onTap: () {
? DefaultContainer( smartDoorBloc.add(GeneratePasswordEvent());
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), },
child: Column( child: const BodyMedium(
children: <Widget>[ text: 'Generate Randomly',
Padding( fontWeight: FontWeight.bold,
padding: const EdgeInsets.all(8.0), fontColor: ColorsManager.primaryColor,
child: Row( )),
mainAxisAlignment: MainAxisAlignment.spaceBetween, )
children: [ ],
InkWell( ),
onTap: () {
BlocProvider.of<SmartDoorBloc>(context)
.add(const SetStartEndTimeEvent(val: true));
},
child: BodyMedium(
text: 'Start',
fontColor: BlocProvider.of<SmartDoorBloc>(context)
.isStartEndTime ==
false
? Colors.black
: Colors.blue,
fontSize: 18,
),
),
InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context)
.add(const SetStartEndTimeEvent(val: false));
},
child: BodyMedium(
text: 'End',
fontColor: BlocProvider.of<SmartDoorBloc>(context)
.isStartEndTime
? Colors.black
: Colors.blue,
fontSize: 18,
),
)
],
),
),
const Divider(
color: ColorsManager.graysColor,
),
Container(
height: 110,
child: BlocProvider.of<SmartDoorBloc>(context).isStartEndTime
? TimePickerSpinner(
time:
BlocProvider.of<SmartDoorBloc>(context).startTime,
is24HourMode: false,
itemHeight: 40,
normalTextStyle: const TextStyle(
color: Colors.grey,
fontSize: 24,
),
highlightedTextStyle:
const TextStyle(fontSize: 30, color: Colors.blue),
onTimeChange: (time) {
BlocProvider.of<SmartDoorBloc>(context).add(
ChangeTimeEvent(
val: time,
isStartEndTime:
BlocProvider.of<SmartDoorBloc>(context)
.isStartEndTime));
},
)
: Container(
child: TimePickerSpinner(
time:
BlocProvider.of<SmartDoorBloc>(context).endTime,
is24HourMode: false,
itemHeight: 40,
normalTextStyle: const TextStyle(
color: Colors.grey,
fontSize: 24,
),
highlightedTextStyle: const TextStyle(
fontSize: 30, color: Colors.blue),
onTimeChange: (time) {
BlocProvider.of<SmartDoorBloc>(context).add(
ChangeTimeEvent(
val: time,
isStartEndTime:
BlocProvider.of<SmartDoorBloc>(
context)
.isStartEndTime));
},
),
),
),
const Divider(
color: ColorsManager.graysColor,
),
const SizedBox(height: 20),
SelectWeekDays(
width: MediaQuery.of(context).size.width / 1,
fontSize: 18,
fontWeight: FontWeight.w600,
days: BlocProvider.of<SmartDoorBloc>(context).days,
border: false,
selectedDayTextColor: Colors.black,
unSelectedDayTextColor: Colors.grey,
boxDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
color: Colors.white),
onSelect: (values) {
BlocProvider.of<SmartDoorBloc>(context).selectedDay =
values;
},
),
],
))
: const SizedBox(),
const SizedBox(
height: 40,
)
],
), ),
), ),
if(smartDoorBloc.passwordController.text.isNotEmpty)
TextButton(
onPressed: () async {
await Clipboard.setData(ClipboardData(
text: smartDoorBloc.passwordController.text));
},
child: const Text('Copy')
),
const SizedBox(
height: 20,
),
NameTimeWidget(),
const SizedBox(
height: 20,
),
DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: const BodyMedium(
text: 'Repeat',
fontWeight: FontWeight.normal,
),
trailing: Transform.scale(
scale: .8,
child: CupertinoSwitch(
value: smartDoorBloc.repeat,
onChanged: (value) {
smartDoorBloc.add(ToggleRepeatEvent());
},
applyTheme: true,
)),
),
) ,
const SizedBox(
height: 20,
),
isRepeat? const RepeatWidget():const SizedBox(),
const SizedBox(
height: 40,
)
],
),
),
); );
})); }));
} }

View File

@ -1,14 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
// import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_event.dart';
// import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart'; import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart'; import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.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_model.dart';
import 'package:syncrow_app/features/devices/model/smart_door_model.dart'; import 'package:syncrow_app/features/devices/model/smart_door_model.dart';
// import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_status_bar.dart';
import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
@ -35,40 +31,36 @@ class _DoorLockButtonState extends State<DoorLockButton> with SingleTickerProvid
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_animationController = AnimationController( _animationController = AnimationController(
vsync: this, vsync: this,
duration: const Duration(seconds: 2), value: context.read<SmartDoorBloc>().unlockRequest > 0 ? 1 : 0,
duration: Duration(seconds: context.read<SmartDoorBloc>().unlockRequest),
); );
_animation = Tween<double>(begin: 0, end: 1.0).animate(_animationController) if (context.read<SmartDoorBloc>().unlockRequest > 0) {
_animationController.reverse();
}
_animation = Tween<double>(begin: 0, end: 1).animate(_animationController)
..addListener(() { ..addListener(() {
if (_animation.status == AnimationStatus.completed) {
// if (widget.doorLock.status
// .firstWhere((element) => element.code == 'normal_open_switch')
// .value !=
// true) {
// DevicesCubit.getInstance().deviceControl(
// DeviceControlModel(
// deviceId: widget.doorLock.uuid, code: 'normal_open_switch', value: true),
// widget.doorLock.uuid ?? "");
// }
BlocProvider.of<SmartDoorBloc>(context)
.add(UpdateLockEvent(value: smartDoorModel.normalOpenSwitch));
_animationController.reverse();
} else if (_animation.status == AnimationStatus.dismissed) {
// if (widget.doorLock.status
// .firstWhere((element) => element.code == 'normal_open_switch')
// .value !=
// false) {
// DevicesCubit.getInstance().deviceControl(
// DeviceControlModel(
// deviceId: widget.doorLock.uuid, code: 'normal_open_switch', value: false),
// widget.doorLock.uuid ?? "");
// }
}
setState(() {}); setState(() {});
}); });
} }
@override
void didUpdateWidget(DoorLockButton oldWidget) {
super.didUpdateWidget(oldWidget);
if (_animationController.status == AnimationStatus.dismissed) {
if (context.read<SmartDoorBloc>().unlockRequest > 0) {
_animationController.value = 1;
_animationController.duration =
Duration(seconds: context.read<SmartDoorBloc>().unlockRequest);
_animationController.reverse();
}
}
}
@override @override
void dispose() { void dispose() {
_animationController.dispose(); _animationController.dispose();
@ -88,14 +80,18 @@ class _DoorLockButtonState extends State<DoorLockButton> with SingleTickerProvid
WidgetStateProperty.all(ColorsManager.primaryColorWithOpacity.withOpacity(0.1)), WidgetStateProperty.all(ColorsManager.primaryColorWithOpacity.withOpacity(0.1)),
borderRadius: BorderRadius.circular(999), borderRadius: BorderRadius.circular(999),
onTapDown: (details) { onTapDown: (details) {
if (_animationController.status == AnimationStatus.dismissed) { // if (_animationController.status == AnimationStatus.dismissed) {
_animationController.forward(); // _animationController.forward();
} else if (_animationController.status == AnimationStatus.completed) { // } else if (_animationController.status == AnimationStatus.completed) {
_animationController.reverse(); // _animationController.reverse();
} else if (_animationController.status == AnimationStatus.forward) { // } else if (_animationController.status == AnimationStatus.forward) {
_animationController.reverse(); // _animationController.reverse();
} else if (_animationController.status == AnimationStatus.reverse) { // } else if (_animationController.status == AnimationStatus.reverse) {
_animationController.forward(); // _animationController.forward();
// }
if (context.read<SmartDoorBloc>().unlockRequest > 0) {
BlocProvider.of<SmartDoorBloc>(context)
.add(UpdateLockEvent(value: smartDoorModel.normalOpenSwitch));
} }
}, },
onTapUp: (details) { onTapUp: (details) {

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:syncrow_app/features/devices/model/offline_password_model.dart';
import 'package:syncrow_app/features/devices/model/temporary_password_model.dart'; import 'package:syncrow_app/features/devices/model/temporary_password_model.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/context_extension.dart';
@ -7,13 +8,15 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
class DoorDialog extends StatefulWidget { class DoorDialog extends StatefulWidget {
final String title; final String? title;
final TemporaryPassword value; final TemporaryPassword? temporaryPassword;
final OfflinePasswordModel? offline;
const DoorDialog({ const DoorDialog({
super.key, super.key,
required this.title, this.title,
required this.value, this.offline,
this.temporaryPassword,
}); });
@override @override
@ -28,16 +31,18 @@ class DoorDialogState extends State<DoorDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final effectiveTime = widget.temporaryPassword?.effectiveTime ??int.parse( widget.offline?.gmtStart);
final invalidTime = widget.temporaryPassword?.invalidTime ?? int.parse(widget.offline?.gmtExpired);
final DateTime effectiveDateTime = final DateTime effectiveDateTime =
DateTime.fromMillisecondsSinceEpoch(widget.value.effectiveTime * 1000, isUtc: false); DateTime.fromMillisecondsSinceEpoch(effectiveTime! * 1000, isUtc: false);
String formattedDateEffectiveTime = DateFormat('yyyy-MM-dd').format(effectiveDateTime); String formattedDateEffectiveTime = DateFormat('yyyy-MM-dd').format(effectiveDateTime);
String formattedTimeEffectiveTime = DateFormat('HH:mm:ss').format(effectiveDateTime); String formattedTimeEffectiveTime = DateFormat('hh:mm a').format(effectiveDateTime);
final DateTime expiredDateTime = final DateTime expiredDateTime =
DateTime.fromMillisecondsSinceEpoch(widget.value.invalidTime * 1000, isUtc: false); DateTime.fromMillisecondsSinceEpoch(invalidTime! * 1000, isUtc: false);
String formattedDateExpiredDateTime = DateFormat('yyyy-MM-dd').format(expiredDateTime); String formattedDateExpiredDateTime = DateFormat('yyyy-MM-dd').format(expiredDateTime);
String formattedTimeExpiredDateTime = DateFormat('HH:mm:ss').format(expiredDateTime); String formattedTimeExpiredDateTime = DateFormat('hh:mm a').format(expiredDateTime);
return Dialog( return Dialog(
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -49,7 +54,7 @@ class DoorDialogState extends State<DoorDialog> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
BodyMedium( BodyMedium(
text: widget.title, text: widget.title ?? '',
style: context.bodyMedium.copyWith( style: context.bodyMedium.copyWith(
color: ColorsManager.primaryColorWithOpacity, color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontsManager.extraBold, fontWeight: FontsManager.extraBold,
@ -111,6 +116,7 @@ class DoorDialogState extends State<DoorDialog> {
width: double.infinity, width: double.infinity,
color: ColorsManager.greyColor, color: ColorsManager.greyColor,
), ),
widget.temporaryPassword?.effectiveTime!=null?
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
@ -142,6 +148,27 @@ class DoorDialogState extends State<DoorDialog> {
), ),
), ),
], ],
):
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
height: 50,
child: InkWell(
onTap: () {
Navigator.pop(context);
},
child: Center(
child: BodyMedium(
text: 'Cancel',
style: context.bodyMedium.copyWith(color: ColorsManager.greyColor),
),
),
),
),
],
) )
], ],
), ),

View File

@ -7,27 +7,22 @@ import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_sta
import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/smart_door_model.dart'; import 'package:syncrow_app/features/devices/model/smart_door_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart'; import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart';
import 'package:syncrow_app/features/devices/view/widgets/popup_menu_widget.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_button.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_button.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_grid.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_grid.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_status_bar.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_status_bar.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart';
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
class DoorInterface extends StatelessWidget { class DoorInterface extends StatelessWidget {
const DoorInterface({super.key, required this.doorLock});
DoorInterface({super.key, required this.doorLock});
final DeviceModel doorLock; final DeviceModel doorLock;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => SmartDoorBloc(deviceId: doorLock.uuid ?? '')..add(InitialEvent()), create: (context) => SmartDoorBloc(deviceId: doorLock.uuid ?? '')..add(InitialEvent()),
child: BlocConsumer<SmartDoorBloc, SmartDoorState>( child: BlocConsumer<SmartDoorBloc, SmartDoorState>(listener: (context, state) {
listener: (context, state) {
if (state is FailedState) { if (state is FailedState) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
@ -63,32 +58,32 @@ class DoorInterface extends StatelessWidget {
} }
return AnnotatedRegion( return AnnotatedRegion(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
statusBarIconBrightness: Brightness.light, statusBarIconBrightness: Brightness.light,
),
child: Scaffold(
backgroundColor: ColorsManager.backgroundColor,
extendBodyBehindAppBar: true,
extendBody: true,
appBar: DeviceAppbar(
deviceName: doorLock.name!,
deviceUuid: doorLock.uuid!,
), ),
body: Container( child: Scaffold(
width: MediaQuery.sizeOf(context).width, backgroundColor: ColorsManager.backgroundColor,
height: MediaQuery.sizeOf(context).height, extendBodyBehindAppBar: true,
decoration: const BoxDecoration( extendBody: true,
image: DecorationImage( appBar: DeviceAppbar(
image: AssetImage( deviceName: doorLock.name!,
Assets.assetsImagesBackground, deviceUuid: doorLock.uuid!,
),
fit: BoxFit.cover,
opacity: 0.4,
),
), ),
child: SafeArea( body: Container(
child: Padding( width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
Assets.assetsImagesBackground,
),
fit: BoxFit.cover,
opacity: 0.4,
),
),
child: SafeArea(
child: Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
top: Constants.appBarHeight, top: Constants.appBarHeight,
left: Constants.defaultPadding, left: Constants.defaultPadding,
@ -98,38 +93,35 @@ class DoorInterface extends StatelessWidget {
? const Center( ? const Center(
child: RefreshProgressIndicator(), child: RefreshProgressIndicator(),
) )
: : RefreshIndicator(
onRefresh: () async {
RefreshIndicator( BlocProvider.of<SmartDoorBloc>(context).add(InitialEvent());
onRefresh: () async { },
BlocProvider.of<SmartDoorBloc>(context).add(InitialEvent()); child: ListView(
}, children: [
child: DoorLockStatusBar(
ListView( smartDoorModel: smartDoorModel,
children: [ ),
DoorLockStatusBar( Column(
smartDoorModel: smartDoorModel, mainAxisAlignment: MainAxisAlignment.center,
), crossAxisAlignment: CrossAxisAlignment.stretch,
Column( children: [
mainAxisAlignment: MainAxisAlignment.center, DoorLockButton(
crossAxisAlignment: CrossAxisAlignment.stretch, doorLock: doorLock,
children: [ smartDoorModel: smartDoorModel,
DoorLockButton( ),
doorLock: doorLock, DoorLockGrid(
smartDoorModel: smartDoorModel, uuid: doorLock.uuid!,
), ),
DoorLockGrid( uuid: doorLock.uuid!, ), ],
)
], ],
) )),
], ),
)), ),
), ),
), ));
),
));
}), }),
); );
} }
} }

View File

@ -0,0 +1,288 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/repeat_widget.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/door_lock_button.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/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
class CreateOfflineTimeLimitPasswordPage extends StatelessWidget {
final String? deviceId;
final String? type;
const CreateOfflineTimeLimitPasswordPage({super.key, this.deviceId, this.type});
@override
Widget build(BuildContext context) {
bool isRepeat = false;
bool generated = false;
return BlocProvider(
create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!),
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(
listener: (context, state) {
if (state is FailedState) {
CustomSnackBar.displaySnackBar(
state.errorMessage
);
}
if (state is IsRepeatState) {
isRepeat = state.repeat;
}
if (state is GeneratePasswordOneTimestate) {
generated = state.generated;
}
}, builder: (context, state) {
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
return DefaultScaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
title: const BodyLarge(
text: 'Create Time-Limited Password',
fontColor: ColorsManager.primaryColor,
fontWeight: FontsManager.bold,
),
leading: IconButton(
onPressed: () {
Navigator.of(context).pop(true);
},
icon: const Icon(Icons.arrow_back)),
),
child: state is LoadingInitialState
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const BodyMedium(
text:
'Save the password immediately. The password is not displayed in the app.',
fontWeight: FontWeight.normal,
fontColor: ColorsManager.grayColor,
),
const SizedBox(
height: 20,
),
DefaultContainer(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 15),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: smartDoorBloc.passwordController.text.isEmpty ?
List.generate(10, (index) {
return const Padding(
padding: EdgeInsets.symmetric(
horizontal: 4.0,
vertical: 15),
child: Icon(
Icons.circle,
size: 20.0,
color: Colors.black,
),
);
}) : [
Expanded(
child: Row(
children: [
Expanded(
child: BodyLarge(
style: const TextStyle(
color: ColorsManager.primaryColor,
fontWeight: FontWeight.bold,
letterSpacing: 8.0,
fontSize: 25,
wordSpacing: 2),
textAlign: TextAlign.center,
text: smartDoorBloc.passwordController.text,
fontSize: 25,
),
),
IconButton(
onPressed: () async {
await Clipboard.setData(
ClipboardData(text: smartDoorBloc.passwordController.text)
);
},
icon: const Icon(Icons.copy)),
],
),
),
],
)),
const SizedBox(
width: 10,
),
],
),
DefaultContainer(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(10.0),
child: const BodyMedium(
text: 'Password Name',
fontWeight: FontWeight.normal,
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 2.6,
child: TextFormField(
controller: BlocProvider.of<SmartDoorBloc>(context).passwordNameController,
decoration:
const InputDecoration(
hintText: 'Enter The Name',
hintStyle: TextStyle(
fontSize: 14,
color: ColorsManager.textGray)
),
)),
],
),
Column(
children: [
const Divider(color: ColorsManager.graysColor,),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Expanded(
child: BodyMedium(
text: 'Effective Time',
fontWeight: FontWeight.normal,
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 3.5,
child: InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context).add(SelectTimeEvent(context: context, isEffective: true));
},
child: Text(
BlocProvider.of<SmartDoorBloc>(context).effectiveTime,
style: TextStyle(
fontSize: 14,
color: BlocProvider.of<SmartDoorBloc>(context).effectiveTime ==
'Select Time' ? ColorsManager.textGray : null),
),
)),],
),
),
const Divider(
color: ColorsManager.graysColor,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
const Expanded(
child: BodyMedium(
text: 'Expiration Time',
fontWeight: FontWeight.normal,
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 3.5,
child: InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context).add(SelectTimeEvent(
context: context,
isEffective: false));
},
child: Text(
BlocProvider.of<SmartDoorBloc>(context).expirationTime,
style: TextStyle(
fontSize: 14,
color: BlocProvider.of<SmartDoorBloc>(context)
.expirationTime == 'Select Time' ? ColorsManager
.textGray : null),
),
),
),
],
),
),
],
),
],
)),
],
),
),
const SizedBox(
height: 20,
),
const BodyMedium(
textAlign: TextAlign.center,
text: 'Use the time-limited password at least once within 24 hours after the password takes effect. Otherwise, the password becomes invalid.',
fontWeight: FontWeight.normal,
fontColor: ColorsManager.grayColor,
),
// NameTimeWidget(type:type!),
const SizedBox(
height: 20,
),
Center(
child: SizedBox(
width: MediaQuery.of(context).size.width / 1.5,
child: DoorLockButton(
isDone: generated,
isLoading: smartDoorBloc.isSavingPassword,
borderRadius: 30,
backgroundColor: ColorsManager.primaryColor,
onPressed: () async {
if (generated == false) {
smartDoorBloc.add(GenerateAndSavePasswordTimeLimitEvent(context: context));
} else {
if(smartDoorBloc.passwordNameController.text.isNotEmpty){
smartDoorBloc.add(RenamePasswordEvent());
}
Navigator.of(context).pop(true);
}
},
child: const BodyMedium(
text: 'Obtain Password',
fontWeight: FontWeight.bold,
fontColor: Colors.white,
),
),
),
),
const SizedBox(
height: 20,
),
isRepeat ? const RepeatWidget() : const SizedBox(),
const SizedBox(
height: 40,
)
],
),
),
);
}));
}
}

View File

@ -0,0 +1,111 @@
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/smart_door_bloc/smart_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart';
import 'package:syncrow_app/features/devices/view/widgets/offline_one_time_password_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_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/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class OnetimePasswordPage extends StatelessWidget {
final String? deviceId;
const OnetimePasswordPage({super.key, this.deviceId,});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!)..add(InitialOneTimePassword( )),
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(
listener: (context, state) {
if (state is FailedState) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.errorMessage),
backgroundColor: Colors.red,
),
);
}
},
builder: (context, state) {
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
return DefaultScaffold(
title: 'Passwords',
actions: [
IconButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => OfflineOneTimePasswordPage(deviceId: deviceId, )
)).then((result) {
if(result!=null){
smartDoorBloc.add(InitialOneTimePassword());
smartDoorBloc.add(InitialOneTimePassword());
}
});
},
icon: const Icon(Icons.add)
)
],
child: Builder(
builder: (context) {
return state is LoadingInitialState
? const Center(child: CircularProgressIndicator())
: Center(
child: smartDoorBloc.oneTimePasswords!.isNotEmpty
? ListView.builder(
itemCount: smartDoorBloc.oneTimePasswords!.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: SvgPicture.asset(Assets.timeLimitedPasswordIcon),
title: BodyMedium(
text: 'Password Name: ${smartDoorBloc.oneTimePasswords![index].pwdName}',
fontWeight: FontWeight.normal,
),
onTap: () async {
final result = await showDialog(
context: context,
builder: (context) {
return DoorDialog(
title: 'Password Information',
offline: smartDoorBloc.oneTimePasswords![index],
);
},
);
},
trailing: const Icon(
Icons.arrow_forward_ios,
color: ColorsManager.greyColor,
size: 15,
),
),
),
);
},
) : Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(Assets.noValidPasswords),
const SizedBox(
height: 10,
),
const BodyMedium(text: 'No Valid Passwords')
],
),
);
},
));
})
);
}
}

View File

@ -0,0 +1,125 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class RepeatWidget extends StatelessWidget {
const RepeatWidget({
super.key,
});
@override
Widget build(BuildContext context) {
return BlocBuilder<SmartDoorBloc, SmartDoorState>(
builder: (context, state) {
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
return DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: Column(children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
smartDoorBloc.add(const SetStartEndTimeEvent(val: true));
},
child: BodyMedium(
text: 'Start',
fontColor: smartDoorBloc.isStartEndTime == false
? Colors.black
: Colors.blue,
fontSize: 18,
),
),
InkWell(
onTap: () {
smartDoorBloc.add(const SetStartEndTimeEvent(val: false));
},
child: BodyMedium(
text: 'End',
fontColor: smartDoorBloc.isStartEndTime
? Colors.black
: Colors.blue,
fontSize: 18,
),
)
],
),
),
const Divider(
color: ColorsManager.graysColor,
),
smartDoorBloc.isStartEndTime
? Container(
height: 110,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.time,
initialDateTime:smartDoorBloc.startTime,
onDateTimeChanged: (startTime) {
smartDoorBloc.add(ChangeTimeEvent(val: startTime, isStartEndTime: true));
},
)
):SizedBox(
height: 110,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.time,
initialDateTime:smartDoorBloc.endTime,
onDateTimeChanged: (endTime) {
smartDoorBloc.add(ChangeTimeEvent(val: endTime, isStartEndTime: false));
},
)
),
const Divider(
color: ColorsManager.graysColor,
),
const SizedBox(height: 20),
SizedBox(
height: MediaQuery.of(context).size.height *0.10,
child: ListView(
scrollDirection: Axis.horizontal,
children: smartDoorBloc.days.map((day) {
bool isSelected = smartDoorBloc.selectedDays.contains(day['key']);
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
smartDoorBloc.add(ToggleDaySelectionEvent(key:day['key']!));
},
child: Container(
width: 70,
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8),
decoration: BoxDecoration(
border: Border.all(
color: isSelected?
Colors.black:ColorsManager.grayColor
),
color: Colors.transparent,
borderRadius: BorderRadius.circular(55),
),
child: Center(
child: Text(
day['day']!,
style: TextStyle(
fontSize: 18,
color: isSelected ? Colors.black : ColorsManager.grayColor,
),
),
),
),
),
);
}).toList(),
)
)
]));
});
}
}

View File

@ -1,6 +1,8 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/onetime_password_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/timelimited_password_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/view_temporary_password.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/view_temporary_password.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.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/default_scaffold.dart';
@ -10,7 +12,7 @@ import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class TemporaryPasswordPage extends StatelessWidget { class TemporaryPasswordPage extends StatelessWidget {
final String? deviceId; final String? deviceId;
const TemporaryPasswordPage({super.key,this.deviceId}); const TemporaryPasswordPage({super.key,this.deviceId});
@override @override
@ -40,13 +42,17 @@ class TemporaryPasswordPage extends StatelessWidget {
child:ListTile( child:ListTile(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
leading: SvgPicture.asset( leading: SvgPicture.asset(
Assets.timeLimitedPasswordIcon), Assets.timeLimitedPasswordIcon),
title: const BodyMedium( title: const BodyMedium(
text: 'Time-Limited Password', text: 'Time-Limited Password',
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
onTap: () { onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'Online Password'),)); Navigator.of(context).push(MaterialPageRoute(builder: (context) =>
ViewTemporaryPassword(
deviceId:deviceId,
type:'Online Password'),
));
}, },
trailing: const Icon( trailing: const Icon(
Icons.arrow_forward_ios, Icons.arrow_forward_ios,
@ -57,7 +63,6 @@ class TemporaryPasswordPage extends StatelessWidget {
), ),
], ],
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
Column( Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
@ -75,46 +80,45 @@ class TemporaryPasswordPage extends StatelessWidget {
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
DefaultContainer( DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20), padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),
child: Column(children: [ child: Column(
ListTile( children: [
contentPadding: EdgeInsets.zero, ListTile(
leading: SvgPicture.asset(Assets.oneTimePassword), contentPadding: EdgeInsets.zero,
title: leading: SvgPicture.asset(Assets.oneTimePassword),
const BodyMedium( title:
text: 'One-Time Password', const BodyMedium(
fontWeight: FontWeight.normal, text: 'One-Time Password',
), fontWeight: FontWeight.normal,
onTap: () { ),
Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'One-Time'),)); onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => OnetimePasswordPage(deviceId:deviceId,),));
}, },
trailing: const Icon( trailing: const Icon(
Icons.arrow_forward_ios, Icons.arrow_forward_ios,
color: ColorsManager.greyColor, color: ColorsManager.greyColor,
size: 15, size: 15,
), ),
), ),
const Divider(color:ColorsManager.graysColor,),
const Divider(color:ColorsManager.graysColor,), ListTile(
ListTile( contentPadding: EdgeInsets.zero,
contentPadding: EdgeInsets.zero, leading: SvgPicture.asset(
leading: SvgPicture.asset( Assets.timeLimitedPassword),
Assets.timeLimitedPassword), title: const BodyMedium(
title: const BodyMedium( text: 'Time-Limited Password',
text: 'Time-Limited Password', fontWeight: FontWeight.normal,
fontWeight: FontWeight.normal, ),
), onTap: () {
onTap: () { Navigator.of(context).push(MaterialPageRoute(builder: (context) => TimeLimitedPasswordPage(deviceId:deviceId,),));
Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'Time-Limited'),)); },
}, trailing: const Icon(
trailing: const Icon( Icons.arrow_forward_ios,
Icons.arrow_forward_ios, color: ColorsManager.greyColor,
color: ColorsManager.greyColor, size: 15,
size: 15, ),
), ),
), ],)
],)
), ),
], ],
), ),

View File

@ -0,0 +1,105 @@
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/smart_door_bloc/smart_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/offline_timeLimit_password_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/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'door_dialog.dart';
class TimeLimitedPasswordPage extends StatelessWidget {
final String? deviceId;
const TimeLimitedPasswordPage({super.key, this.deviceId});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!)..add(InitialTimeLimitPassword()),
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(
listener: (context, state) {
if (state is FailedState) {
CustomSnackBar.displaySnackBar(
state.errorMessage
);
}
},
builder: (context, state) {
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
return DefaultScaffold(
title: 'Passwords',
actions: [
IconButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => CreateOfflineTimeLimitPasswordPage(deviceId: deviceId,)
)).then((result) {
smartDoorBloc.add(InitialTimeLimitPassword());
smartDoorBloc.add(InitialTimeLimitPassword());
});
},
icon: const Icon(Icons.add)
)
],
child: Builder(
builder: (context) {
return state is LoadingInitialState
? const Center(child: CircularProgressIndicator())
: Center(
child: smartDoorBloc.timeLimitPasswords!.isNotEmpty
? ListView.builder(
itemCount: smartDoorBloc.timeLimitPasswords!.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: SvgPicture.asset(Assets.timeLimitedPasswordIcon),
title: BodyMedium(
text: 'Password Name: ${smartDoorBloc.timeLimitPasswords![index].pwdName}',
fontWeight: FontWeight.normal,
),
onTap: () async {
final result = await showDialog(
context: context,
builder: (context) {
return DoorDialog(
title: 'Password Information',
offline: smartDoorBloc.timeLimitPasswords![index],
);
},
);
},
trailing: const Icon(
Icons.arrow_forward_ios,
color: ColorsManager.greyColor,
size: 15,
),
),
),
);
},
) : Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(Assets.noValidPasswords),
const SizedBox(
height: 10,
),
const BodyMedium(text: 'No Valid Passwords')
],
),
);
},
));
})
);
}
}

View File

@ -20,8 +20,7 @@ class ViewTemporaryPassword extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (BuildContext context) => create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!)..add(InitialPasswordsPage( )),
SmartDoorBloc(deviceId: deviceId!)..add(InitialPasswordsPage(type:type )),
child: BlocConsumer<SmartDoorBloc, SmartDoorState>( child: BlocConsumer<SmartDoorBloc, SmartDoorState>(
listener: (context, state) { listener: (context, state) {
if (state is FailedState) { if (state is FailedState) {
@ -32,84 +31,86 @@ class ViewTemporaryPassword extends StatelessWidget {
), ),
); );
} }
}, builder: (context, state) { },
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context); builder: (context, state) {
return DefaultScaffold( final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
title: 'Passwords', return DefaultScaffold(
actions: [ title: 'Passwords',
IconButton( actions: [
onPressed: () { IconButton(
Navigator.of(context).push(MaterialPageRoute( onPressed: () {
builder: (context) => CreateTemporaryPassword(deviceId: deviceId, type: type), Navigator.of(context).push(
)).then((result) { MaterialPageRoute(builder: (context) =>
if (result != null && result) { CreateTemporaryPassword(deviceId: deviceId, type: type))).then((result) {
smartDoorBloc.add(InitialPasswordsPage(type:type )); if (result != null && result) {
} smartDoorBloc.add(InitialPasswordsPage());
}); smartDoorBloc.add(InitialPasswordsPage());
}, }
icon: const Icon(Icons.add)) });
], },
child: Builder( icon: const Icon(Icons.add)
builder: (context) {
return state is LoadingInitialState
? const Center(child: CircularProgressIndicator())
: Center(
child: smartDoorBloc.temporaryPasswords!.isNotEmpty
? ListView.builder(
itemCount: smartDoorBloc.temporaryPasswords!.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: DefaultContainer(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 10),
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: SvgPicture.asset(
Assets.timeLimitedPasswordIcon),
title: BodyMedium(
text:
'Password Name: ${smartDoorBloc.temporaryPasswords![index].name}',
fontWeight: FontWeight.normal,
),
onTap: () async {
final result = await showDialog(
context: context,
builder: (context) {
return DoorDialog(
title: 'Password Information',
value: smartDoorBloc.temporaryPasswords![index],
);
},
);
if(result=='delete'){
smartDoorBloc.add(DeletePasswordEvent(passwordId: smartDoorBloc.temporaryPasswords![index].id.toString()));
}
},
trailing: const Icon(
Icons.arrow_forward_ios,
color: ColorsManager.greyColor,
size: 15,
),
),
),
);
},
) )
: Column( ],
crossAxisAlignment: CrossAxisAlignment.center, child: Builder(
mainAxisAlignment: MainAxisAlignment.center, builder: (context) {
children: [ return state is LoadingInitialState
SvgPicture.asset(Assets.noValidPasswords), ? const Center(child: CircularProgressIndicator())
const SizedBox( : Center(
height: 10, child: smartDoorBloc.temporaryPasswords!.isNotEmpty
? ListView.builder(
itemCount: smartDoorBloc.temporaryPasswords!.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: SvgPicture.asset(
Assets.timeLimitedPasswordIcon),
title: BodyMedium(
text: 'Password Name: ${smartDoorBloc.temporaryPasswords![index].name}',
fontWeight: FontWeight.normal,
),
onTap: () async {
final result = await showDialog(
context: context,
builder: (context) {
return DoorDialog(
title: 'Password Information',
temporaryPassword: smartDoorBloc.temporaryPasswords![index],
);
},
);
if(result=='delete'){
smartDoorBloc.add(DeletePasswordEvent(passwordId: smartDoorBloc.temporaryPasswords![index].id.toString()));
}
},
trailing: const Icon(
Icons.arrow_forward_ios,
color: ColorsManager.greyColor,
size: 15,
),
),
),
);
},
)
: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(Assets.noValidPasswords),
const SizedBox(
height: 10,
),
const BodyMedium(text: 'No Valid Passwords')
],
), ),
const BodyMedium(text: 'No Valid Passwords') );
], },
), ));
); })
}, );
));
}));
} }
} }

View File

@ -18,7 +18,7 @@ class ManageHomeView extends StatelessWidget {
title: 'Manage Your Home', title: 'Manage Your Home',
child: spaces == null child: spaces == null
? const Center( ? const Center(
child: CircularProgressIndicator(), child: BodyMedium(text: 'No spaces found'),
) )
: Column( : Column(
children: [ children: [
@ -30,8 +30,7 @@ class ManageHomeView extends StatelessWidget {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: children: List.generate(
List.generate(
spaces.length, spaces.length,
(index) { (index) {
if (index == spaces.length - 1) { if (index == spaces.length - 1) {
@ -43,12 +42,10 @@ class ManageHomeView extends StatelessWidget {
))); )));
}, },
child: Row( child: Row(
mainAxisAlignment: mainAxisAlignment: MainAxisAlignment.spaceBetween,
MainAxisAlignment.spaceBetween,
children: [ children: [
BodyMedium( BodyMedium(
text: StringHelpers.toTitleCase( text: StringHelpers.toTitleCase(spaces[index].name ?? "")),
spaces[index].name ?? "")),
const Icon( const Icon(
Icons.arrow_forward_ios, Icons.arrow_forward_ios,
color: ColorsManager.greyColor, color: ColorsManager.greyColor,
@ -76,14 +73,10 @@ class ManageHomeView extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Row( Row(
mainAxisAlignment: mainAxisAlignment: MainAxisAlignment.spaceBetween,
MainAxisAlignment.spaceBetween,
children: [ children: [
BodyMedium( BodyMedium(
text: HomeCubit.getInstance() text: HomeCubit.getInstance().spaces![index].name ?? ""),
.spaces![index]
.name ??
""),
const Icon( const Icon(
Icons.arrow_forward_ios, Icons.arrow_forward_ios,
color: ColorsManager.greyColor, color: ColorsManager.greyColor,
@ -92,8 +85,7 @@ class ManageHomeView extends StatelessWidget {
], ],
), ),
Container( Container(
margin: margin: const EdgeInsets.symmetric(vertical: 15),
const EdgeInsets.symmetric(vertical: 15),
height: 1, height: 1,
color: ColorsManager.greyColor, color: ColorsManager.greyColor,
), ),

View File

@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart'; import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart';
@ -10,6 +9,16 @@ import 'package:syncrow_app/features/scene/model/create_automation_model.dart';
import 'package:syncrow_app/navigation/navigation_service.dart'; import 'package:syncrow_app/navigation/navigation_service.dart';
class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> { class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
final daysMap = {
'Sun': 'S',
'Mon': 'M',
'Tue': 'T',
'Wed': 'W',
'Thu': 'T',
'Fri': 'F',
'Sat': 'S',
};
EffectPeriodBloc() : super(EffectPeriodState.initial()) { EffectPeriodBloc() : super(EffectPeriodState.initial()) {
on<SetPeriod>(_onSetPeriod); on<SetPeriod>(_onSetPeriod);
on<ToggleDay>(_onToggleDay); on<ToggleDay>(_onToggleDay);
@ -44,20 +53,17 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
break; break;
} }
BlocProvider.of<CreateSceneBloc>( BlocProvider.of<CreateSceneBloc>(NavigationService.navigatorKey.currentContext!).add(
NavigationService.navigatorKey.currentContext!) EffectiveTimePeriodEvent(
.add(EffectiveTimePeriodEvent(EffectiveTime( EffectiveTime(start: startTime, end: endTime, loops: state.selectedDaysBinary)));
start: startTime, end: endTime, loops: state.selectedDaysBinary)));
emit(state.copyWith( emit(state.copyWith(
selectedPeriod: event.period, selectedPeriod: event.period, customStartTime: startTime, customEndTime: endTime));
customStartTime: startTime,
customEndTime: endTime));
} }
void _onToggleDay(ToggleDay event, Emitter<EffectPeriodState> emit) { void _onToggleDay(ToggleDay event, Emitter<EffectPeriodState> emit) {
final daysList = state.selectedDaysBinary.split(''); final daysList = state.selectedDaysBinary.split('');
final dayIndex = _getDayIndex(event.day); final dayIndex = getDayIndex(event.day);
if (daysList[dayIndex] == '1') { if (daysList[dayIndex] == '1') {
daysList[dayIndex] = '0'; daysList[dayIndex] = '0';
} else { } else {
@ -66,9 +72,8 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
final newDaysBinary = daysList.join(); final newDaysBinary = daysList.join();
emit(state.copyWith(selectedDaysBinary: newDaysBinary)); emit(state.copyWith(selectedDaysBinary: newDaysBinary));
BlocProvider.of<CreateSceneBloc>( BlocProvider.of<CreateSceneBloc>(NavigationService.navigatorKey.currentContext!).add(
NavigationService.navigatorKey.currentContext!) EffectiveTimePeriodEvent(EffectiveTime(
.add(EffectiveTimePeriodEvent(EffectiveTime(
start: state.customStartTime ?? '00:00', start: state.customStartTime ?? '00:00',
end: state.customEndTime ?? '23:59', end: state.customEndTime ?? '23:59',
loops: newDaysBinary))); loops: newDaysBinary)));
@ -90,37 +95,31 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
period = EnumEffectivePeriodOptions.custom; period = EnumEffectivePeriodOptions.custom;
} }
emit(state.copyWith( emit(
customStartTime: startTime, state.copyWith(customStartTime: startTime, customEndTime: endTime, selectedPeriod: period));
customEndTime: endTime,
selectedPeriod: period));
BlocProvider.of<CreateSceneBloc>( BlocProvider.of<CreateSceneBloc>(NavigationService.navigatorKey.currentContext!).add(
NavigationService.navigatorKey.currentContext!) EffectiveTimePeriodEvent(
.add(EffectiveTimePeriodEvent(EffectiveTime( EffectiveTime(start: startTime, end: endTime, loops: state.selectedDaysBinary)));
start: startTime, end: endTime, loops: state.selectedDaysBinary)));
} }
void _onResetEffectivePeriod( void _onResetEffectivePeriod(ResetEffectivePeriod event, Emitter<EffectPeriodState> emit) {
ResetEffectivePeriod event, Emitter<EffectPeriodState> emit) {
emit(state.copyWith( emit(state.copyWith(
selectedPeriod: EnumEffectivePeriodOptions.allDay, selectedPeriod: EnumEffectivePeriodOptions.allDay,
customStartTime: '00:00', customStartTime: '00:00',
customEndTime: '23:59', customEndTime: '23:59',
selectedDaysBinary: '1111111')); selectedDaysBinary: '1111111'));
BlocProvider.of<CreateSceneBloc>( BlocProvider.of<CreateSceneBloc>(NavigationService.navigatorKey.currentContext!).add(
NavigationService.navigatorKey.currentContext!) EffectiveTimePeriodEvent(EffectiveTime(start: '00:00', end: '23:59', loops: '1111111')));
.add(EffectiveTimePeriodEvent(
EffectiveTime(start: '00:00', end: '23:59', loops: '1111111')));
} }
void _onResetDays(ResetDays event, Emitter<EffectPeriodState> emit) { void _onResetDays(ResetDays event, Emitter<EffectPeriodState> emit) {
emit(state.copyWith(selectedDaysBinary: '1111111')); emit(state.copyWith(selectedDaysBinary: '1111111'));
} }
int _getDayIndex(String day) { int getDayIndex(String day) {
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
return days.indexOf(day); return days.indexOf(day);
} }

View File

@ -34,6 +34,7 @@ mixin SceneLogicHelper {
required List<SceneStaticFunction> conditions, required List<SceneStaticFunction> conditions,
}) { }) {
final sceneBloc = context.read<CreateSceneBloc>(); final sceneBloc = context.read<CreateSceneBloc>();
final effectiveTime = sceneBloc.effectiveTime;
if (isOnlyDelayOrDelayLast(actions)) { if (isOnlyDelayOrDelayLast(actions)) {
context.showCustomSnackbar( context.showCustomSnackbar(
@ -46,6 +47,17 @@ mixin SceneLogicHelper {
return; return;
} }
if (isAutomation == true && effectiveTime?.loops == '0000000') {
context.showCustomSnackbar(
message: 'At least one day in Effective Period must be selected!',
icon: const Icon(
Icons.error,
color: Colors.red,
),
);
return;
}
if (isAutomation) { if (isAutomation) {
final createAutomationModel = CreateAutomationModel( final createAutomationModel = CreateAutomationModel(
unitUuid: HomeCubit.getInstance().selectedSpace!.id ?? '', unitUuid: HomeCubit.getInstance().selectedSpace!.id ?? '',
@ -81,7 +93,7 @@ mixin SceneLogicHelper {
executorProperty: CreateSceneExecutorProperty( executorProperty: CreateSceneExecutorProperty(
functionCode: '', functionCode: '',
functionValue: '', functionValue: '',
delaySeconds: task.functionValue, delaySeconds: task.functionValue ?? 1,
), ),
); );
} }
@ -159,21 +171,24 @@ mixin SceneLogicHelper {
} }
} }
Widget getTheCorrectDialogBody(SceneStaticFunction taskItem, dynamic functionValue, Widget getTheCorrectDialogBody(
SceneStaticFunction taskItem, dynamic functionValue,
{required bool isAutomation}) { {required bool isAutomation}) {
if (taskItem.operationDialogType == OperationDialogType.temperature) { if (taskItem.operationDialogType == OperationDialogType.temperature) {
return AlertDialogTemperatureBody( return AlertDialogTemperatureBody(
taskItem: taskItem, taskItem: taskItem,
functionValue: functionValue ?? taskItem.functionValue, functionValue: functionValue ?? taskItem.functionValue,
); );
} else if ((taskItem.operationDialogType == OperationDialogType.countdown) || } else if ((taskItem.operationDialogType ==
OperationDialogType.countdown) ||
(taskItem.operationDialogType == OperationDialogType.delay)) { (taskItem.operationDialogType == OperationDialogType.delay)) {
return AlertDialogCountdown( return AlertDialogCountdown(
durationValue: taskItem.functionValue ?? 0, durationValue: taskItem.functionValue ?? 0,
functionValue: functionValue ?? taskItem.functionValue, functionValue: functionValue ?? taskItem.functionValue,
function: taskItem, function: taskItem,
); );
} else if (taskItem.operationDialogType == OperationDialogType.integerSteps) { } else if (taskItem.operationDialogType ==
OperationDialogType.integerSteps) {
return AlertDialogSliderSteps( return AlertDialogSliderSteps(
taskItem: taskItem, taskItem: taskItem,
functionValue: functionValue ?? taskItem.functionValue, functionValue: functionValue ?? taskItem.functionValue,

View File

@ -15,10 +15,12 @@ class SceneAutoSettings extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final sceneSettings = ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>? ?? {}; final sceneSettings =
ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>? ??
{};
final sceneId = sceneSettings['sceneId'] as String? ?? ''; final sceneId = sceneSettings['sceneId'] as String? ?? '';
final isAutomation = final isAutomation = context.read<CreateSceneBloc>().sceneType ==
context.read<CreateSceneBloc>().sceneType == CreateSceneEnum.deviceStatusChanges; CreateSceneEnum.deviceStatusChanges;
final sceneName = sceneSettings['sceneName'] as String? ?? ''; final sceneName = sceneSettings['sceneName'] as String? ?? '';
return DefaultScaffold( return DefaultScaffold(
@ -48,9 +50,11 @@ class SceneAutoSettings extends StatelessWidget {
Visibility( Visibility(
visible: isAutomation, visible: isAutomation,
child: SceneListTile( child: SceneListTile(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 8),
titleString: "Effective Period", titleString: "Effective Period",
trailingWidget: const Icon(Icons.arrow_forward_ios_rounded), trailingWidget:
const Icon(Icons.arrow_forward_ios_rounded),
onPressed: () { onPressed: () {
context.customBottomSheet( context.customBottomSheet(
child: const EffectPeriodBottomSheetContent(), child: const EffectPeriodBottomSheetContent(),

View File

@ -48,12 +48,21 @@ class _AlertDialogSliderStepsState extends State<AlertDialogSliderSteps> {
} }
} }
} }
if (widget.taskItem.code == 'temp_current') {
if (widget.functionValue != null) { if (widget.functionValue != null) {
groupValue = _normalizeValue(widget.functionValue); groupValue = _normalizeValue(
double.tryParse(widget.functionValue.toString()) ??
widget.taskItem.operationalValues[0].minValue);
} else {
groupValue = widget.taskItem.operationalValues[0].minValue;
}
} else { } else {
groupValue = if (widget.functionValue != null) {
_normalizeValue(widget.taskItem.operationalValues[0].minValue); groupValue = _normalizeValue(widget.functionValue);
} else {
groupValue =
_normalizeValue(widget.taskItem.operationalValues[0].minValue);
}
} }
setState(() {}); setState(() {});
@ -68,14 +77,6 @@ class _AlertDialogSliderStepsState extends State<AlertDialogSliderSteps> {
); );
} }
double _normalizeValue(dynamic value) {
if (((widget.taskItem.code == "temp_set" && value > 199) ||
widget.taskItem.code == "temp_current")) {
return (value) / 10;
}
return value.toDouble();
}
int _comparatorToIndex(String? comparator) { int _comparatorToIndex(String? comparator) {
switch (comparator) { switch (comparator) {
case "<": case "<":
@ -216,7 +217,7 @@ class _AlertDialogSliderStepsState extends State<AlertDialogSliderSteps> {
: null, : null,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
groupValue = normalizeValue(value); groupValue = normalizeToDoubleValue(value);
}); });
context.read<CreateSceneBloc>().add(SelectedValueEvent( context.read<CreateSceneBloc>().add(SelectedValueEvent(
value: _deNormalizeValue(groupValue), value: _deNormalizeValue(groupValue),
@ -237,8 +238,16 @@ class _AlertDialogSliderStepsState extends State<AlertDialogSliderSteps> {
); );
} }
double normalizeValue(double value) { double _normalizeValue(dynamic value) {
return double.parse(value.toStringAsFixed(1)); if ((widget.taskItem.code == "temp_set" && value > 199) ||
(widget.taskItem.code == "temp_current" && value >= -99.0)) {
return (value) / 10;
}
return value.toDouble();
}
double? normalizeToDoubleValue(double value) {
return double.tryParse(value.toStringAsFixed(1));
} }
double _deNormalizeValue(double? value) { double _deNormalizeValue(double? value) {

View File

@ -50,10 +50,10 @@ class _AlertDialogTemperatureBodyState
}); });
} }
// context.read<CreateSceneBloc>().add(SelectedValueEvent( context.read<CreateSceneBloc>().add(SelectedValueEvent(
// value: temperature * 10, value: temperature * 10,
// code: widget.taskItem.code, code: widget.taskItem.code,
// )); ));
} }
int _normalizeTemperature(dynamic value) { int _normalizeTemperature(dynamic value) {

View File

@ -19,18 +19,8 @@ class EffectPeriodBottomSheetContent extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
// Expanded(
// child: TextButton(
// onPressed: () => Navigator.pop(context),
// child: BodySmall(
// text: 'Save',
// style:
// context.bodySmall.copyWith(color: ColorsManager.primaryColorWithOpacity),
// )),
// ),
const Spacer(), const Spacer(),
Expanded( Expanded(
// flex: 2,
child: BodyMedium( child: BodyMedium(
text: 'Effective Period', text: 'Effective Period',
fontColor: ColorsManager.primaryColorWithOpacity, fontColor: ColorsManager.primaryColorWithOpacity,

View File

@ -4,22 +4,16 @@ import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_b
import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart'; import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart';
import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_state.dart'; import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_state.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.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/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class RepeatDays extends StatelessWidget { class RepeatDays extends StatelessWidget {
const RepeatDays({super.key}); const RepeatDays({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final daysMap = { final effectiveBloc = context.read<EffectPeriodBloc>();
'Mon': 'M',
'Tue': 'T',
'Wed': 'W',
'Thu': 'T',
'Fri': 'F',
'Sat': 'S',
'Sun': 'S',
};
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
@ -27,53 +21,61 @@ class RepeatDays extends StatelessWidget {
const SizedBox(width: 8), const SizedBox(width: 8),
BlocBuilder<EffectPeriodBloc, EffectPeriodState>( BlocBuilder<EffectPeriodBloc, EffectPeriodState>(
builder: (context, state) { builder: (context, state) {
return Row( return Column(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisSize: MainAxisSize.min,
children: daysMap.entries.map((entry) { crossAxisAlignment: CrossAxisAlignment.start,
final day = entry.key; mainAxisAlignment: MainAxisAlignment.center,
final abbreviation = entry.value; children: [
final dayIndex = _getDayIndex(day); Row(
final isSelected = state.selectedDaysBinary[dayIndex] == '1'; mainAxisAlignment: MainAxisAlignment.spaceAround,
return Padding( children: effectiveBloc.daysMap.entries.map((entry) {
padding: const EdgeInsets.symmetric(horizontal: 3.0), final day = entry.key;
child: GestureDetector( final abbreviation = entry.value;
onTap: () { final dayIndex = effectiveBloc.getDayIndex(day);
context.read<EffectPeriodBloc>().add(ToggleDay(day)); final isSelected = state.selectedDaysBinary[dayIndex] == '1';
}, return Padding(
child: Container( padding: const EdgeInsets.symmetric(horizontal: 3.0),
decoration: BoxDecoration( child: GestureDetector(
shape: BoxShape.circle, onTap: () {
border: Border.all( effectiveBloc.add(ToggleDay(day));
color: },
isSelected ? Colors.grey : Colors.grey.shade300, child: Container(
width: 1, decoration: BoxDecoration(
), shape: BoxShape.circle,
), border: Border.all(
child: CircleAvatar( color: isSelected ? Colors.grey : Colors.grey.shade300,
radius: 15, width: 1,
backgroundColor: Colors.white, ),
child: Text( ),
abbreviation, child: CircleAvatar(
style: TextStyle( radius: 15,
fontSize: 16, backgroundColor: Colors.white,
color: child: Text(
isSelected ? Colors.grey : Colors.grey.shade300, abbreviation,
style: TextStyle(
fontSize: 16,
color: isSelected ? Colors.grey : Colors.grey.shade300,
),
),
), ),
), ),
), ),
), );
}).toList(),
),
const SizedBox(
height: 8,
),
if (state.selectedDaysBinary == '0000000')
BodySmall(
text: 'At least one day must be selected',
style: context.bodyMedium.copyWith(color: ColorsManager.red),
), ),
); ],
}).toList(),
); );
}, },
), ),
], ],
); );
} }
int _getDayIndex(String day) {
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
return days.indexOf(day);
}
} }

View File

@ -93,7 +93,7 @@ class _SmartSceneSelectTabToRunListState extends State<SmartSceneSelectTabToRunL
.read<SmartSceneSelectBloc>() .read<SmartSceneSelectBloc>()
.add(SmartSceneEnableEvent(SmartSceneEnable( .add(SmartSceneEnableEvent(SmartSceneEnable(
entityId: scene.id, entityId: scene.id,
actionExecutor: 'rule_enable', actionExecutor: 'rule_trigger',
sceneORAutomationName: scene.name, sceneORAutomationName: scene.name,
type: scene.type, type: scene.type,
isAutomation: false, isAutomation: false,
@ -109,7 +109,7 @@ class _SmartSceneSelectTabToRunListState extends State<SmartSceneSelectTabToRunL
}); });
context.read<SmartSceneSelectBloc>().add(SmartSceneEnableEvent(SmartSceneEnable( context.read<SmartSceneSelectBloc>().add(SmartSceneEnableEvent(SmartSceneEnable(
entityId: scene.id, entityId: scene.id,
actionExecutor: 'rule_enable', actionExecutor: 'rule_trigger',
sceneORAutomationName: scene.name, sceneORAutomationName: scene.name,
type: scene.type, type: scene.type,
isAutomation: false, isAutomation: false,

View File

@ -0,0 +1,99 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class DoorLockButton extends StatelessWidget {
const DoorLockButton({
super.key,
this.enabled = true,
this.onPressed,
required this.child,
this.isSecondary = false,
this.isLoading = false,
this.isDone = false,
this.customTextStyle,
this.customButtonStyle,
this.backgroundColor,
this.foregroundColor,
this.borderRadius,
this.height,
this.padding,
});
final void Function()? onPressed;
final Widget child;
final double? height;
final bool isSecondary;
final double? borderRadius;
final bool enabled;
final double? padding;
final bool isDone;
final bool isLoading;
final TextStyle? customTextStyle;
final ButtonStyle? customButtonStyle;
final Color? backgroundColor;
final Color? foregroundColor;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: enabled ? onPressed : null,
style: isSecondary
? null
: customButtonStyle ??
ButtonStyle(
textStyle: MaterialStateProperty.all(
customTextStyle ??
context.bodyMedium.copyWith(
fontSize: 16,
color: foregroundColor,
),
),
foregroundColor: MaterialStateProperty.all(
isSecondary
? Colors.black
: enabled
? foregroundColor ?? Colors.white
: Colors.black,
),
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) {
return enabled
? backgroundColor ?? ColorsManager.primaryColor
: Colors.grey;
}),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(borderRadius ?? 20),
),
),
fixedSize: MaterialStateProperty.all(
const Size.fromHeight(50),
),
padding: MaterialStateProperty.all(
EdgeInsets.all(padding ?? 10),
),
minimumSize: MaterialStateProperty.all(
const Size.fromHeight(50),
),
),
child: SizedBox(
height: height ?? 50,
child: Center(
child: isLoading
? const SizedBox.square(
dimension: 24,
child: CircularProgressIndicator(
color: Colors.white,
),
)
: isDone
? const Text('Done') :child ,
),
),
);
}
}

View File

@ -7,9 +7,7 @@ import 'package:syncrow_app/firebase_options.dart';
import 'package:syncrow_app/services/locator.dart'; import 'package:syncrow_app/services/locator.dart';
import 'package:syncrow_app/utils/bloc_observer.dart'; import 'package:syncrow_app/utils/bloc_observer.dart';
import 'package:syncrow_app/utils/helpers/localization_helpers.dart'; import 'package:syncrow_app/utils/helpers/localization_helpers.dart';
import 'my_app.dart'; import 'my_app.dart';
void main() { void main() {
//to observe the state of the blocs in the output console //to observe the state of the blocs in the output console
Bloc.observer = MyBlocObserver(); Bloc.observer = MyBlocObserver();

View File

@ -81,8 +81,7 @@ abstract class ApiEndpoints {
static const String controlGroup = '/group/control'; static const String controlGroup = '/group/control';
//GET //GET
static const String groupBySpace = '/group/{unitUuid}'; static const String groupBySpace = '/group/{unitUuid}';
static const String devicesByGroupName = static const String devicesByGroupName = '/group/{unitUuid}/devices/{groupName}';
'/group/{unitUuid}/devices/{groupName}';
static const String groupByUuid = '/group/{groupUuid}'; static const String groupByUuid = '/group/{groupUuid}';
//DELETE //DELETE
@ -94,18 +93,17 @@ abstract class ApiEndpoints {
static const String addDeviceToRoom = '/device/room'; static const String addDeviceToRoom = '/device/room';
static const String addDeviceToGroup = '/device/group'; static const String addDeviceToGroup = '/device/group';
static const String controlDevice = '/device/{deviceUuid}/control'; static const String controlDevice = '/device/{deviceUuid}/control';
static const String firmwareDevice = static const String firmwareDevice = '/device/{deviceUuid}/firmware/{firmwareVersion}';
'/device/{deviceUuid}/firmware/{firmwareVersion}';
static const String getDevicesByUserId = '/device/user/{userId}'; static const String getDevicesByUserId = '/device/user/{userId}';
static const String getDevicesByUnitId = '/device/unit/{unitUuid}'; static const String getDevicesByUnitId = '/device/unit/{unitUuid}';
static const String openDoorLock = '/door-lock/open/{doorLockUuid}';
//GET //GET
static const String deviceByRoom = '/device/room'; static const String deviceByRoom = '/device/room';
static const String deviceByUuid = '/device/{deviceUuid}'; static const String deviceByUuid = '/device/{deviceUuid}';
static const String deviceFunctions = '/device/{deviceUuid}/functions'; static const String deviceFunctions = '/device/{deviceUuid}/functions';
static const String gatewayApi = '/device/gateway/{gatewayUuid}/devices'; static const String gatewayApi = '/device/gateway/{gatewayUuid}/devices';
static const String deviceFunctionsStatus = static const String deviceFunctionsStatus = '/device/{deviceUuid}/functions/status';
'/device/{deviceUuid}/functions/status';
///Device Permission Module ///Device Permission Module
//POST //POST
@ -130,29 +128,24 @@ abstract class ApiEndpoints {
static const String getUnitAutomation = '/automation/{unitUuid}'; static const String getUnitAutomation = '/automation/{unitUuid}';
static const String getAutomationDetails = static const String getAutomationDetails = '/automation/details/{automationId}';
'/automation/details/{automationId}';
/// PUT /// PUT
static const String updateScene = '/scene/tap-to-run/{sceneId}'; static const String updateScene = '/scene/tap-to-run/{sceneId}';
static const String updateAutomation = '/automation/{automationId}'; static const String updateAutomation = '/automation/{automationId}';
static const String updateAutomationStatus = static const String updateAutomationStatus = '/automation/status/{automationId}';
'/automation/status/{automationId}';
/// DELETE /// DELETE
static const String deleteScene = '/scene/tap-to-run/{unitUuid}/{sceneId}'; static const String deleteScene = '/scene/tap-to-run/{unitUuid}/{sceneId}';
static const String deleteAutomation = static const String deleteAutomation = '/automation/{unitUuid}/{automationId}';
'/automation/{unitUuid}/{automationId}';
//////////////////////Door Lock ////////////////////// //////////////////////Door Lock //////////////////////
//online //online
static const String addTemporaryPassword = static const String addTemporaryPassword = '/door-lock/temporary-password/online/{doorLockUuid}';
'/door-lock/temporary-password/online/{doorLockUuid}'; static const String getTemporaryPassword = '/door-lock/temporary-password/online/{doorLockUuid}';
static const String getTemporaryPassword =
'/door-lock/temporary-password/online/{doorLockUuid}';
//one-time offline //one-time offline
static const String addOneTimeTemporaryPassword = static const String addOneTimeTemporaryPassword =
@ -160,16 +153,6 @@ abstract class ApiEndpoints {
static const String getOneTimeTemporaryPassword = static const String getOneTimeTemporaryPassword =
'/door-lock/temporary-password/offline/one-time/{doorLockUuid}'; '/door-lock/temporary-password/offline/one-time/{doorLockUuid}';
//multiple-time offline
static const String addMultipleTimeTemporaryPassword =
'/door-lock/temporary-password/offline/multiple-time/{doorLockUuid}';
static const String getMultipleTimeTemporaryPassword =
'/door-lock/temporary-password/offline/multiple-time/{doorLockUuid}';
//multiple-time offline
static const String deleteTemporaryPassword =
'/door-lock/temporary-password/{doorLockUuid}/{passwordId}';
//user //user
static const String getUser = '/user/{userUuid}'; static const String getUser = '/user/{userUuid}';
@ -179,4 +162,17 @@ abstract class ApiEndpoints {
static const String sendPicture = '/user/profile-picture/{userUuid}'; static const String sendPicture = '/user/profile-picture/{userUuid}';
static const String getRegion = '/region'; static const String getRegion = '/region';
static const String getTimezone = '/timezone'; static const String getTimezone = '/timezone';
//multiple-time offline
static const String addMultipleTimeTemporaryPassword =
'/door-lock/temporary-password/offline/multiple-time/{doorLockUuid}';
static const String getMultipleTimeTemporaryPassword =
'/door-lock/temporary-password/offline/multiple-time/{doorLockUuid}';
static const String renamePassword =
'/door-lock/temporary-password/{doorLockUuid}/offline/{passwordId}';
//multiple-time offline
static const String deleteTemporaryPassword =
'/door-lock/temporary-password/online/{doorLockUuid}/{passwordId}';
} }

View File

@ -32,6 +32,7 @@ class DevicesAPI {
static Future<Map<String, dynamic>> controlDevice( static Future<Map<String, dynamic>> controlDevice(
DeviceControlModel controlModel, String deviceId) async { DeviceControlModel controlModel, String deviceId) async {
try { try {
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.controlDevice.replaceAll('{deviceUuid}', deviceId), path: ApiEndpoints.controlDevice.replaceAll('{deviceUuid}', deviceId),
body: controlModel.toJson(), body: controlModel.toJson(),
@ -46,6 +47,17 @@ class DevicesAPI {
} }
} }
static Future<bool> openDoorLock(String deviceId) async {
final response = await _httpService.post(
path: ApiEndpoints.openDoorLock.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json['success'] ?? false;
},
);
return response;
}
static Future<List<DevicesCategoryModel>> fetchGroups(String spaceId) async { static Future<List<DevicesCategoryModel>> fetchGroups(String spaceId) async {
// Map<String, dynamic> params = {"homeId": spaceId, "pageSize": 100, "pageNo": 1}; // Map<String, dynamic> params = {"homeId": spaceId, "pageSize": 100, "pageNo": 1};
@ -69,6 +81,20 @@ class DevicesAPI {
return response; return response;
} }
static Future<Map<String, dynamic>> renamePass(
{required String name, required String doorLockUuid, required String passwordId}) async {
final response = await _httpService.put(
path: ApiEndpoints.renamePassword
.replaceAll('{doorLockUuid}', doorLockUuid)
.replaceAll('{passwordId}', passwordId),
body: {"name": name},
expectedResponseModel: (json) {
return json;
},
);
return response;
}
/// Get Device Functions /// Get Device Functions
static Future<FunctionModel> deviceFunctions(String deviceId) async { static Future<FunctionModel> deviceFunctions(String deviceId) async {
final response = await _httpService.get( final response = await _httpService.get(
@ -138,14 +164,11 @@ class DevicesAPI {
return response; return response;
} }
static Future getTemporaryPasswords(String deviceId, pageType) async { static Future getTemporaryPasswords(
String deviceId,
) async {
final response = await _httpService.get( final response = await _httpService.get(
path: pageType == 'One-Time' path: ApiEndpoints.getTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
? ApiEndpoints.getOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId)
: pageType == 'Online Password'
? ApiEndpoints.getTemporaryPassword.replaceAll('{doorLockUuid}', deviceId)
: ApiEndpoints.getMultipleTimeTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json;
@ -154,37 +177,48 @@ class DevicesAPI {
return response; return response;
} }
//create password static Future getOneTimePasswords(String deviceId) async {
final response = await _httpService.get(
path: ApiEndpoints.getOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
},
);
return response;
}
static Future getTimeLimitPasswords(String deviceId) async {
final response = await _httpService.get(
path: ApiEndpoints.getMultipleTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
},
);
return response;
}
//create password
static Future createPassword({ static Future createPassword({
required String name, required String name,
required String password, required String password,
required String effectiveTime, required String effectiveTime,
required String invalidTime, required String invalidTime,
required String deviceId, required String deviceId,
required String pageType,
List<Schedule>? scheduleList, List<Schedule>? scheduleList,
}) async { }) async {
String endpointPath;
if (pageType == 'Online Password') {
endpointPath = ApiEndpoints.addTemporaryPassword.replaceAll('{doorLockUuid}', deviceId);
} else if (pageType == 'One-Time') {
endpointPath =
ApiEndpoints.addOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId);
} else {
endpointPath =
ApiEndpoints.addMultipleTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId);
}
Map<String, dynamic> body = { Map<String, dynamic> body = {
"name": name, "name": name,
"password": password, "password": password,
"effectiveTime": effectiveTime, "effectiveTime": effectiveTime,
"invalidTime": invalidTime, "invalidTime": invalidTime,
}; };
if (pageType == 'Online Password' && scheduleList != null) { if (scheduleList != null) {
body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList(); body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList();
} }
final response = await _httpService.post( final response = await _httpService.post(
path: endpointPath, path: ApiEndpoints.addTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
body: body, body: body,
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) => json, expectedResponseModel: (json) => json,
@ -192,6 +226,37 @@ class DevicesAPI {
return response; return response;
} }
static Future generateOneTimePassword({deviceId}) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
},
);
return response;
} catch (e) {
rethrow;
}
}
static Future generateMultiTimePassword({deviceId, effectiveTime, invalidTime}) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addMultipleTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: true,
body: {"effectiveTime": effectiveTime, "invalidTime": invalidTime},
expectedResponseModel: (json) {
return json;
},
);
return response;
} catch (e) {
rethrow;
}
}
static Future<Map<String, dynamic>> deletePassword( static Future<Map<String, dynamic>> deletePassword(
{required String passwordId, required String deviceId}) async { {required String passwordId, required String deviceId}) async {
final response = await _httpService.delete( final response = await _httpService.delete(

View File

@ -68,6 +68,7 @@ Map<String, DeviceType> devicesTypesMap = {
"DL": DeviceType.DoorLock, "DL": DeviceType.DoorLock,
"WPS": DeviceType.WallSensor, "WPS": DeviceType.WallSensor,
"3G": DeviceType.ThreeGang, "3G": DeviceType.ThreeGang,
"CUR": DeviceType.Curtain,
}; };
Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = { Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
DeviceType.AC: [ DeviceType.AC: [
@ -174,6 +175,21 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
type: functionTypesMap['Integer'], 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(
code: 'control',
type: functionTypesMap['Enum'],
values: ValueModel.fromJson(
{"range": ["open","stop","close"]}
)
),
FunctionModel(
code: 'percent_control',
type: functionTypesMap['Integer'],
values: ValueModel.fromJson(
{"unit": "%", "min": 0, "max": 100, "scale": 0, "step": 1})
),
],
}; };
enum TempModes { hot, cold, wind } enum TempModes { hot, cold, wind }

View File

@ -5,10 +5,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: _flutterfire_internals name: _flutterfire_internals
sha256: fe4c077084ddda88f327dc1c96d16631cd68d4948644593fcbcd911c2c89e2fa sha256: "37a42d06068e2fe3deddb2da079a8c4d105f241225ba27b7122b37e9865fd8f7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.23" version: "1.3.35"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -245,10 +245,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: firebase_core name: firebase_core
sha256: "797379ea206eaeeb62499775de812761493d0692890fdc7f90b6183a3369176d" sha256: "26de145bb9688a90962faec6f838247377b0b0d32cc0abecd9a4e43525fc856c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.25.5" version: "2.32.0"
firebase_core_platform_interface: firebase_core_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -281,6 +281,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.6.23" version: "3.6.23"
firebase_database:
dependency: "direct main"
description:
name: firebase_database
sha256: "3b9ca306d26ad243ccbc4c717ff6e8563a080ebe11ee77fa7349b419c894b42d"
url: "https://pub.dev"
source: hosted
version: "10.5.7"
firebase_database_platform_interface:
dependency: transitive
description:
name: firebase_database_platform_interface
sha256: "5864cc362275465e9bd682b243f19419c9d78b861c2db820241eea596ae3b320"
url: "https://pub.dev"
source: hosted
version: "0.2.5+35"
firebase_database_web:
dependency: transitive
description:
name: firebase_database_web
sha256: a6008395dd20e8b8dde0691b441c181a1216c3866f89f48dcb6889d34fd35905
url: "https://pub.dev"
source: hosted
version: "0.2.5+7"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:

View File

@ -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. # 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 publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 1.0.2+16 version: 1.0.3+21
environment: environment:
sdk: ">=3.0.6 <4.0.0" sdk: ">=3.0.6 <4.0.0"
@ -45,6 +45,7 @@ dependencies:
time_picker_spinner: ^1.0.0 time_picker_spinner: ^1.0.0
image_picker: ^1.1.2 image_picker: ^1.1.2
device_info_plus: ^10.1.0 device_info_plus: ^10.1.0
firebase_database: ^10.5.7
dev_dependencies: dev_dependencies:
flutter_lints: ^3.0.1 flutter_lints: ^3.0.1