Compare commits
40 Commits
online_doo
...
bugfix/SP-
Author | SHA1 | Date | |
---|---|---|---|
340ac35b3e | |||
af31d33778 | |||
26833cf215 | |||
6165c2ceee | |||
65bb388daf | |||
b7f59902cd | |||
740ca4e4ba | |||
d9cd24e7f0 | |||
42b5ff105f | |||
ddaf36797d | |||
ad47e34fa2 | |||
46662b5bac | |||
b5842194ff | |||
ba4ebd07e1 | |||
21c336360c | |||
fee34703bd | |||
a92676d6a4 | |||
8c5cf2c04e | |||
1f02f66916 | |||
3ea089425c | |||
76be98354b | |||
3418fbe7b4 | |||
ea3bc4b5e1 | |||
77d2f54352 | |||
cff66bb653 | |||
022ba53f5d | |||
bf69399af2 | |||
a72fc0b466 | |||
df13840a65 | |||
e63bf2a2c2 | |||
bf944a6121 | |||
eb5aa56536 | |||
1e35bb8736 | |||
9c23ab1399 | |||
b3bb0b9eea | |||
032a28d47a | |||
dcc98445d7 | |||
611c515173 | |||
e733dd9230 | |||
8e104aeea7 |
5
assets/icons/1gang.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M38.0142 39.2553L35.3682 40H20.9308H19.9999H19.0691H1.24111C0.555643 40 0 39.4444 0 38.7589V1.24111C0 0.555643 0.555643 0 1.24111 0H19.0682H20.1226H20.9543H35.4255L38.2625 1.24111C38.9479 1.24111 39.5036 1.79675 39.5036 2.48221L39.2553 38.0142C39.2553 38.6997 38.6997 39.2553 38.0142 39.2553Z" fill="#E9E9E9"/>
|
||||
<path d="M38.7589 0H35.0356C35.721 0 36.2767 0.555643 36.2767 1.24111V38.7589C36.2767 39.4444 35.721 40 35.0356 40H38.7589C39.4444 40 40 39.4444 40 38.7589V1.24111C40 0.555643 39.4444 0 38.7589 0Z" fill="#D1D1D1"/>
|
||||
<path opacity="0.6" d="M21.375 31.8319V33.3213C21.375 34.0067 20.9553 34.5624 20.4375 34.5624H16.3125C15.7947 34.5624 15.375 34.0067 15.375 33.3213V31.8319C15.375 31.1465 15.7947 30.5908 16.3125 30.5908H20.4375C20.9553 30.5908 21.375 31.1465 21.375 31.8319Z" fill="#023DFE" fill-opacity="0.5"/>
|
||||
</svg>
|
After Width: | Height: | Size: 933 B |
7
assets/icons/2gang.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M38.0142 39.2553L35.3682 40H20.9308H19.9999H19.0691H1.24111C0.555643 40 0 39.4444 0 38.7589V1.24111C0 0.555643 0.555643 0 1.24111 0H19.0682H20.1226H20.9543H35.4255L38.2625 1.24111C38.9479 1.24111 39.5036 1.79675 39.5036 2.48221L39.2553 38.0142C39.2553 38.6997 38.6997 39.2553 38.0142 39.2553Z" fill="#E9E9E9"/>
|
||||
<path d="M38.7589 0H35.0356C35.721 0 36.2767 0.555643 36.2767 1.24111V38.7589C36.2767 39.4444 35.721 40 35.0356 40H38.7589C39.4444 40 40 39.4444 40 38.7589V1.24111C40 0.555643 39.4444 0 38.7589 0Z" fill="#D1D1D1"/>
|
||||
<path opacity="0.6" d="M12.0284 31.8319V33.3213C12.0284 34.0067 11.6087 34.5624 11.0909 34.5624H6.96594C6.44816 34.5624 6.02844 34.0067 6.02844 33.3213V31.8319C6.02844 31.1465 6.44816 30.5908 6.96594 30.5908H11.0909C11.6087 30.5908 12.0284 31.1465 12.0284 31.8319Z" fill="#023DFE" fill-opacity="0.5"/>
|
||||
<path opacity="0.6" d="M26.0285 31.8319V33.3213C26.0285 34.0067 26.4482 34.5624 26.966 34.5624H31.091C31.6088 34.5624 32.0285 34.0067 32.0285 33.3213V31.8319C32.0285 31.1465 31.6088 30.5908 31.091 30.5908H26.966C26.4482 30.5908 26.0285 31.1465 26.0285 31.8319Z" fill="#023DFE" fill-opacity="0.5"/>
|
||||
<path d="M19.0691 0H20.9308V40H19.0691V0Z" fill="#D1D1D1"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
15
assets/icons/emptySchedule.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.5419 20.2425C11.1182 19.3486 10.0545 18.9679 9.16647 19.3936L1.38334 23.1269C0.591819 23.3099 0 24.0198 0 24.8727V33.8466C0 34.8381 0.797668 35.6414 1.78229 35.6414C2.76668 35.6414 3.56412 34.8381 3.56412 33.8466V27.6645L9.16647 30.3517C9.41376 30.4705 9.67449 30.5264 9.93112 30.5264C10.5974 30.5264 11.2359 30.1484 11.5419 29.5026C11.9643 28.6076 11.5875 27.5367 10.699 27.1104L6.03295 24.8725L10.699 22.6347C11.5877 22.2085 11.9643 21.1376 11.5419 20.2425Z" fill="#D5D5D5"/>
|
||||
<path d="M20.6084 17.6893C20.8655 17.6893 21.1257 17.6334 21.3737 17.5145L32.0481 12.395C32.9366 11.9684 33.3141 10.8973 32.891 10.0024C32.4682 9.10855 31.4052 8.72804 30.5156 9.1535L19.8408 14.2735C18.9522 14.6999 18.5754 15.7705 18.998 16.6657C19.3038 17.3111 19.9423 17.6893 20.6084 17.6893Z" fill="#D5D5D5"/>
|
||||
<path d="M42.7227 7.27445L48.1841 4.65449V10.9114C48.1841 11.9027 48.9818 12.7061 49.9659 12.7061C50.9503 12.7061 51.7478 11.9027 51.7478 10.9114V4.62192L57.2779 7.27445C57.5259 7.39303 57.7862 7.44922 58.0433 7.44922C58.7091 7.44922 59.3478 7.07124 59.6534 6.42537C60.0763 5.53041 59.6994 4.45953 58.8106 4.03316L50.7663 0.174427C50.2813 -0.0581424 49.7189 -0.0581424 49.2338 0.174427L41.19 4.03316C40.3015 4.45953 39.9244 5.53018 40.3472 6.42537C40.771 7.32101 41.8362 7.70198 42.7227 7.27445Z" fill="#D5D5D5"/>
|
||||
<path d="M67.9533 12.3945L78.6281 17.5147C78.875 17.6335 79.1361 17.6895 79.3932 17.6895C80.059 17.6895 80.6978 17.3115 81.0034 16.6656C81.426 15.7707 81.0494 14.6998 80.1606 14.2734L69.4856 9.15299C68.5982 8.7273 67.5345 9.10735 67.1105 10.0018C66.6875 10.8968 67.0645 11.9677 67.9533 12.3945Z" fill="#D5D5D5"/>
|
||||
<path d="M81.539 34.8098C82.4275 34.3836 82.8046 33.3128 82.3815 32.4176C81.9587 31.5231 80.8946 31.1423 80.0065 31.5694L73.167 34.8508L51.7477 24.577V16.5765C51.7477 15.5845 50.9503 14.7817 49.9659 14.7817C48.9817 14.7817 48.1841 15.5847 48.1841 16.5765V24.6095L26.8346 34.8499L20.6999 31.9073C19.8093 31.4809 18.7482 31.8612 18.3244 32.7563C17.9016 33.6513 18.2787 34.7224 19.1672 35.1488L25.8031 38.3318V61.6684L19.0593 64.903C18.1707 65.3294 17.7939 66.4 18.2165 67.2955C18.5221 67.9411 19.1606 68.3191 19.8266 68.3191C20.0837 68.3191 20.3447 68.2631 20.5917 68.1443L26.8349 65.1498L48.1841 75.3902V81.5074C48.1841 82.4992 48.9817 83.3022 49.9659 83.3022C50.9503 83.3022 51.7477 82.4989 51.7477 81.5074V75.4232L73.1657 65.15L79.6524 68.2615C79.9002 68.3801 80.1607 68.4363 80.4175 68.4363C81.0834 68.4363 81.7221 68.0583 82.0277 67.4124C82.4508 66.5175 82.0735 65.4466 81.1849 65.02L74.1977 61.6686V38.3318L81.539 34.8098ZM70.634 62.3879L51.7479 71.4466V49.0993L70.634 40.0401V62.3879ZM29.4788 61.8938C29.4472 61.8269 29.4053 61.7691 29.3668 61.708V40.0403L48.1841 49.0663V71.414L29.6409 62.5197C29.6239 62.3085 29.5745 62.0963 29.4788 61.8938ZM50.0003 27.715L69.0207 36.8382L50.0003 45.961L30.9799 36.8382L50.0003 27.715Z" fill="#D5D5D5"/>
|
||||
<path d="M78.6271 82.4859L67.9525 87.6061C67.0639 88.0327 66.6864 89.1036 67.1095 89.9979C67.4153 90.644 68.054 91.0215 68.7199 91.0215C68.9767 91.0215 69.2379 90.9655 69.485 90.8477L80.1598 85.7272C81.0483 85.3009 81.4252 84.2295 81.0025 83.3346C80.579 82.4401 79.5149 82.0616 78.6271 82.4859Z" fill="#D5D5D5"/>
|
||||
<path d="M57.2782 92.7267L51.748 95.3794V88.2271C51.748 87.2358 50.9506 86.4324 49.9662 86.4324C48.982 86.4324 48.1844 87.2358 48.1844 88.2271V95.3469L42.723 92.7267C41.8335 92.3024 40.7713 92.6808 40.3475 93.576C39.9246 94.4709 40.3015 95.5414 41.1903 95.9677L49.2346 99.826C49.4769 99.9418 49.7387 100 50.0008 100C50.2629 100 50.5248 99.9418 50.7671 99.826L58.8114 95.9677C59.6999 95.5414 60.077 94.4709 59.6541 93.576C59.2304 92.6808 58.166 92.3033 57.2782 92.7267Z" fill="#D5D5D5"/>
|
||||
<path d="M32.0476 87.6063L21.3728 82.4861C20.4863 82.0606 19.4217 82.4395 18.9975 83.3345C18.5749 84.2294 18.9517 85.3008 19.8403 85.7271L30.5153 90.8476C30.7622 90.9654 31.0233 91.0214 31.2802 91.0214C31.946 91.0214 32.5847 90.6439 32.8903 89.9978C33.3132 89.1037 32.9361 88.0329 32.0476 87.6063Z" fill="#D5D5D5"/>
|
||||
<path d="M11.5419 70.4973C11.1182 69.6028 10.0545 69.2239 9.16647 69.6487L3.56412 72.3354V65.6177C3.56412 64.6264 2.76668 63.823 1.78229 63.823C0.797668 63.823 0 64.6264 0 65.6177V74.5918C0 74.8162 0.0457694 75.0283 0.120686 75.2262C0.156892 75.8783 0.53193 76.465 1.12215 76.7485L9.16647 80.6072C9.41376 80.7254 9.67449 80.7818 9.93112 80.7818C10.5974 80.7818 11.2359 80.4038 11.5419 79.7581C11.9643 78.8632 11.5875 77.7923 10.699 77.3655L6.03295 75.1281L10.699 72.8902C11.5877 72.4636 11.9643 71.3923 11.5419 70.4973Z" fill="#D5D5D5"/>
|
||||
<path d="M1.78229 56.8224C2.76668 56.8224 3.56412 56.0192 3.56412 55.0273V44.4373C3.56412 43.446 2.76668 42.6426 1.78229 42.6426C0.797668 42.6426 0 43.446 0 44.4373V55.0273C0.00022771 56.0192 0.797896 56.8224 1.78229 56.8224Z" fill="#D5D5D5"/>
|
||||
<path d="M98.6173 23.1267L90.8342 19.3935C89.9443 18.9666 88.8825 19.3478 88.4589 20.2423C88.0363 21.1377 88.4132 22.2084 89.3017 22.635L93.9677 24.8729L89.3017 27.1107C88.4132 27.5371 88.0363 28.6077 88.4589 29.5029C88.7645 30.1488 89.4032 30.5268 90.0691 30.5268C90.3262 30.5268 90.5873 30.4708 90.8342 30.352L96.4365 27.6648V33.847C96.4365 34.8385 97.234 35.6417 98.2184 35.6417C99.2027 35.6417 100 34.8385 100 33.847V24.8731C100 24.0196 99.4088 23.3098 98.6173 23.1267Z" fill="#D5D5D5"/>
|
||||
<path d="M98.2184 42.6421C97.2337 42.6421 96.4365 43.4455 96.4365 44.4368V55.0268C96.4365 56.0187 97.234 56.8215 98.2184 56.8215C99.2027 56.8215 100 56.0185 100 55.0268V44.4368C100 43.4455 99.203 42.6421 98.2184 42.6421Z" fill="#D5D5D5"/>
|
||||
<path d="M98.2184 63.8228C97.2337 63.8228 96.4365 64.6262 96.4365 65.6175V72.3352L90.8342 69.6485C89.9443 69.223 88.8825 69.6021 88.4589 70.4971C88.0363 71.392 88.4132 72.4634 89.3017 72.8898L93.9677 75.1276L89.3017 77.365C88.4132 77.7918 88.0363 78.8627 88.4589 79.7577C88.7645 80.4033 89.4032 80.7813 90.0691 80.7813C90.3262 80.7813 90.5873 80.7249 90.8342 80.6068L98.8785 76.748C99.4687 76.4645 99.8438 75.8778 99.88 75.2258C99.9546 75.0278 100 74.8157 100 74.5914V65.6173C100 64.6262 99.203 63.8228 98.2184 63.8228Z" fill="#D5D5D5"/>
|
||||
</svg>
|
After Width: | Height: | Size: 6.0 KiB |
14
assets/icons/success-white.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group 284">
|
||||
<g id="Group">
|
||||
<g id="Group_2">
|
||||
<path id="Vector" d="M37.8033 16.3571C37.2313 15.7851 36.3039 15.785 35.7318 16.3572L21.5532 30.5356L14.2681 23.2508C13.6962 22.6788 12.7686 22.6787 12.1966 23.2509C11.6246 23.8229 11.6246 24.7504 12.1966 25.3224L20.5174 33.643C20.8034 33.9291 21.1783 34.072 21.5531 34.072C21.928 34.072 22.3029 33.9291 22.5888 33.6429L37.8033 18.4287C38.3754 17.8567 38.3754 16.9292 37.8033 16.3571Z" fill="white"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Group_3">
|
||||
<g id="Group_4">
|
||||
<path id="Vector_2" d="M42.6776 7.32236C37.9558 2.60049 31.6776 0 25 0C18.3223 0 12.0442 2.60049 7.32236 7.32236C2.60039 12.0443 0 18.3224 0 25C0 31.6778 2.60039 37.9559 7.32236 42.6777C12.0441 47.3996 18.3223 50 25 50C31.6777 50 37.9558 47.3996 42.6776 42.6777C47.3995 37.9559 50 31.6778 50 25C50 18.3224 47.3995 12.0443 42.6776 7.32236ZM25 47.0703C12.8304 47.0703 2.92969 37.1696 2.92969 25C2.92969 12.8304 12.8304 2.92969 25 2.92969C37.1696 2.92969 47.0703 12.8304 47.0703 25C47.0703 37.1696 37.1696 47.0703 25 47.0703Z" fill="white"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
116
ios/Podfile.lock
@ -1,46 +1,54 @@
|
||||
PODS:
|
||||
- device_info_plus (0.0.1):
|
||||
- Flutter
|
||||
- Firebase/Analytics (10.20.0):
|
||||
- Firebase/Analytics (10.25.0):
|
||||
- Firebase/Core
|
||||
- Firebase/Core (10.20.0):
|
||||
- Firebase/Core (10.25.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseAnalytics (~> 10.20.0)
|
||||
- Firebase/CoreOnly (10.20.0):
|
||||
- FirebaseCore (= 10.20.0)
|
||||
- Firebase/Crashlytics (10.20.0):
|
||||
- FirebaseAnalytics (~> 10.25.0)
|
||||
- Firebase/CoreOnly (10.25.0):
|
||||
- FirebaseCore (= 10.25.0)
|
||||
- Firebase/Crashlytics (10.25.0):
|
||||
- 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.20.0)
|
||||
- Firebase/Analytics (= 10.25.0)
|
||||
- firebase_core
|
||||
- Flutter
|
||||
- firebase_core (2.25.5):
|
||||
- Firebase/CoreOnly (= 10.20.0)
|
||||
- firebase_core (2.32.0):
|
||||
- Firebase/CoreOnly (= 10.25.0)
|
||||
- Flutter
|
||||
- firebase_crashlytics (3.4.16):
|
||||
- Firebase/Crashlytics (= 10.20.0)
|
||||
- Firebase/Crashlytics (= 10.25.0)
|
||||
- firebase_core
|
||||
- Flutter
|
||||
- FirebaseAnalytics (10.20.0):
|
||||
- FirebaseAnalytics/AdIdSupport (= 10.20.0)
|
||||
- firebase_database (10.5.7):
|
||||
- Firebase/Database (= 10.25.0)
|
||||
- firebase_core
|
||||
- Flutter
|
||||
- FirebaseAnalytics (10.25.0):
|
||||
- FirebaseAnalytics/AdIdSupport (= 10.25.0)
|
||||
- FirebaseCore (~> 10.0)
|
||||
- FirebaseInstallations (~> 10.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
||||
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
||||
- GoogleUtilities/Network (~> 7.11)
|
||||
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||
- FirebaseAnalytics/AdIdSupport (10.20.0):
|
||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
||||
- FirebaseAnalytics/AdIdSupport (10.25.0):
|
||||
- FirebaseCore (~> 10.0)
|
||||
- FirebaseInstallations (~> 10.0)
|
||||
- GoogleAppMeasurement (= 10.20.0)
|
||||
- GoogleAppMeasurement (= 10.25.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
||||
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
||||
- GoogleUtilities/Network (~> 7.11)
|
||||
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||
- FirebaseCore (10.20.0):
|
||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
||||
- FirebaseAppCheckInterop (10.29.0)
|
||||
- FirebaseCore (10.25.0):
|
||||
- FirebaseCoreInternal (~> 10.0)
|
||||
- GoogleUtilities/Environment (~> 7.12)
|
||||
- GoogleUtilities/Logger (~> 7.12)
|
||||
@ -48,19 +56,27 @@ PODS:
|
||||
- FirebaseCore (~> 10.0)
|
||||
- FirebaseCoreInternal (10.29.0):
|
||||
- "GoogleUtilities/NSData+zlib (~> 7.8)"
|
||||
- FirebaseCrashlytics (10.20.0):
|
||||
- FirebaseCrashlytics (10.25.0):
|
||||
- FirebaseCore (~> 10.5)
|
||||
- FirebaseInstallations (~> 10.0)
|
||||
- FirebaseRemoteConfigInterop (~> 10.23)
|
||||
- FirebaseSessions (~> 10.5)
|
||||
- GoogleDataTransport (~> 9.2)
|
||||
- GoogleUtilities/Environment (~> 7.8)
|
||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
||||
- 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):
|
||||
- FirebaseCore (~> 10.0)
|
||||
- GoogleUtilities/Environment (~> 7.8)
|
||||
- GoogleUtilities/UserDefaults (~> 7.8)
|
||||
- PromisesObjC (~> 2.1)
|
||||
- FirebaseRemoteConfigInterop (10.29.0)
|
||||
- FirebaseSessions (10.29.0):
|
||||
- FirebaseCore (~> 10.5)
|
||||
- FirebaseCoreExtension (~> 10.0)
|
||||
@ -70,31 +86,32 @@ PODS:
|
||||
- GoogleUtilities/UserDefaults (~> 7.13)
|
||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
||||
- PromisesSwift (~> 2.1)
|
||||
- FirebaseSharedSwift (10.29.0)
|
||||
- Flutter (1.0.0)
|
||||
- flutter_localization (0.0.1):
|
||||
- Flutter
|
||||
- flutter_secure_storage (6.0.0):
|
||||
- Flutter
|
||||
- GoogleAppMeasurement (10.20.0):
|
||||
- GoogleAppMeasurement/AdIdSupport (= 10.20.0)
|
||||
- GoogleAppMeasurement (10.25.0):
|
||||
- GoogleAppMeasurement/AdIdSupport (= 10.25.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
||||
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
||||
- GoogleUtilities/Network (~> 7.11)
|
||||
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||
- GoogleAppMeasurement/AdIdSupport (10.20.0):
|
||||
- GoogleAppMeasurement/WithoutAdIdSupport (= 10.20.0)
|
||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
||||
- GoogleAppMeasurement/AdIdSupport (10.25.0):
|
||||
- GoogleAppMeasurement/WithoutAdIdSupport (= 10.25.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
||||
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
||||
- GoogleUtilities/Network (~> 7.11)
|
||||
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||
- GoogleAppMeasurement/WithoutAdIdSupport (10.20.0):
|
||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
||||
- GoogleAppMeasurement/WithoutAdIdSupport (10.25.0):
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
||||
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
||||
- GoogleUtilities/Network (~> 7.11)
|
||||
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
||||
- GoogleDataTransport (9.4.1):
|
||||
- GoogleUtilities/Environment (~> 7.7)
|
||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
||||
@ -129,11 +146,12 @@ PODS:
|
||||
- GoogleUtilities/Privacy
|
||||
- image_picker_ios (0.0.1):
|
||||
- Flutter
|
||||
- nanopb (2.30909.1):
|
||||
- nanopb/decode (= 2.30909.1)
|
||||
- nanopb/encode (= 2.30909.1)
|
||||
- nanopb/decode (2.30909.1)
|
||||
- nanopb/encode (2.30909.1)
|
||||
- leveldb-library (1.22.5)
|
||||
- nanopb (2.30910.0):
|
||||
- nanopb/decode (= 2.30910.0)
|
||||
- nanopb/encode (= 2.30910.0)
|
||||
- nanopb/decode (2.30910.0)
|
||||
- nanopb/encode (2.30910.0)
|
||||
- onesignal_flutter (5.2.0):
|
||||
- Flutter
|
||||
- OneSignalXCFramework (= 5.2.0)
|
||||
@ -207,6 +225,7 @@ DEPENDENCIES:
|
||||
- firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
|
||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||
- firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
|
||||
- firebase_database (from `.symlinks/plugins/firebase_database/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_localization (from `.symlinks/plugins/flutter_localization/ios`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
@ -223,15 +242,20 @@ SPEC REPOS:
|
||||
trunk:
|
||||
- Firebase
|
||||
- FirebaseAnalytics
|
||||
- FirebaseAppCheckInterop
|
||||
- FirebaseCore
|
||||
- FirebaseCoreExtension
|
||||
- FirebaseCoreInternal
|
||||
- FirebaseCrashlytics
|
||||
- FirebaseDatabase
|
||||
- FirebaseInstallations
|
||||
- FirebaseRemoteConfigInterop
|
||||
- FirebaseSessions
|
||||
- FirebaseSharedSwift
|
||||
- GoogleAppMeasurement
|
||||
- GoogleDataTransport
|
||||
- GoogleUtilities
|
||||
- leveldb-library
|
||||
- nanopb
|
||||
- OneSignalXCFramework
|
||||
- PromisesObjC
|
||||
@ -246,6 +270,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/firebase_core/ios"
|
||||
firebase_crashlytics:
|
||||
:path: ".symlinks/plugins/firebase_crashlytics/ios"
|
||||
firebase_database:
|
||||
:path: ".symlinks/plugins/firebase_database/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_localization:
|
||||
@ -271,25 +297,31 @@ EXTERNAL SOURCES:
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
|
||||
Firebase: 10c8cb12fb7ad2ae0c09ffc86cd9c1ab392a0031
|
||||
firebase_analytics: 2c1c3057d5da3bd3aab819f7e6ee153a4e46c59e
|
||||
firebase_core: c8628c7ce80f79439149549052bff22f6784fbf5
|
||||
firebase_crashlytics: 012078b4eec6fc9716f97ba3da0f0e44a04e95b1
|
||||
FirebaseAnalytics: a2731bf3670747ce8f65368b118d18aa8e368246
|
||||
FirebaseCore: 28045c1560a2600d284b9c45a904fe322dc890b6
|
||||
Firebase: 0312a2352584f782ea56f66d91606891d4607f06
|
||||
firebase_analytics: 3a9263fedec72970e6bd30a7132bbdd386de2c14
|
||||
firebase_core: a626d00494efa398e7c54f25f1454a64c8abf197
|
||||
firebase_crashlytics: 0b7cb41f5fb3b6889d0fb408cfce3cc7a4247061
|
||||
firebase_database: 2713033e426b176d4fe5e7195f3d19aa1b549a91
|
||||
FirebaseAnalytics: ec00fe8b93b41dc6fe4a28784b8e51da0647a248
|
||||
FirebaseAppCheckInterop: 6a1757cfd4067d8e00fccd14fcc1b8fd78cfac07
|
||||
FirebaseCore: 7ec4d0484817f12c3373955bc87762d96842d483
|
||||
FirebaseCoreExtension: 705ca5b14bf71d2564a0ddc677df1fc86ffa600f
|
||||
FirebaseCoreInternal: df84dd300b561c27d5571684f389bf60b0a5c934
|
||||
FirebaseCrashlytics: 81530595edb6d99f1918f723a6c33766a24a4c86
|
||||
FirebaseCrashlytics: 4b96efb0ce73b38b2a85e8b8bd1bd8f63f09d015
|
||||
FirebaseDatabase: faa489a42f5f868d23a55dd442d6e2099348458e
|
||||
FirebaseInstallations: 913cf60d0400ebd5d6b63a28b290372ab44590dd
|
||||
FirebaseRemoteConfigInterop: 6efda51fb5e2f15b16585197e26eaa09574e8a4d
|
||||
FirebaseSessions: dbd14adac65ce996228652c1fc3a3f576bdf3ecc
|
||||
FirebaseSharedSwift: 20530f495084b8d840f78a100d8c5ee613375f6e
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_localization: f43b18844a2b3d2c71fd64f04ffd6b1e64dd54d4
|
||||
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
||||
GoogleAppMeasurement: bb3c564c3efb933136af0e94899e0a46167466a8
|
||||
GoogleAppMeasurement: 9abf64b682732fed36da827aa2a68f0221fd2356
|
||||
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
|
||||
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
|
||||
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
||||
nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5
|
||||
leveldb-library: e8eadf9008a61f9e1dde3978c086d2b6d9b9dc28
|
||||
nanopb: 438bc412db1928dac798aa6fd75726007be04262
|
||||
onesignal_flutter: 5ce68a29861960168e81101cb1bd685d264361de
|
||||
OneSignalXCFramework: bdf74fdc06888f9466dc21e826fe1549ed143095
|
||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||
|
@ -514,8 +514,8 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
CURRENT_PROJECT_VERSION = 20;
|
||||
DEVELOPMENT_TEAM = 48V27SBR8J;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Syncrow;
|
||||
@ -524,7 +524,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.0;
|
||||
MARKETING_VERSION = 1.0.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.syncrow.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@ -706,8 +706,8 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
CURRENT_PROJECT_VERSION = 20;
|
||||
DEVELOPMENT_TEAM = 48V27SBR8J;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Syncrow;
|
||||
@ -716,7 +716,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.0;
|
||||
MARKETING_VERSION = 1.0.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.syncrow.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@ -736,8 +736,8 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
CURRENT_PROJECT_VERSION = 20;
|
||||
DEVELOPMENT_TEAM = 48V27SBR8J;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Syncrow;
|
||||
@ -746,7 +746,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.0;
|
||||
MARKETING_VERSION = 1.0.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.syncrow.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
@ -1,115 +1,115 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "logo-20@2x.png",
|
||||
"filename" : "Syncrow Icon-20@2x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-20@3x.png",
|
||||
"filename" : "Syncrow Icon-20@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-29.png",
|
||||
"filename" : "Syncrow Icon-29.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-29@2x.png",
|
||||
"filename" : "Syncrow Icon-29@2x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-29@3x.png",
|
||||
"filename" : "Syncrow Icon-29@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-40@2x.png",
|
||||
"filename" : "Syncrow Icon-40@2x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-40@3x.png",
|
||||
"filename" : "Syncrow Icon-40@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-60@2x.png",
|
||||
"filename" : "Syncrow Icon-60@2x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-60@3x.png",
|
||||
"filename" : "Syncrow Icon-60@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-20.png",
|
||||
"filename" : "Syncrow Icon-20.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-20@2x 1.png",
|
||||
"filename" : "Syncrow Icon-20@2x 1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-29 1.png",
|
||||
"filename" : "Syncrow Icon-29 1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-29@2x 1.png",
|
||||
"filename" : "Syncrow Icon-29@2x 1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-40.png",
|
||||
"filename" : "Syncrow Icon-40.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-40@2x 1.png",
|
||||
"filename" : "Syncrow Icon-40@2x 1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-76.png",
|
||||
"filename" : "Syncrow Icon-76.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-76@2x.png",
|
||||
"filename" : "Syncrow Icon-76@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-83.5@2x.png",
|
||||
"filename" : "Syncrow Icon-83.5@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename" : "logo-1024.png",
|
||||
"filename" : "Syncrow Icon-1024.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
|
After Width: | Height: | Size: 321 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 179 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 11 KiB |
@ -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/dashboard/view/dashboard_view.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/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/menu/view/menu_view.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/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/model/create_automation_model.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/generated/assets.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/profile_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/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/constants.dart';
|
||||
|
||||
part 'home_state.dart';
|
||||
|
||||
@ -161,7 +153,7 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
}
|
||||
|
||||
_sendSubscriptionId() async {
|
||||
String? subscriptionId = OneSignal.User.pushSubscription.id ?? '';
|
||||
// String? subscriptionId = OneSignal.User.pushSubscription.id ?? '';
|
||||
//TODO send the subscription id to BE
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
@ -59,19 +58,39 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
|
||||
/////////////////////////////////////VALIDATORS/////////////////////////////////////
|
||||
String? passwordValidator(String? value) {
|
||||
if (value != null) {
|
||||
if (value.isEmpty) {
|
||||
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';
|
||||
}
|
||||
}
|
||||
List<String> errors = [];
|
||||
|
||||
if (value == null || value.isEmpty) {
|
||||
errors.add("Please enter your password");
|
||||
}
|
||||
return null;
|
||||
|
||||
if (value != null && value.length < 8) {
|
||||
errors.add('Password must be at least 8 characters long');
|
||||
}
|
||||
|
||||
if (value != null && !RegExp(r'[a-z]').hasMatch(value)) {
|
||||
errors.add('Password must contain at least one lowercase letter');
|
||||
}
|
||||
|
||||
if (value != null && !RegExp(r'[A-Z]').hasMatch(value)) {
|
||||
errors.add('Password must contain at least one uppercase letter');
|
||||
}
|
||||
|
||||
if (value != null && !RegExp(r'\d').hasMatch(value)) {
|
||||
errors.add('Password must contain at least one number');
|
||||
}
|
||||
|
||||
if (value != null &&
|
||||
!RegExp(r'[!"#$%&()*+,-./:;<=>?@[\]^_`{|}~]').hasMatch(value)) {
|
||||
errors.add('Password must contain at least one special character');
|
||||
}
|
||||
|
||||
if (errors.isNotEmpty) {
|
||||
return errors
|
||||
.join('\n'); // Join the errors with new lines to show all at once
|
||||
}
|
||||
|
||||
return null; // No errors
|
||||
}
|
||||
|
||||
String? fullNameValidator(String? value) {
|
||||
@ -182,13 +201,10 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
debugPrint('token: ${token.accessToken}');
|
||||
FlutterSecureStorage storage = const FlutterSecureStorage();
|
||||
await storage.write(
|
||||
key: Token.loginAccessTokenKey,
|
||||
value: token.accessToken
|
||||
);
|
||||
key: Token.loginAccessTokenKey, value: token.accessToken);
|
||||
const FlutterSecureStorage().write(
|
||||
key: UserModel.userUuidKey,
|
||||
value: Token.decodeToken(token.accessToken)['uuid'].toString()
|
||||
);
|
||||
value: Token.decodeToken(token.accessToken)['uuid'].toString());
|
||||
user = UserModel.fromToken(token);
|
||||
emailController.clear();
|
||||
passwordController.clear();
|
||||
@ -225,20 +241,24 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
sendOtp() async {
|
||||
try {
|
||||
emit(AuthLoading());
|
||||
await AuthenticationAPI.sendOtp(body: {'email': email, 'type': 'VERIFICATION'});
|
||||
await AuthenticationAPI.sendOtp(
|
||||
body: {'email': email, 'type': 'VERIFICATION'});
|
||||
emit(AuthSignUpSuccess());
|
||||
} catch (_) {
|
||||
emit(AuthLoginError(message: 'Something went wrong'));
|
||||
}
|
||||
}
|
||||
|
||||
reSendOtp() async {
|
||||
Future<bool> reSendOtp() async {
|
||||
try {
|
||||
emit(AuthLoading());
|
||||
await AuthenticationAPI.sendOtp(body: {'email': email, 'type': 'VERIFICATION'});
|
||||
await AuthenticationAPI.sendOtp(
|
||||
body: {'email': email, 'type': 'VERIFICATION'});
|
||||
emit(ResendOtpSuccess());
|
||||
return true;
|
||||
} catch (_) {
|
||||
emit(AuthLoginError(message: 'Something went wrong'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,13 +302,16 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
try {
|
||||
emit(AuthTokenLoading());
|
||||
const storage = FlutterSecureStorage();
|
||||
final firstLaunch = await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true;
|
||||
final firstLaunch = await SharedPreferencesHelper.readBoolFromSP(
|
||||
StringsManager.firstLaunch) ??
|
||||
true;
|
||||
|
||||
if (firstLaunch) {
|
||||
storage.deleteAll();
|
||||
}
|
||||
|
||||
await SharedPreferencesHelper.saveBoolToSP(StringsManager.firstLaunch, false);
|
||||
await SharedPreferencesHelper.saveBoolToSP(
|
||||
StringsManager.firstLaunch, false);
|
||||
|
||||
final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
|
||||
if (value.isEmpty) {
|
||||
@ -315,7 +338,6 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sendToForgetPassword({required String password}) async {
|
||||
try {
|
||||
emit(AuthForgetPassLoading());
|
||||
@ -325,8 +347,4 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
emit(AuthForgetPassError(message: 'Something went wrong'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import 'package:pin_code_fields/pin_code_fields.dart';
|
||||
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
|
||||
import 'package:syncrow_app/features/auth/view/create_new_password.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_button.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/success_dialog.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/navigation/routing_constants.dart';
|
||||
@ -40,13 +41,19 @@ class _OtpViewState extends State<OtpView> {
|
||||
_lifecycleEventHandler = LifecycleEventHandler(
|
||||
resumeCallBack: () async {
|
||||
SharedPreferencesHelper.saveBoolToSP('timeStampSaved', false);
|
||||
String timeStampInBackground = await SharedPreferencesHelper.readStringFromSP('timeStamp');
|
||||
int savedCounter = await SharedPreferencesHelper.readIntFromSP('savedCounter');
|
||||
String timeStampInBackground =
|
||||
await SharedPreferencesHelper.readStringFromSP('timeStamp');
|
||||
int savedCounter =
|
||||
await SharedPreferencesHelper.readIntFromSP('savedCounter');
|
||||
DateTime currentTime = DateTime.now();
|
||||
int differenceInSeconds = timeStampInBackground.isNotEmpty
|
||||
? currentTime.difference(DateTime.parse(timeStampInBackground)).inSeconds
|
||||
? currentTime
|
||||
.difference(DateTime.parse(timeStampInBackground))
|
||||
.inSeconds
|
||||
: 0;
|
||||
remainingSec = differenceInSeconds > savedCounter ? 0 : savedCounter - differenceInSeconds;
|
||||
remainingSec = differenceInSeconds > savedCounter
|
||||
? 0
|
||||
: savedCounter - differenceInSeconds;
|
||||
timerStarted = true;
|
||||
startTimer(remainingSec ?? 0);
|
||||
return;
|
||||
@ -75,7 +82,8 @@ class _OtpViewState extends State<OtpView> {
|
||||
}
|
||||
|
||||
handleTimerOnBackground() async {
|
||||
bool timeStampSaved = await SharedPreferencesHelper.readBoolFromSP('timeStampSaved') ?? false;
|
||||
bool timeStampSaved =
|
||||
await SharedPreferencesHelper.readBoolFromSP('timeStampSaved') ?? false;
|
||||
if (!timeStampSaved) {
|
||||
final dateInString = DateTime.now().toString();
|
||||
SharedPreferencesHelper.saveIntToSP('savedCounter', remainingSec ?? 0);
|
||||
@ -115,7 +123,8 @@ class _OtpViewState extends State<OtpView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String maskedEmail = AuthCubit.get(context).maskEmail(AuthCubit.get(context).email);
|
||||
String maskedEmail =
|
||||
AuthCubit.get(context).maskEmail(AuthCubit.get(context).email);
|
||||
return BlocConsumer<AuthCubit, AuthState>(
|
||||
listener: (context, state) {
|
||||
if (state is AuthOtpSuccess) {
|
||||
@ -199,7 +208,10 @@ class _OtpViewState extends State<OtpView> {
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: 'We have sent the verification code to',
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall!
|
||||
.copyWith(
|
||||
color: Colors.white,
|
||||
fontWeight: FontsManager.regular,
|
||||
fontSize: 14,
|
||||
@ -207,7 +219,10 @@ class _OtpViewState extends State<OtpView> {
|
||||
children: [
|
||||
TextSpan(
|
||||
text: ' $maskedEmail',
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall!
|
||||
.copyWith(
|
||||
color: Colors.black,
|
||||
fontWeight: FontsManager.bold,
|
||||
fontSize: 14,
|
||||
@ -215,7 +230,10 @@ class _OtpViewState extends State<OtpView> {
|
||||
),
|
||||
TextSpan(
|
||||
text: ' change email?',
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall!
|
||||
.copyWith(
|
||||
color: const Color(0xFF87C7FF),
|
||||
fontWeight: FontsManager.regular,
|
||||
fontSize: 14,
|
||||
@ -239,7 +257,8 @@ class _OtpViewState extends State<OtpView> {
|
||||
keyboardType: TextInputType.number,
|
||||
autoFocus: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
animationDuration: const Duration(milliseconds: 30),
|
||||
animationDuration:
|
||||
const Duration(milliseconds: 30),
|
||||
beforeTextPaste: (text) {
|
||||
// Allow pasting only if all characters are numeric
|
||||
return int.tryParse(text!) != null;
|
||||
@ -262,18 +281,27 @@ class _OtpViewState extends State<OtpView> {
|
||||
errorBorderWidth: 1,
|
||||
borderWidth: 1,
|
||||
errorBorderColor: Colors.red,
|
||||
activeColor: state is AuthLoginError ? Colors.red : Colors.white,
|
||||
inactiveColor:
|
||||
state is AuthLoginError ? Colors.red : Colors.white,
|
||||
activeFillColor:
|
||||
state is AuthLoginError ? Colors.red : Colors.white,
|
||||
inactiveFillColor:
|
||||
state is AuthLoginError ? Colors.red : Colors.white,
|
||||
selectedFillColor:
|
||||
state is AuthLoginError ? Colors.red : Colors.white,
|
||||
activeColor: state is AuthLoginError
|
||||
? Colors.red
|
||||
: Colors.white,
|
||||
inactiveColor: state is AuthLoginError
|
||||
? Colors.red
|
||||
: Colors.white,
|
||||
activeFillColor: state is AuthLoginError
|
||||
? Colors.red
|
||||
: Colors.white,
|
||||
inactiveFillColor: state is AuthLoginError
|
||||
? Colors.red
|
||||
: Colors.white,
|
||||
selectedFillColor: state is AuthLoginError
|
||||
? Colors.red
|
||||
: Colors.white,
|
||||
disabledColor: Colors.white,
|
||||
fieldHeight: 56,
|
||||
fieldWidth: MediaQuery.sizeOf(context).width > 340 ? 40 : 20,
|
||||
fieldWidth:
|
||||
MediaQuery.sizeOf(context).width > 340
|
||||
? 40
|
||||
: 20,
|
||||
// fieldWidth: 40,
|
||||
selectedColor: Colors.white,
|
||||
shape: PinCodeFieldShape.box,
|
||||
@ -295,10 +323,12 @@ class _OtpViewState extends State<OtpView> {
|
||||
isDone: state is AuthLoginSuccess,
|
||||
isLoading: state is AuthLoading,
|
||||
customButtonStyle: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
backgroundColor:
|
||||
MaterialStateProperty.all(
|
||||
Colors.black.withOpacity(.25),
|
||||
),
|
||||
foregroundColor: MaterialStateProperty.all(
|
||||
foregroundColor:
|
||||
MaterialStateProperty.all(
|
||||
Colors.white,
|
||||
),
|
||||
),
|
||||
@ -307,7 +337,8 @@ class _OtpViewState extends State<OtpView> {
|
||||
),
|
||||
onPressed: () {
|
||||
if ((state is! AuthLoading)) {
|
||||
AuthCubit.get(context).verifyOtp(widget.isForgetPage);
|
||||
AuthCubit.get(context)
|
||||
.verifyOtp(widget.isForgetPage);
|
||||
FocusScope.of(context).unfocus();
|
||||
}
|
||||
},
|
||||
@ -321,10 +352,12 @@ class _OtpViewState extends State<OtpView> {
|
||||
isDone: state is AuthLoginSuccess,
|
||||
isLoading: state is AuthLoading,
|
||||
customButtonStyle: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
backgroundColor:
|
||||
MaterialStateProperty.all(
|
||||
Colors.black.withOpacity(.25),
|
||||
),
|
||||
foregroundColor: MaterialStateProperty.all(
|
||||
foregroundColor:
|
||||
MaterialStateProperty.all(
|
||||
Colors.white,
|
||||
),
|
||||
),
|
||||
@ -341,8 +374,20 @@ class _OtpViewState extends State<OtpView> {
|
||||
return;
|
||||
}
|
||||
if ((state is! AuthLoading)) {
|
||||
await AuthCubit.get(context).reSendOtp();
|
||||
bool success = await AuthCubit.get(context)
|
||||
.reSendOtp();
|
||||
FocusScope.of(context).unfocus();
|
||||
if(success){
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => SuccessDialog(
|
||||
key: ValueKey('SuccessDialog'),
|
||||
message: 'New OTP sent!',));
|
||||
}
|
||||
Future.delayed(Duration(seconds: 2),
|
||||
() {
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
|
@ -105,8 +105,8 @@ class SignUpView extends StatelessWidget {
|
||||
autocorrect: false,
|
||||
autofillHints: const [AutofillHints.name],
|
||||
// controller: AuthCubit.get(context).fullNameController,
|
||||
validator: (value) =>
|
||||
AuthCubit.get(context).fullNameValidator(value),
|
||||
validator: (value) => AuthCubit.get(context)
|
||||
.fullNameValidator(value),
|
||||
onTapOutside: (event) {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
@ -114,7 +114,11 @@ class SignUpView extends StatelessWidget {
|
||||
AuthCubit.get(context).fullName = value;
|
||||
},
|
||||
onTap: () {},
|
||||
decoration: defaultInputDecoration(context, hint: "Full Name"),
|
||||
decoration: defaultInputDecoration(context,
|
||||
hint: "Full Name")
|
||||
.copyWith(
|
||||
errorMaxLines: 2,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const BodyMedium(
|
||||
@ -129,15 +133,16 @@ class SignUpView extends StatelessWidget {
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
autofillHints: const [AutofillHints.email],
|
||||
validator: AuthCubit.get(context).emailAddressValidator,
|
||||
validator: AuthCubit.get(context)
|
||||
.emailAddressValidator,
|
||||
onTapOutside: (event) {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
onChanged: (value) {
|
||||
AuthCubit.get(context).email = value;
|
||||
},
|
||||
decoration:
|
||||
defaultInputDecoration(context, hint: "Example@email.com"),
|
||||
decoration: defaultInputDecoration(context,
|
||||
hint: "Example@email.com"),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const BodyMedium(
|
||||
@ -150,17 +155,25 @@ class SignUpView extends StatelessWidget {
|
||||
keyboardType: TextInputType.text,
|
||||
scrollPadding: EdgeInsets.zero,
|
||||
autocorrect: false,
|
||||
autofillHints: const [AutofillHints.password],
|
||||
validator: AuthCubit.get(context).passwordValidator,
|
||||
autofillHints: const [
|
||||
AutofillHints.password
|
||||
],
|
||||
validator: AuthCubit.get(context)
|
||||
.passwordValidator,
|
||||
onChanged: (value) {
|
||||
AuthCubit.get(context).signUpPassword = value;
|
||||
AuthCubit.get(context).signUpPassword =
|
||||
value;
|
||||
},
|
||||
onTapOutside: (event) {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
obscureText: !AuthCubit.get(context).isPasswordVisible,
|
||||
obscureText: !AuthCubit.get(context)
|
||||
.isPasswordVisible,
|
||||
decoration: defaultInputDecoration(context,
|
||||
hint: "At least 8 characters"),
|
||||
hint: "At least 8 characters")
|
||||
.copyWith(
|
||||
errorMaxLines: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const BodyMedium(
|
||||
@ -174,13 +187,17 @@ class SignUpView extends StatelessWidget {
|
||||
scrollPadding: EdgeInsets.zero,
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
autofillHints: const [AutofillHints.password],
|
||||
autofillHints: const [
|
||||
AutofillHints.password
|
||||
],
|
||||
onChanged: (value) {},
|
||||
validator: AuthCubit.get(context).reEnterPasswordCheck,
|
||||
validator: AuthCubit.get(context)
|
||||
.reEnterPasswordCheck,
|
||||
onTapOutside: (event) {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
obscureText: !AuthCubit.get(context).isPasswordVisible,
|
||||
obscureText: !AuthCubit.get(context)
|
||||
.isPasswordVisible,
|
||||
decoration: defaultInputDecoration(context,
|
||||
hint: "At least 8 characters"),
|
||||
),
|
||||
@ -193,10 +210,12 @@ class SignUpView extends StatelessWidget {
|
||||
isDone: state is AuthLoginSuccess,
|
||||
isLoading: state is AuthLoading,
|
||||
customButtonStyle: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
backgroundColor:
|
||||
MaterialStateProperty.all(
|
||||
Colors.black.withOpacity(.25),
|
||||
),
|
||||
foregroundColor: MaterialStateProperty.all(
|
||||
foregroundColor:
|
||||
MaterialStateProperty.all(
|
||||
Colors.white,
|
||||
),
|
||||
),
|
||||
@ -204,11 +223,14 @@ class SignUpView extends StatelessWidget {
|
||||
'Sign up',
|
||||
),
|
||||
onPressed: () {
|
||||
AuthCubit.get(context).showValidationMessage = true;
|
||||
if (formKey.currentState!.validate()) {
|
||||
AuthCubit.get(context)
|
||||
.showValidationMessage = true;
|
||||
if (formKey.currentState!
|
||||
.validate()) {
|
||||
if ((state is! AuthLoading)) {
|
||||
AuthCubit.get(context).signUp();
|
||||
FocusScope.of(context).unfocus();
|
||||
FocusScope.of(context)
|
||||
.unfocus();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_event.dart';
|
||||
@ -40,6 +40,7 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
on<ChangeAllSwitch>(_changeAllAcSwitch);
|
||||
on<IncreaseAllTemp>(_increaseAllTemp);
|
||||
on<DecreaseAllTemp>(_decreaseAllTemp);
|
||||
on<AcUpdated>(_onAcUpdated);
|
||||
}
|
||||
|
||||
void _fetchAcsStatus(AcsInitial event, Emitter<AcsState> emit) async {
|
||||
@ -62,6 +63,8 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
}
|
||||
deviceStatus = AcStatusModel.fromJson(response['productUuid'], statusModelList);
|
||||
emit(GetAcStatusState(acStatusModel: deviceStatus));
|
||||
Future.delayed(const Duration(milliseconds: 500));
|
||||
_listenToChanges();
|
||||
}
|
||||
} catch (e) {
|
||||
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 {
|
||||
deviceStatusList = [];
|
||||
devicesList = [];
|
||||
@ -164,7 +190,7 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
deviceStatus.childLock = lockValue;
|
||||
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 {
|
||||
|
@ -20,6 +20,8 @@ class AcSwitch extends AcsEvent {
|
||||
List<Object> get props => [acSwitch, deviceId, productId];
|
||||
}
|
||||
|
||||
class AcUpdated extends AcsEvent {}
|
||||
|
||||
class AcsInitial extends AcsEvent {
|
||||
final bool allAcs;
|
||||
const AcsInitial({required this.allAcs});
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:firebase_database/firebase_database.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_state.dart';
|
||||
@ -15,6 +16,7 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
||||
CeilingSensorBloc({required this.deviceId}) : super(InitialState()) {
|
||||
on<InitialEvent>(_fetchCeilingSensorStatus);
|
||||
on<ChangeValueEvent>(_changeValue);
|
||||
on<CeilingSensorUpdated>(_onCeilingSensorUpdated);
|
||||
}
|
||||
|
||||
void _fetchCeilingSensorStatus(InitialEvent event, Emitter<CeilingSensorState> emit) async {
|
||||
@ -27,12 +29,36 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
||||
}
|
||||
deviceStatus = CeilingSensorModel.fromJson(statusModelList);
|
||||
emit(UpdateState(ceilingSensorModel: deviceStatus));
|
||||
_listenToChanges();
|
||||
} catch (e) {
|
||||
emit(FailedState(error: 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 = CeilingSensorModel.fromJson(statusList);
|
||||
add(CeilingSensorUpdated());
|
||||
});
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
_onCeilingSensorUpdated(CeilingSensorUpdated event, Emitter<CeilingSensorState> emit) {
|
||||
emit(UpdateState(ceilingSensorModel: deviceStatus));
|
||||
}
|
||||
|
||||
void _changeValue(ChangeValueEvent event, Emitter<CeilingSensorState> emit) async {
|
||||
emit(LoadingNewSate(ceilingSensorModel: deviceStatus));
|
||||
try {
|
||||
|
@ -11,6 +11,8 @@ class LoadingEvent extends CeilingSensorEvent {}
|
||||
|
||||
class InitialEvent extends CeilingSensorEvent {}
|
||||
|
||||
class CeilingSensorUpdated extends CeilingSensorEvent {}
|
||||
|
||||
class ChangeValueEvent extends CeilingSensorEvent {
|
||||
final int value;
|
||||
final String code;
|
||||
|
184
lib/features/devices/bloc/curtain_bloc/curtain_bloc.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
34
lib/features/devices/bloc/curtain_bloc/curtain_event.dart
Normal 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 {}
|
76
lib/features/devices/bloc/curtain_bloc/curtain_state.dart
Normal 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];
|
||||
}
|
@ -87,10 +87,10 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
return const DoorsListView();
|
||||
case DeviceType.Curtain:
|
||||
return const CurtainListView();
|
||||
// case DeviceType.ThreeGang:
|
||||
// return const ThreeGangSwitchesView();
|
||||
// case DeviceType.Gateway:
|
||||
// return const GateWayView();
|
||||
// case DeviceType.ThreeGang:
|
||||
// return const ThreeGangSwitchesView();
|
||||
// case DeviceType.Gateway:
|
||||
// return const GateWayView();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@ -308,10 +308,10 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
|
||||
emitSafe(GetDevicesLoading());
|
||||
int roomIndex =
|
||||
HomeCubit.getInstance().selectedSpace!.rooms!.indexWhere((element) => element.id == roomId);
|
||||
HomeCubit.getInstance().selectedSpace!.rooms!.indexWhere((element) => element.id == roomId);
|
||||
try {
|
||||
HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices =
|
||||
await DevicesAPI.getDevicesByRoomId(roomId);
|
||||
await DevicesAPI.getDevicesByRoomId(roomId);
|
||||
} catch (e) {
|
||||
emitSafe(GetDevicesError(e.toString()));
|
||||
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 {
|
||||
|
326
lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart
Normal file
@ -0,0 +1,326 @@
|
||||
import 'dart:async';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/one_gang_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/schedule_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/helpers/snack_bar.dart';
|
||||
import 'one_gang_event.dart';
|
||||
|
||||
class OneGangBloc extends Bloc<OneGangEvent, OneGangState> {
|
||||
final String oneGangId;
|
||||
final String switchCode;
|
||||
OneGangModel deviceStatus = OneGangModel(
|
||||
firstSwitch: false,
|
||||
firstCountDown: 0,
|
||||
);
|
||||
Timer? _timer;
|
||||
|
||||
bool oneGangGroup = false;
|
||||
List<DeviceModel> devicesList = [];
|
||||
|
||||
OneGangBloc({required this.oneGangId,required this.switchCode}) : super(InitialState()) {
|
||||
on<InitialEvent>(_fetchOneGangStatus);
|
||||
on<OneGangUpdated>(_oneGangUpdated);
|
||||
on<ChangeFirstSwitchStatusEvent>(_changeFirstSwitch);
|
||||
on<ChangeSlidingSegment>(_changeSliding);
|
||||
on<SetCounterValue>(_setCounterValue);
|
||||
on<GetCounterEvent>(_getCounterValue);
|
||||
on<TickTimer>(_onTickTimer);
|
||||
on<OnClose>(_onClose);
|
||||
|
||||
|
||||
|
||||
|
||||
on<ToggleDaySelectionEvent>(toggleDaySelection);
|
||||
on<ThreeGangSave>(saveSchedule);
|
||||
on<GetScheduleEvent>(getSchedule);
|
||||
on<ToggleScheduleEvent>(toggleChange);
|
||||
on<DeleteScheduleEvent>(deleteSchedule);
|
||||
}
|
||||
|
||||
void _fetchOneGangStatus(InitialEvent event, Emitter<OneGangState> emit) async {
|
||||
emit(LoadingInitialState());
|
||||
try {
|
||||
var response = await DevicesAPI.getDeviceStatus(oneGangId);
|
||||
List<StatusModel> statusModelList = [];
|
||||
for (var status in response['status']) {
|
||||
statusModelList.add(StatusModel.fromJson(status));
|
||||
}
|
||||
deviceStatus = OneGangModel.fromJson(statusModelList);
|
||||
emit(UpdateState(oneGangModel: deviceStatus));
|
||||
_listenToChanges();
|
||||
} catch (e) {
|
||||
emit(FailedState(error: e.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_listenToChanges() {
|
||||
try {
|
||||
DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$oneGangId');
|
||||
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 = OneGangModel.fromJson(statusList);
|
||||
if (!isClosed) {
|
||||
add(OneGangUpdated());
|
||||
}
|
||||
});
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
_oneGangUpdated(OneGangUpdated event, Emitter<OneGangState> emit) {
|
||||
emit(UpdateState(oneGangModel: deviceStatus));
|
||||
}
|
||||
|
||||
void _changeFirstSwitch(ChangeFirstSwitchStatusEvent event, Emitter<OneGangState> emit) async {
|
||||
emit(LoadingNewSate(oneGangModel: deviceStatus));
|
||||
try {
|
||||
deviceStatus.firstSwitch = !event.value;
|
||||
emit(UpdateState(oneGangModel: deviceStatus));
|
||||
if (_timer != null) {
|
||||
_timer!.cancel();
|
||||
}
|
||||
_timer = Timer(const Duration(milliseconds: 500), () async {
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: oneGangGroup ? event.deviceId : oneGangId,
|
||||
code: 'switch_1',
|
||||
value: !event.value),
|
||||
oneGangGroup ? event.deviceId : oneGangId);
|
||||
|
||||
if (!response['success']) {
|
||||
add(InitialEvent(groupScreen: oneGangGroup));
|
||||
}
|
||||
});
|
||||
} catch (_) {
|
||||
add(InitialEvent(groupScreen: oneGangGroup));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _changeSliding(ChangeSlidingSegment event, Emitter<OneGangState> emit) async {
|
||||
emit(ChangeSlidingSegmentState(value: event.value));
|
||||
}
|
||||
|
||||
void _setCounterValue(SetCounterValue event, Emitter<OneGangState> emit) async {
|
||||
emit(LoadingNewSate(oneGangModel: deviceStatus));
|
||||
int seconds = 0;
|
||||
try {
|
||||
seconds = event.duration.inSeconds;
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: oneGangId, code: event.deviceCode, value: seconds),
|
||||
oneGangId);
|
||||
|
||||
if (response['success'] ?? false) {
|
||||
if (event.deviceCode == 'countdown_1') {
|
||||
deviceStatus.firstCountDown = seconds;
|
||||
}
|
||||
} else {
|
||||
emit(const FailedState(error: 'Something went wrong'));
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
emit(FailedState(error: e.toString()));
|
||||
return;
|
||||
}
|
||||
if (seconds > 0) {
|
||||
_onStartTimer(seconds);
|
||||
} else {
|
||||
_timer?.cancel();
|
||||
emit(TimerRunComplete());
|
||||
}
|
||||
}
|
||||
|
||||
void _getCounterValue(GetCounterEvent event, Emitter<OneGangState> emit) async {
|
||||
emit(LoadingInitialState());
|
||||
try {
|
||||
var response = await DevicesAPI.getDeviceStatus(oneGangId);
|
||||
List<StatusModel> statusModelList = [];
|
||||
for (var status in response['status']) {
|
||||
statusModelList.add(StatusModel.fromJson(status));
|
||||
}
|
||||
deviceStatus = OneGangModel.fromJson(statusModelList);
|
||||
|
||||
if (event.deviceCode == 'countdown_1') {
|
||||
deviceStatus.firstCountDown > 0
|
||||
? _onStartTimer(deviceStatus.firstCountDown)
|
||||
: emit(UpdateTimerState(seconds: deviceStatus.firstCountDown));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(FailedState(error: e.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void _onClose(OnClose event, Emitter<OneGangState> emit) {
|
||||
_timer?.cancel();
|
||||
}
|
||||
|
||||
void _onStartTimer(int seconds) {
|
||||
_timer?.cancel();
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
add(TickTimer(seconds - timer.tick));
|
||||
});
|
||||
}
|
||||
|
||||
void _onTickTimer(TickTimer event, Emitter<OneGangState> emit) {
|
||||
if (event.remainingTime > 0) {
|
||||
emit(TimerRunInProgress(event.remainingTime));
|
||||
} else {
|
||||
_timer?.cancel();
|
||||
emit(TimerRunComplete());
|
||||
}
|
||||
}
|
||||
|
||||
List<Map<String, String>> days = [
|
||||
{"day": "Sun", "key": "Sun"},
|
||||
{"day": "Mon", "key": "Mon"},
|
||||
{"day": "Tue", "key": "Tue"},
|
||||
{"day": "Wed", "key": "Wed"},
|
||||
{"day": "Thu", "key": "Thu"},
|
||||
{"day": "Fri", "key": "Fri"},
|
||||
{"day": "Sat", "key": "Sat"},
|
||||
];
|
||||
|
||||
Future<void> saveSchedule(ThreeGangSave event, Emitter<OneGangState> emit,) async {
|
||||
try {
|
||||
if(selectedDays.isNotEmpty) {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.postSchedule(
|
||||
category: switchCode,
|
||||
deviceId: oneGangId,
|
||||
time: getTimeStampWithoutSeconds(selectedTime).toString(),
|
||||
code: switchCode,
|
||||
value: toggleSchedule,
|
||||
days: selectedDays);
|
||||
CustomSnackBar.displaySnackBar('Save Successfully');
|
||||
add(GetScheduleEvent());
|
||||
emit(ThreeGangSaveSchedule());
|
||||
toggleCreateSchedule();
|
||||
}else{
|
||||
CustomSnackBar.displaySnackBar('Please select days');
|
||||
}
|
||||
} catch (e) {
|
||||
emit(FailedState(error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<void> getSchedule(GetScheduleEvent event, Emitter<OneGangState> emit,) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.getSchedule(
|
||||
category: switchCode,
|
||||
deviceId: oneGangId ,
|
||||
);
|
||||
List<dynamic> jsonData = response;
|
||||
listSchedule = jsonData.map((item) => ScheduleModel.fromJson(item)).toList();
|
||||
emit(InitialState());
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['message'];
|
||||
emit(FailedState(error: errorMessage.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
int? getTimeStampWithoutSeconds(DateTime? dateTime) {
|
||||
if (dateTime == null) return null;
|
||||
DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month,
|
||||
dateTime.day, dateTime.hour, dateTime.minute);
|
||||
return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000;
|
||||
}
|
||||
|
||||
Future toggleChange(
|
||||
ToggleScheduleEvent event, Emitter<OneGangState> emit) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.changeSchedule(
|
||||
scheduleId: event.id, deviceUuid: oneGangId, enable: event.toggle);
|
||||
if (response == true) {
|
||||
add(GetScheduleEvent());
|
||||
toggleSchedule = event.toggle;
|
||||
return toggleSchedule;
|
||||
}
|
||||
emit(IsToggleState(onOff: toggleSchedule));
|
||||
add(const ChangeSlidingSegment(value: 1));
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['message'];
|
||||
emit(FailedState(error: errorMessage.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future deleteSchedule(
|
||||
DeleteScheduleEvent event, Emitter<OneGangState> emit) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.deleteSchedule(
|
||||
scheduleId: event.id,
|
||||
deviceUuid: oneGangId, );
|
||||
if (response == true) {
|
||||
add(GetScheduleEvent());
|
||||
return toggleSchedule;
|
||||
}
|
||||
emit(IsToggleState(onOff: toggleSchedule));
|
||||
add(const ChangeSlidingSegment(value: 1));
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['message'];
|
||||
emit(FailedState(error: errorMessage.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
void toggleCreateSchedule() {
|
||||
emit(LoadingInitialState());
|
||||
createSchedule = !createSchedule;
|
||||
selectedDays.clear();
|
||||
selectedTime=DateTime.now();
|
||||
emit(UpdateCreateScheduleState(createSchedule));
|
||||
emit(ChangeSlidingSegmentState(value: 1));
|
||||
}
|
||||
|
||||
bool toggleSchedule = true;
|
||||
List<String> selectedDays = [];
|
||||
bool createSchedule = false;
|
||||
List<ScheduleModel> listSchedule = [];
|
||||
DateTime? selectedTime=DateTime.now();
|
||||
Future<void> toggleDaySelection(
|
||||
ToggleDaySelectionEvent event,
|
||||
Emitter<OneGangState> emit,
|
||||
) async {
|
||||
emit(LoadingInitialState());
|
||||
if (selectedDays.contains(event.key)) {
|
||||
selectedDays.remove(event.key);
|
||||
} else {
|
||||
selectedDays.add(event.key);
|
||||
}
|
||||
emit(ChangeTimeState());
|
||||
add(ChangeSlidingSegment(value: 1));
|
||||
}
|
||||
|
||||
int selectedTabIndex = 0;
|
||||
|
||||
void toggleSelectedIndex(index) {
|
||||
emit(LoadingInitialState());
|
||||
selectedTabIndex = index;
|
||||
emit(ChangeSlidingSegmentState(value: selectedTabIndex));
|
||||
}
|
||||
|
||||
}
|
116
lib/features/devices/bloc/one_gang_bloc/one_gang_event.dart
Normal file
@ -0,0 +1,116 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class OneGangEvent extends Equatable {
|
||||
const OneGangEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class LoadingEvent extends OneGangEvent {}
|
||||
|
||||
class OneGangUpdated extends OneGangEvent {}
|
||||
|
||||
class InitialEvent extends OneGangEvent {
|
||||
final bool groupScreen;
|
||||
const InitialEvent({required this.groupScreen});
|
||||
@override
|
||||
List<Object> get props => [groupScreen];
|
||||
}
|
||||
|
||||
class ChangeFirstSwitchStatusEvent extends OneGangEvent {
|
||||
final bool value;
|
||||
final String deviceId;
|
||||
const ChangeFirstSwitchStatusEvent({required this.value, this.deviceId = ''});
|
||||
@override
|
||||
List<Object> get props => [value, deviceId];
|
||||
}
|
||||
|
||||
class ChangeSecondSwitchStatusEvent extends OneGangEvent {
|
||||
final bool value;
|
||||
final String deviceId;
|
||||
const ChangeSecondSwitchStatusEvent({required this.value, this.deviceId = ''});
|
||||
@override
|
||||
List<Object> get props => [value, deviceId];
|
||||
}
|
||||
|
||||
|
||||
class AllOffEvent extends OneGangEvent {}
|
||||
|
||||
class AllOnEvent extends OneGangEvent {}
|
||||
|
||||
class GroupAllOnEvent extends OneGangEvent {}
|
||||
|
||||
class GroupAllOffEvent extends OneGangEvent {}
|
||||
|
||||
class ChangeSlidingSegment extends OneGangEvent {
|
||||
final int value;
|
||||
const ChangeSlidingSegment({required this.value});
|
||||
@override
|
||||
List<Object> get props => [value];
|
||||
}
|
||||
|
||||
class GetCounterEvent extends OneGangEvent {
|
||||
final String deviceCode;
|
||||
const GetCounterEvent({required this.deviceCode});
|
||||
@override
|
||||
List<Object> get props => [deviceCode];
|
||||
}
|
||||
|
||||
class SetCounterValue extends OneGangEvent {
|
||||
final Duration duration;
|
||||
final String deviceCode;
|
||||
const SetCounterValue({required this.duration, required this.deviceCode});
|
||||
@override
|
||||
List<Object> get props => [duration, deviceCode];
|
||||
}
|
||||
|
||||
class StartTimer extends OneGangEvent {
|
||||
final int duration;
|
||||
|
||||
const StartTimer(this.duration);
|
||||
|
||||
@override
|
||||
List<Object> get props => [duration];
|
||||
}
|
||||
|
||||
class TickTimer extends OneGangEvent {
|
||||
final int remainingTime;
|
||||
|
||||
const TickTimer(this.remainingTime);
|
||||
|
||||
@override
|
||||
List<Object> get props => [remainingTime];
|
||||
}
|
||||
|
||||
class StopTimer extends OneGangEvent {}
|
||||
|
||||
class OnClose extends OneGangEvent {}
|
||||
|
||||
|
||||
|
||||
|
||||
//------------------- Schedule ----------=---------
|
||||
class GetScheduleEvent extends OneGangEvent {}
|
||||
class ThreeGangSave extends OneGangEvent {}
|
||||
class ToggleScheduleEvent extends OneGangEvent {
|
||||
final String id;
|
||||
final bool toggle;
|
||||
const ToggleScheduleEvent({required this.toggle,required this.id});
|
||||
@override
|
||||
List<Object> get props => [toggle,id];
|
||||
}
|
||||
class ToggleDaySelectionEvent extends OneGangEvent {
|
||||
|
||||
final String key;
|
||||
|
||||
const ToggleDaySelectionEvent({required this.key});
|
||||
@override
|
||||
List<Object> get props => [key];
|
||||
}
|
||||
class DeleteScheduleEvent extends OneGangEvent {
|
||||
final String id;
|
||||
const DeleteScheduleEvent({required this.id});
|
||||
@override
|
||||
List<Object> get props => [id];
|
||||
}
|
89
lib/features/devices/bloc/one_gang_bloc/one_gang_state.dart
Normal file
@ -0,0 +1,89 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_app/features/devices/model/groupTwoGangModel.dart';
|
||||
import 'package:syncrow_app/features/devices/model/one_gang_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/two_gang_model.dart';
|
||||
|
||||
|
||||
class OneGangState extends Equatable {
|
||||
const OneGangState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class InitialState extends OneGangState {}
|
||||
|
||||
class LoadingInitialState extends OneGangState {}
|
||||
|
||||
class UpdateState extends OneGangState {
|
||||
final OneGangModel oneGangModel;
|
||||
const UpdateState({required this.oneGangModel});
|
||||
|
||||
@override
|
||||
List<Object> get props => [TwoGangModel];
|
||||
}
|
||||
|
||||
class LoadingNewSate extends OneGangState {
|
||||
final OneGangModel oneGangModel;
|
||||
const LoadingNewSate({required this.oneGangModel});
|
||||
|
||||
@override
|
||||
List<Object> get props => [OneGangModel];
|
||||
}
|
||||
|
||||
class UpdateGroupState extends OneGangState {
|
||||
final List<GroupTwoGangModel> twoGangList;
|
||||
final bool allSwitches;
|
||||
|
||||
const UpdateGroupState({required this.twoGangList, required this.allSwitches});
|
||||
|
||||
@override
|
||||
List<Object> get props => [twoGangList, allSwitches];
|
||||
}
|
||||
|
||||
class FailedState extends OneGangState {
|
||||
final String error;
|
||||
|
||||
const FailedState({required this.error});
|
||||
|
||||
@override
|
||||
List<Object> get props => [error];
|
||||
}
|
||||
|
||||
class ChangeSlidingSegmentState extends OneGangState {
|
||||
final int value;
|
||||
|
||||
const ChangeSlidingSegmentState({required this.value});
|
||||
|
||||
@override
|
||||
List<Object> get props => [value];
|
||||
}
|
||||
|
||||
class UpdateTimerState extends OneGangState {
|
||||
final int seconds;
|
||||
const UpdateTimerState({required this.seconds});
|
||||
|
||||
@override
|
||||
List<Object> get props => [seconds];
|
||||
}
|
||||
|
||||
class TimerRunInProgress extends OneGangState {
|
||||
final int remainingTime;
|
||||
|
||||
const TimerRunInProgress(this.remainingTime);
|
||||
|
||||
@override
|
||||
List<Object> get props => [remainingTime];
|
||||
}
|
||||
|
||||
class TimerRunComplete extends OneGangState {}
|
||||
class ThreeGangSaveSchedule extends OneGangState {}
|
||||
class IsToggleState extends OneGangState {
|
||||
final bool? onOff;
|
||||
const IsToggleState({this.onOff});
|
||||
}
|
||||
class ChangeTimeState extends OneGangState {}
|
||||
class UpdateCreateScheduleState extends OneGangState {
|
||||
final bool createSchedule;
|
||||
UpdateCreateScheduleState(this.createSchedule);
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
import 'dart:math';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.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_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';
|
||||
@ -25,6 +24,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
|
||||
SmartDoorBloc({required this.deviceId}) : super(InitialState()) {
|
||||
on<InitialEvent>(_fetchSmartDoorStatus);
|
||||
on<DoorLockUpdated>(_doorLockUpdated);
|
||||
on<InitialPasswordsPage>(getTemporaryPasswords);
|
||||
on<InitialOneTimePassword>(getOneTimePasswords);
|
||||
on<InitialTimeLimitPassword>(getTimeLimitPasswords);
|
||||
@ -54,6 +54,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
bool isStartEndTime = true;
|
||||
DateTime? startTime;
|
||||
DateTime? endTime;
|
||||
int unlockRequest = 0;
|
||||
List<TemporaryPassword>? temporaryPasswords = [];
|
||||
List<OfflinePasswordModel>? oneTimePasswords = [];
|
||||
List<OfflinePasswordModel>? timeLimitPasswords = [];
|
||||
@ -69,23 +70,24 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
return passwordController.text;
|
||||
}
|
||||
|
||||
Future generateAndSavePasswordOneTime (GenerateAndSavePasswordOneTimeEvent event, Emitter<SmartDoorState> emit) async {
|
||||
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;
|
||||
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));
|
||||
Clipboard.setData(ClipboardData(text: passwordController.text));
|
||||
});
|
||||
emit(const GeneratePasswordOneTimestate(generated: true));
|
||||
} catch (_) {
|
||||
emit(FailedState(errorMessage: _.toString()));
|
||||
}finally {
|
||||
} finally {
|
||||
isSavingPassword = false;
|
||||
}
|
||||
}
|
||||
@ -100,20 +102,42 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
}
|
||||
deviceStatus = SmartDoorModel.fromJson(statusModelList);
|
||||
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());
|
||||
var response = await DevicesAPI.renamePass(
|
||||
name:passwordNameController.text ,
|
||||
doorLockUuid:deviceId ,
|
||||
passwordId:passwordId
|
||||
);
|
||||
await DevicesAPI.renamePass(
|
||||
name: passwordNameController.text, doorLockUuid: deviceId, passwordId: passwordId);
|
||||
add(InitialOneTimePassword());
|
||||
add(InitialTimeLimitPassword());
|
||||
emit(UpdateState(smartDoorModel: deviceStatus));
|
||||
@ -126,11 +150,14 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
void getTemporaryPasswords(InitialPasswordsPage event, Emitter<SmartDoorState> emit) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
var response = await DevicesAPI.getTemporaryPasswords(deviceId, );
|
||||
var response = await DevicesAPI.getTemporaryPasswords(
|
||||
deviceId,
|
||||
);
|
||||
if (response is List) {
|
||||
temporaryPasswords = response.map((item) => TemporaryPassword.fromJson(item)).toList();
|
||||
} else if (response is Map && response.containsKey('data')) {
|
||||
temporaryPasswords = (response['data'] as List).map((item) => TemporaryPassword.fromJson(item)).toList();
|
||||
temporaryPasswords =
|
||||
(response['data'] as List).map((item) => TemporaryPassword.fromJson(item)).toList();
|
||||
}
|
||||
emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!));
|
||||
} catch (e) {
|
||||
@ -183,25 +210,26 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
bool setStartEndTime(SetStartEndTimeEvent event, Emitter<SmartDoorState> emit) {
|
||||
emit(LoadingInitialState());
|
||||
isStartEndTime = event.val;
|
||||
emit(IsStartEndState(isStartEndTime:isStartEndTime));
|
||||
emit(IsStartEndState(isStartEndTime: isStartEndTime));
|
||||
return isStartEndTime;
|
||||
}
|
||||
|
||||
void _updateLock(UpdateLockEvent event, Emitter<SmartDoorState> emit) async {
|
||||
emit(LoadingNewSate(smartDoorModel: deviceStatus));
|
||||
try {
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: deviceId, code: 'normal_open_switch', value: !event.value),
|
||||
deviceId);
|
||||
// final response = await DevicesAPI.controlDevice(
|
||||
// DeviceControlModel(deviceId: deviceId, code: 'normal_open_switch', value: !event.value),
|
||||
// deviceId);
|
||||
|
||||
if (response['success'] ?? false) {
|
||||
final response = await DevicesAPI.openDoorLock(deviceId);
|
||||
|
||||
if (response) {
|
||||
deviceStatus.normalOpenSwitch = !event.value;
|
||||
}
|
||||
} catch (_) {}
|
||||
emit(UpdateState(smartDoorModel: deviceStatus));
|
||||
}
|
||||
|
||||
|
||||
Future<void> selectTimeOfLinePassword(SelectTimeEvent event, Emitter<SmartDoorState> emit) async {
|
||||
emit(ChangeTimeState());
|
||||
final DateTime? picked = await showDatePicker(
|
||||
@ -224,24 +252,28 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
0,
|
||||
);
|
||||
final selectedTimestamp = DateTime(
|
||||
selectedDateTime.year,
|
||||
selectedDateTime.month,
|
||||
selectedDateTime.day,
|
||||
selectedDateTime.hour,
|
||||
selectedDateTime.minute,
|
||||
).millisecondsSinceEpoch ~/ 1000; // Divide by 1000 to remove milliseconds
|
||||
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
|
||||
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.');
|
||||
CustomSnackBar.displaySnackBar(
|
||||
'Expiration Time cannot be earlier than Effective Time.');
|
||||
} else {
|
||||
expirationTime = selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds
|
||||
expirationTime =
|
||||
selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds
|
||||
expirationTimeTimeStamp = selectedTimestamp;
|
||||
}
|
||||
}
|
||||
@ -250,7 +282,8 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> selectTimeOnlinePassword(SelectTimeOnlinePasswordEvent event, Emitter<SmartDoorState> emit) async {
|
||||
Future<void> selectTimeOnlinePassword(
|
||||
SelectTimeOnlinePasswordEvent event, Emitter<SmartDoorState> emit) async {
|
||||
emit(ChangeTimeState());
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: event.context,
|
||||
@ -288,12 +321,12 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
timePicked.minute,
|
||||
);
|
||||
final selectedTimestamp = DateTime(
|
||||
selectedDateTime.year,
|
||||
selectedDateTime.month,
|
||||
selectedDateTime.day,
|
||||
selectedDateTime.hour,
|
||||
selectedDateTime.minute,
|
||||
).millisecondsSinceEpoch ~/
|
||||
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!) {
|
||||
@ -305,7 +338,8 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
expirationTime =
|
||||
selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds
|
||||
@ -322,7 +356,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
try {
|
||||
isSavingPassword = true;
|
||||
emit(LoadingSaveState());
|
||||
var res = await DevicesAPI.createPassword(
|
||||
await DevicesAPI.createPassword(
|
||||
deviceId: deviceId,
|
||||
effectiveTime: effectiveTimeTimeStamp.toString(),
|
||||
invalidTime: expirationTimeTimeStamp.toString(),
|
||||
@ -341,25 +375,25 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
CustomSnackBar.displaySnackBar('Save Successfully');
|
||||
emit(SaveState());
|
||||
} catch (_) {
|
||||
|
||||
}finally {
|
||||
} finally {
|
||||
isSavingPassword = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> generateAndSavePasswordTimeLimited (GenerateAndSavePasswordTimeLimitEvent event, Emitter<SmartDoorState> emit) async {
|
||||
Future<void> generateAndSavePasswordTimeLimited(
|
||||
GenerateAndSavePasswordTimeLimitEvent event, Emitter<SmartDoorState> emit) async {
|
||||
if (timeLimitValidate() || isSavingPassword) return;
|
||||
try {
|
||||
isSavingPassword = true;
|
||||
emit(LoadingInitialState());
|
||||
var res = await DevicesAPI.generateMultiTimePassword(
|
||||
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;
|
||||
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), () {
|
||||
@ -368,8 +402,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
emit(const GeneratePasswordOneTimestate(generated: true));
|
||||
} catch (_) {
|
||||
add(InitialPasswordsPage());
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
isSavingPassword = false;
|
||||
}
|
||||
}
|
||||
@ -377,7 +410,6 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
Future<void> deletePassword(DeletePasswordEvent event, Emitter<SmartDoorState> emit) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
var response =
|
||||
await DevicesAPI.deletePassword(deviceId: deviceId, passwordId: event.passwordId)
|
||||
.then((value) async {
|
||||
add(InitialPasswordsPage());
|
||||
@ -388,7 +420,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
}
|
||||
|
||||
bool _validateInputs() {
|
||||
if (passwordController.text.length < 7 ) {
|
||||
if (passwordController.text.length < 7) {
|
||||
CustomSnackBar.displaySnackBar('Password less than 7');
|
||||
return true;
|
||||
}
|
||||
@ -397,7 +429,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
CustomSnackBar.displaySnackBar('Password required');
|
||||
return true;
|
||||
}
|
||||
if (passwordNameController.text.isEmpty ) {
|
||||
if (passwordNameController.text.isEmpty) {
|
||||
CustomSnackBar.displaySnackBar('Password name required');
|
||||
return true;
|
||||
}
|
||||
@ -412,7 +444,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (repeat == true && (endTime == null || startTime == null || selectedDays == null)) {
|
||||
if (repeat == true && (endTime == null || startTime == null)) {
|
||||
CustomSnackBar.displaySnackBar('Start Time and End time and the days required ');
|
||||
return true;
|
||||
}
|
||||
@ -420,7 +452,6 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
}
|
||||
|
||||
bool timeLimitValidate() {
|
||||
|
||||
if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) {
|
||||
CustomSnackBar.displaySnackBar('Select effective time');
|
||||
return true;
|
||||
@ -444,7 +475,10 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
|
||||
List<String> selectedDays = [];
|
||||
|
||||
Future<void> toggleDaySelection(ToggleDaySelectionEvent event, Emitter<SmartDoorState> emit,)async {
|
||||
Future<void> toggleDaySelection(
|
||||
ToggleDaySelectionEvent event,
|
||||
Emitter<SmartDoorState> emit,
|
||||
) async {
|
||||
emit(LoadingInitialState());
|
||||
if (selectedDays.contains(event.key)) {
|
||||
selectedDays.remove(event.key);
|
||||
@ -459,8 +493,3 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
return DateFormat('HH:mm').format(dateTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -9,10 +9,15 @@ abstract class SmartDoorEvent extends Equatable {
|
||||
}
|
||||
|
||||
class InitialEvent extends SmartDoorEvent {}
|
||||
|
||||
class InitialPasswordsPage extends SmartDoorEvent {}
|
||||
|
||||
class InitialOneTimePassword extends SmartDoorEvent {}
|
||||
|
||||
class InitialTimeLimitPassword extends SmartDoorEvent {}
|
||||
|
||||
class DoorLockUpdated extends SmartDoorEvent {}
|
||||
|
||||
class UpdateLockEvent extends SmartDoorEvent {
|
||||
final bool value;
|
||||
const UpdateLockEvent({required this.value});
|
||||
@ -26,6 +31,7 @@ class SavePasswordEvent extends SmartDoorEvent {
|
||||
@override
|
||||
List<Object> get props => [context];
|
||||
}
|
||||
|
||||
class GenerateAndSavePasswordTimeLimitEvent extends SmartDoorEvent {
|
||||
final BuildContext context;
|
||||
const GenerateAndSavePasswordTimeLimitEvent({required this.context});
|
||||
@ -40,28 +46,26 @@ class GenerateAndSavePasswordOneTimeEvent extends SmartDoorEvent {
|
||||
List<Object> get props => [context];
|
||||
}
|
||||
|
||||
class GeneratePasswordEvent extends SmartDoorEvent {
|
||||
|
||||
|
||||
}
|
||||
class GeneratePasswordEvent extends SmartDoorEvent {}
|
||||
|
||||
class SelectTimeEvent extends SmartDoorEvent {
|
||||
final BuildContext context;
|
||||
final BuildContext context;
|
||||
final bool isEffective;
|
||||
const SelectTimeEvent({required this.context,required this.isEffective});
|
||||
const SelectTimeEvent({required this.context, required this.isEffective});
|
||||
@override
|
||||
List<Object> get props => [context,isEffective];
|
||||
List<Object> get props => [context, isEffective];
|
||||
}
|
||||
|
||||
class SelectTimeOnlinePasswordEvent extends SmartDoorEvent {
|
||||
final BuildContext context;
|
||||
final BuildContext context;
|
||||
final bool isEffective;
|
||||
const SelectTimeOnlinePasswordEvent({required this.context,required this.isEffective});
|
||||
const SelectTimeOnlinePasswordEvent({required this.context, required this.isEffective});
|
||||
@override
|
||||
List<Object> get props => [context,isEffective];
|
||||
List<Object> get props => [context, isEffective];
|
||||
}
|
||||
|
||||
class ToggleRepeatEvent extends SmartDoorEvent {}
|
||||
|
||||
class SetStartEndTimeEvent extends SmartDoorEvent {
|
||||
final bool val;
|
||||
const SetStartEndTimeEvent({required this.val});
|
||||
@ -70,7 +74,7 @@ class SetStartEndTimeEvent extends SmartDoorEvent {
|
||||
}
|
||||
|
||||
class DeletePasswordEvent extends SmartDoorEvent {
|
||||
final String passwordId;
|
||||
final String passwordId;
|
||||
|
||||
const DeletePasswordEvent({required this.passwordId});
|
||||
@override
|
||||
@ -78,7 +82,7 @@ class DeletePasswordEvent extends SmartDoorEvent {
|
||||
}
|
||||
|
||||
class ToggleDaySelectionEvent extends SmartDoorEvent {
|
||||
final String key;
|
||||
final String key;
|
||||
|
||||
const ToggleDaySelectionEvent({required this.key});
|
||||
@override
|
||||
@ -89,12 +93,9 @@ class ChangeTimeEvent extends SmartDoorEvent {
|
||||
final dynamic val;
|
||||
final bool isStartEndTime;
|
||||
|
||||
const ChangeTimeEvent({required this.val,required this.isStartEndTime});
|
||||
const ChangeTimeEvent({required this.val, required this.isStartEndTime});
|
||||
@override
|
||||
List<Object> get props => [val,isStartEndTime];
|
||||
List<Object> get props => [val, isStartEndTime];
|
||||
}
|
||||
|
||||
class RenamePasswordEvent extends SmartDoorEvent {
|
||||
}
|
||||
|
||||
|
||||
class RenamePasswordEvent extends SmartDoorEvent {}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_event.dart';
|
||||
@ -6,12 +8,16 @@ import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_sta
|
||||
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/group_three_gang_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/schedule_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/status_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/three_gang_model.dart';
|
||||
import 'package:syncrow_app/services/api/devices_api.dart';
|
||||
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
|
||||
|
||||
|
||||
class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
|
||||
final String threeGangId;
|
||||
final String switchCode;
|
||||
ThreeGangModel deviceStatus = ThreeGangModel(
|
||||
firstSwitch: false,
|
||||
secondSwitch: false,
|
||||
@ -20,13 +26,18 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
|
||||
secondCountDown: 0,
|
||||
thirdCountDown: 0);
|
||||
Timer? _timer;
|
||||
// Timer? _firstSwitchTimer;
|
||||
// Timer? _secondSwitchTimer;
|
||||
// Timer? _thirdSwitchTimer;
|
||||
|
||||
bool threeGangGroup = false;
|
||||
List<DeviceModel> devicesList = [];
|
||||
List<GroupThreeGangModel> groupThreeGangList = [];
|
||||
bool allSwitchesOn = true;
|
||||
|
||||
ThreeGangBloc({required this.threeGangId}) : super(InitialState()) {
|
||||
ThreeGangBloc({required this.threeGangId,required this.switchCode}) : super(InitialState()) {
|
||||
on<InitialEvent>(_fetchThreeGangStatus);
|
||||
on<ThreeGangUpdated>(_threeGangUpdated);
|
||||
on<ChangeFirstSwitchStatusEvent>(_changeFirstSwitch);
|
||||
on<ChangeSecondSwitchStatusEvent>(_changeSecondSwitch);
|
||||
on<ChangeThirdSwitchStatusEvent>(_changeThirdSwitch);
|
||||
@ -39,6 +50,14 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
|
||||
on<OnClose>(_onClose);
|
||||
on<GroupAllOnEvent>(_groupAllOn);
|
||||
on<GroupAllOffEvent>(_groupAllOff);
|
||||
|
||||
|
||||
|
||||
on<ToggleDaySelectionEvent>(toggleDaySelection);
|
||||
on<ThreeGangSave>(saveSchedule);
|
||||
on<GetScheduleEvent>(getSchedule);
|
||||
on<ToggleScheduleEvent>(toggleChange);
|
||||
on<DeleteScheduleEvent>(deleteSchedule);
|
||||
}
|
||||
|
||||
void _fetchThreeGangStatus(InitialEvent event, Emitter<ThreeGangState> emit) async {
|
||||
@ -85,6 +104,7 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
|
||||
}
|
||||
deviceStatus = ThreeGangModel.fromJson(statusModelList);
|
||||
emit(UpdateState(threeGangModel: deviceStatus));
|
||||
_listenToChanges();
|
||||
}
|
||||
} catch (e) {
|
||||
emit(FailedState(error: e.toString()));
|
||||
@ -92,6 +112,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 {
|
||||
emit(LoadingNewSate(threeGangModel: deviceStatus));
|
||||
try {
|
||||
@ -111,16 +159,22 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
|
||||
emit(UpdateState(threeGangModel: deviceStatus));
|
||||
}
|
||||
|
||||
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));
|
||||
if (_timer != null) {
|
||||
_timer!.cancel();
|
||||
}
|
||||
|
||||
_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 (_) {
|
||||
add(InitialEvent(groupScreen: threeGangGroup));
|
||||
}
|
||||
@ -146,16 +200,21 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
|
||||
emit(UpdateState(threeGangModel: deviceStatus));
|
||||
}
|
||||
|
||||
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));
|
||||
if (_timer != null) {
|
||||
_timer!.cancel();
|
||||
}
|
||||
_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 (_) {
|
||||
add(InitialEvent(groupScreen: threeGangGroup));
|
||||
}
|
||||
@ -180,16 +239,22 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
|
||||
emit(UpdateState(threeGangModel: deviceStatus));
|
||||
}
|
||||
|
||||
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));
|
||||
if (_timer != null) {
|
||||
_timer!.cancel();
|
||||
}
|
||||
|
||||
_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 (_) {
|
||||
add(InitialEvent(groupScreen: threeGangGroup));
|
||||
}
|
||||
@ -424,4 +489,143 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
|
||||
emit(TimerRunComplete());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
List<Map<String, String>> days = [
|
||||
{"day": "Sun", "key": "Sun"},
|
||||
{"day": "Mon", "key": "Mon"},
|
||||
{"day": "Tue", "key": "Tue"},
|
||||
{"day": "Wed", "key": "Wed"},
|
||||
{"day": "Thu", "key": "Thu"},
|
||||
{"day": "Fri", "key": "Fri"},
|
||||
{"day": "Sat", "key": "Sat"},
|
||||
];
|
||||
|
||||
Future<void> toggleDaySelection(
|
||||
ToggleDaySelectionEvent event,
|
||||
Emitter<ThreeGangState> emit,
|
||||
) async {
|
||||
emit(LoadingInitialState());
|
||||
if (selectedDays.contains(event.key)) {
|
||||
selectedDays.remove(event.key);
|
||||
} else {
|
||||
selectedDays.add(event.key);
|
||||
}
|
||||
emit(ChangeTimeState());
|
||||
add(ChangeSlidingSegment(value: 1));
|
||||
}
|
||||
|
||||
Future<void> saveSchedule(ThreeGangSave event, Emitter<ThreeGangState> emit,) async {
|
||||
try {
|
||||
if(selectedDays.isNotEmpty) {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.postSchedule(
|
||||
category: switchCode,
|
||||
deviceId: threeGangId,
|
||||
time: getTimeStampWithoutSeconds(selectedTime).toString(),
|
||||
code: switchCode,
|
||||
value: toggleSchedule,
|
||||
days: selectedDays);
|
||||
CustomSnackBar.displaySnackBar('Save Successfully');
|
||||
add(GetScheduleEvent());
|
||||
emit(ThreeGangSaveSchedule());
|
||||
toggleCreateSchedule();
|
||||
}else{
|
||||
CustomSnackBar.displaySnackBar('Please select days');
|
||||
}
|
||||
} catch (e) {
|
||||
emit(FailedState(error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<void> getSchedule(GetScheduleEvent event, Emitter<ThreeGangState> emit,) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.getSchedule(
|
||||
category: switchCode,
|
||||
deviceId: threeGangId ,
|
||||
);
|
||||
List<dynamic> jsonData = response;
|
||||
listSchedule = jsonData.map((item) => ScheduleModel.fromJson(item)).toList();
|
||||
emit(InitialState());
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['message'];
|
||||
emit(FailedState(error: errorMessage.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
int? getTimeStampWithoutSeconds(DateTime? dateTime) {
|
||||
if (dateTime == null) return null;
|
||||
DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month,
|
||||
dateTime.day, dateTime.hour, dateTime.minute);
|
||||
return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000;
|
||||
}
|
||||
|
||||
Future toggleChange(
|
||||
ToggleScheduleEvent event, Emitter<ThreeGangState> emit) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.changeSchedule(
|
||||
scheduleId: event.id, deviceUuid: threeGangId, enable: event.toggle);
|
||||
if (response == true) {
|
||||
add(GetScheduleEvent());
|
||||
toggleSchedule = event.toggle;
|
||||
return toggleSchedule;
|
||||
}
|
||||
emit(IsToggleState(onOff: toggleSchedule));
|
||||
add(const ChangeSlidingSegment(value: 1));
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['message'];
|
||||
emit(FailedState(error: errorMessage.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future deleteSchedule(
|
||||
DeleteScheduleEvent event, Emitter<ThreeGangState> emit) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.deleteSchedule(
|
||||
scheduleId: event.id,
|
||||
deviceUuid: threeGangId, );
|
||||
if (response == true) {
|
||||
add(GetScheduleEvent());
|
||||
return toggleSchedule;
|
||||
}
|
||||
emit(IsToggleState(onOff: toggleSchedule));
|
||||
add(const ChangeSlidingSegment(value: 1));
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['message'];
|
||||
emit(FailedState(error: errorMessage.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
void toggleCreateSchedule() {
|
||||
emit(LoadingInitialState());
|
||||
createSchedule = !createSchedule;
|
||||
selectedDays.clear();
|
||||
selectedTime=DateTime.now();
|
||||
emit(UpdateCreateScheduleState(createSchedule));
|
||||
emit(ChangeSlidingSegmentState(value: 1));
|
||||
}
|
||||
|
||||
int selectedTabIndex = 0;
|
||||
|
||||
void toggleSelectedIndex(index) {
|
||||
emit(LoadingInitialState());
|
||||
selectedTabIndex = index;
|
||||
emit(ChangeSlidingSegmentState(value: selectedTabIndex));
|
||||
}
|
||||
|
||||
bool toggleSchedule = true;
|
||||
List<String> selectedDays = [];
|
||||
bool createSchedule = false;
|
||||
List<ScheduleModel> listSchedule = [];
|
||||
DateTime? selectedTime=DateTime.now();
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ abstract class ThreeGangEvent extends Equatable {
|
||||
|
||||
class LoadingEvent extends ThreeGangEvent {}
|
||||
|
||||
class ThreeGangUpdated extends ThreeGangEvent {}
|
||||
|
||||
class InitialEvent extends ThreeGangEvent {
|
||||
final bool groupScreen;
|
||||
const InitialEvent({required this.groupScreen});
|
||||
@ -91,3 +93,29 @@ class TickTimer extends ThreeGangEvent {
|
||||
class StopTimer extends ThreeGangEvent {}
|
||||
|
||||
class OnClose extends ThreeGangEvent {}
|
||||
|
||||
|
||||
//------------------- Schedule ----------=---------
|
||||
class GetScheduleEvent extends ThreeGangEvent {}
|
||||
class ThreeGangSave extends ThreeGangEvent {}
|
||||
class ToggleScheduleEvent extends ThreeGangEvent {
|
||||
final String id;
|
||||
final bool toggle;
|
||||
const ToggleScheduleEvent({required this.toggle,required this.id});
|
||||
@override
|
||||
List<Object> get props => [toggle,id];
|
||||
}
|
||||
class ToggleDaySelectionEvent extends ThreeGangEvent {
|
||||
|
||||
final String key;
|
||||
|
||||
const ToggleDaySelectionEvent({required this.key});
|
||||
@override
|
||||
List<Object> get props => [key];
|
||||
}
|
||||
class DeleteScheduleEvent extends ThreeGangEvent {
|
||||
final String id;
|
||||
const DeleteScheduleEvent({required this.id});
|
||||
@override
|
||||
List<Object> get props => [id];
|
||||
}
|
||||
|
@ -75,3 +75,15 @@ class TimerRunInProgress extends ThreeGangState {
|
||||
}
|
||||
|
||||
class TimerRunComplete extends ThreeGangState {}
|
||||
|
||||
|
||||
class ThreeGangSaveSchedule extends ThreeGangState {}
|
||||
class IsToggleState extends ThreeGangState {
|
||||
final bool? onOff;
|
||||
const IsToggleState({this.onOff});
|
||||
}
|
||||
class ChangeTimeState extends ThreeGangState {}
|
||||
class UpdateCreateScheduleState extends ThreeGangState {
|
||||
final bool createSchedule;
|
||||
UpdateCreateScheduleState(this.createSchedule);
|
||||
}
|
||||
|
553
lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart
Normal file
@ -0,0 +1,553 @@
|
||||
import 'dart:async';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_event.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/groupTwoGangModel.dart';
|
||||
import 'package:syncrow_app/features/devices/model/schedule_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/status_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/two_gang_model.dart';
|
||||
import 'package:syncrow_app/services/api/devices_api.dart';
|
||||
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
|
||||
|
||||
class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
final String twoGangId;
|
||||
final String switchCode;
|
||||
TwoGangModel deviceStatus = TwoGangModel(
|
||||
firstSwitch: false,
|
||||
secondSwitch: false,
|
||||
firstCountDown: 0,
|
||||
secondCountDown: 0,
|
||||
);
|
||||
Timer? _timer;
|
||||
|
||||
bool twoGangGroup = false;
|
||||
List<DeviceModel> devicesList = [];
|
||||
List<GroupTwoGangModel> groupTwoGangList = [];
|
||||
bool allSwitchesOn = true;
|
||||
bool toggleSchedule = true;
|
||||
List<String> selectedDays = [];
|
||||
|
||||
bool createSchedule = false;
|
||||
List<ScheduleModel> listSchedule = [];
|
||||
|
||||
TwoGangBloc({required this.twoGangId,required this.switchCode}) : super(InitialState()) {
|
||||
on<InitialEvent>(_fetchTwoGangStatus);
|
||||
on<TwoGangUpdated>(_twoGangUpdated);
|
||||
on<ChangeFirstSwitchStatusEvent>(_changeFirstSwitch);
|
||||
on<ChangeSecondSwitchStatusEvent>(_changeSecondSwitch);
|
||||
on<AllOffEvent>(_allOff);
|
||||
on<AllOnEvent>(_allOn);
|
||||
on<ChangeSlidingSegment>(_changeSliding);
|
||||
on<SetCounterValue>(_setCounterValue);
|
||||
on<GetCounterEvent>(_getCounterValue);
|
||||
on<TickTimer>(_onTickTimer);
|
||||
on<OnClose>(_onClose);
|
||||
on<GroupAllOnEvent>(_groupAllOn);
|
||||
on<GroupAllOffEvent>(_groupAllOff);
|
||||
on<ToggleDaySelectionEvent>(toggleDaySelection);
|
||||
on<TwoGangSave>(saveSchedule);
|
||||
on<GetScheduleEvent>(getSchedule);
|
||||
on<ToggleScheduleEvent>(toggleRepeat);
|
||||
on<DeleteScheduleEvent>(deleteSchedule);
|
||||
|
||||
}
|
||||
|
||||
DateTime? selectedTime = DateTime.now();
|
||||
|
||||
void toggleCreateSchedule() {
|
||||
emit(LoadingInitialState());
|
||||
createSchedule = !createSchedule;
|
||||
selectedDays.clear();
|
||||
selectedTime=DateTime.now();
|
||||
emit(UpdateCreateScheduleState(createSchedule));
|
||||
emit(ChangeSlidingSegmentState(value: 1));
|
||||
}
|
||||
int selectedTabIndex = 0;
|
||||
|
||||
void toggleSelectedIndex(index) {
|
||||
emit(LoadingInitialState());
|
||||
selectedTabIndex = index;
|
||||
emit(ChangeSlidingSegmentState(value: selectedTabIndex));
|
||||
}
|
||||
|
||||
void _fetchTwoGangStatus(InitialEvent event, Emitter<TwoGangState> emit) async {
|
||||
emit(LoadingInitialState());
|
||||
try {
|
||||
twoGangGroup = event.groupScreen;
|
||||
if (twoGangGroup) {
|
||||
devicesList = [];
|
||||
groupTwoGangList = [];
|
||||
allSwitchesOn = true;
|
||||
devicesList = await DevicesAPI.getDeviceByGroupName(
|
||||
HomeCubit.getInstance().selectedSpace?.id ?? '', '2G');
|
||||
|
||||
for (int i = 0; i < devicesList.length; i++) {
|
||||
var response =
|
||||
await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
|
||||
List<StatusModel> statusModelList = [];
|
||||
for (var status in response['status']) {
|
||||
statusModelList.add(StatusModel.fromJson(status));
|
||||
}
|
||||
deviceStatus = TwoGangModel.fromJson(statusModelList);
|
||||
|
||||
groupTwoGangList.add(GroupTwoGangModel(
|
||||
deviceId: devicesList[i].uuid ?? '',
|
||||
deviceName: devicesList[i].name ?? '',
|
||||
firstSwitch: deviceStatus.firstSwitch,
|
||||
secondSwitch: deviceStatus.secondSwitch,
|
||||
));
|
||||
}
|
||||
|
||||
if (groupTwoGangList.isNotEmpty) {
|
||||
groupTwoGangList.firstWhere((element) {
|
||||
if (!element.firstSwitch || !element.secondSwitch) {
|
||||
allSwitchesOn = false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
emit(UpdateGroupState(
|
||||
twoGangList: groupTwoGangList, allSwitches: allSwitchesOn));
|
||||
} else {
|
||||
var response = await DevicesAPI.getDeviceStatus(twoGangId);
|
||||
List<StatusModel> statusModelList = [];
|
||||
for (var status in response['status']) {
|
||||
statusModelList.add(StatusModel.fromJson(status));
|
||||
}
|
||||
deviceStatus = TwoGangModel.fromJson(statusModelList);
|
||||
emit(UpdateState(twoGangModel: deviceStatus));
|
||||
_listenToChanges();
|
||||
}
|
||||
} catch (e) {
|
||||
emit(FailedState(error: e.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_listenToChanges() {
|
||||
try {
|
||||
DatabaseReference ref =
|
||||
FirebaseDatabase.instance.ref('device-status/$twoGangId');
|
||||
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 = TwoGangModel.fromJson(statusList);
|
||||
if (!isClosed) {
|
||||
add(TwoGangUpdated());
|
||||
}
|
||||
});
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
_twoGangUpdated(TwoGangUpdated event, Emitter<TwoGangState> emit) {
|
||||
emit(UpdateState(twoGangModel: deviceStatus));
|
||||
}
|
||||
|
||||
void _changeFirstSwitch(
|
||||
ChangeFirstSwitchStatusEvent event, Emitter<TwoGangState> emit) async {
|
||||
emit(LoadingNewSate(twoGangModel: deviceStatus));
|
||||
try {
|
||||
deviceStatus.firstSwitch = !event.value;
|
||||
emit(UpdateState(twoGangModel: deviceStatus));
|
||||
if (_timer != null) {
|
||||
_timer!.cancel();
|
||||
}
|
||||
|
||||
_timer = Timer(const Duration(milliseconds: 500), () async {
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangGroup ? event.deviceId : twoGangId,
|
||||
code: 'switch_1',
|
||||
value: !event.value),
|
||||
twoGangGroup ? event.deviceId : twoGangId);
|
||||
|
||||
if (!response['success']) {
|
||||
add(InitialEvent(groupScreen: twoGangGroup));
|
||||
}
|
||||
});
|
||||
} catch (_) {
|
||||
add(InitialEvent(groupScreen: twoGangGroup));
|
||||
}
|
||||
}
|
||||
|
||||
void _changeSecondSwitch(
|
||||
ChangeSecondSwitchStatusEvent event, Emitter<TwoGangState> emit) async {
|
||||
emit(LoadingNewSate(twoGangModel: deviceStatus));
|
||||
try {
|
||||
deviceStatus.secondSwitch = !event.value;
|
||||
emit(UpdateState(twoGangModel: deviceStatus));
|
||||
if (_timer != null) {
|
||||
_timer!.cancel();
|
||||
}
|
||||
_timer = Timer(const Duration(milliseconds: 500), () async {
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangGroup ? event.deviceId : twoGangId,
|
||||
code: 'switch_2',
|
||||
value: !event.value),
|
||||
twoGangGroup ? event.deviceId : twoGangId);
|
||||
|
||||
if (!response['success']) {
|
||||
add(InitialEvent(groupScreen: twoGangGroup));
|
||||
}
|
||||
});
|
||||
} catch (_) {
|
||||
add(InitialEvent(groupScreen: twoGangGroup));
|
||||
}
|
||||
}
|
||||
|
||||
void _allOff(AllOffEvent event, Emitter<TwoGangState> emit) async {
|
||||
emit(LoadingNewSate(twoGangModel: deviceStatus));
|
||||
|
||||
try {
|
||||
deviceStatus.firstSwitch = false;
|
||||
deviceStatus.secondSwitch = false;
|
||||
emit(UpdateState(twoGangModel: deviceStatus));
|
||||
|
||||
final response = await Future.wait([
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangId,
|
||||
code: 'switch_1',
|
||||
value: deviceStatus.firstSwitch),
|
||||
twoGangId),
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangId,
|
||||
code: 'switch_2',
|
||||
value: deviceStatus.secondSwitch),
|
||||
twoGangId),
|
||||
]);
|
||||
|
||||
if (response.every((element) => !element['success'])) {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
add(const InitialEvent(groupScreen: false));
|
||||
}
|
||||
} catch (_) {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
add(const InitialEvent(groupScreen: false));
|
||||
}
|
||||
}
|
||||
|
||||
void _allOn(AllOnEvent event, Emitter<TwoGangState> emit) async {
|
||||
emit(LoadingNewSate(twoGangModel: deviceStatus));
|
||||
try {
|
||||
deviceStatus.firstSwitch = true;
|
||||
deviceStatus.secondSwitch = true;
|
||||
emit(UpdateState(twoGangModel: deviceStatus));
|
||||
final response = await Future.wait([
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangId,
|
||||
code: 'switch_1',
|
||||
value: deviceStatus.firstSwitch),
|
||||
twoGangId),
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangId,
|
||||
code: 'switch_2',
|
||||
value: deviceStatus.secondSwitch),
|
||||
twoGangId),
|
||||
]);
|
||||
if (response.every((element) => !element['success'])) {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
add(const InitialEvent(groupScreen: false));
|
||||
}
|
||||
} catch (_) {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
add(const InitialEvent(groupScreen: false));
|
||||
}
|
||||
}
|
||||
|
||||
void _groupAllOn(GroupAllOnEvent event, Emitter<TwoGangState> emit) async {
|
||||
emit(LoadingNewSate(twoGangModel: deviceStatus));
|
||||
try {
|
||||
for (int i = 0; i < groupTwoGangList.length; i++) {
|
||||
groupTwoGangList[i].firstSwitch = true;
|
||||
groupTwoGangList[i].secondSwitch = true;
|
||||
}
|
||||
emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: true));
|
||||
|
||||
for (int i = 0; i < groupTwoGangList.length; i++) {
|
||||
final response = await Future.wait([
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: groupTwoGangList[i].deviceId,
|
||||
code: 'switch_1',
|
||||
value: true),
|
||||
groupTwoGangList[i].deviceId),
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: groupTwoGangList[i].deviceId,
|
||||
code: 'switch_2',
|
||||
value: true),
|
||||
groupTwoGangList[i].deviceId),
|
||||
]);
|
||||
|
||||
if (response.every((element) => !element['success'])) {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
add(const InitialEvent(groupScreen: true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (_) {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
add(const InitialEvent(groupScreen: true));
|
||||
}
|
||||
}
|
||||
|
||||
void _groupAllOff(GroupAllOffEvent event, Emitter<TwoGangState> emit) async {
|
||||
emit(LoadingNewSate(twoGangModel: deviceStatus));
|
||||
try {
|
||||
for (int i = 0; i < groupTwoGangList.length; i++) {
|
||||
groupTwoGangList[i].firstSwitch = false;
|
||||
groupTwoGangList[i].secondSwitch = false;
|
||||
}
|
||||
emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: false));
|
||||
|
||||
for (int i = 0; i < groupTwoGangList.length; i++) {
|
||||
final response = await Future.wait([
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: groupTwoGangList[i].deviceId,
|
||||
code: 'switch_1',
|
||||
value: false),
|
||||
groupTwoGangList[i].deviceId),
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: groupTwoGangList[i].deviceId,
|
||||
code: 'switch_2',
|
||||
value: false),
|
||||
groupTwoGangList[i].deviceId),
|
||||
]);
|
||||
|
||||
if (response.every((element) => !element['success'])) {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
add(const InitialEvent(groupScreen: true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (_) {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
add(const InitialEvent(groupScreen: true));
|
||||
}
|
||||
}
|
||||
|
||||
void _changeSliding(
|
||||
ChangeSlidingSegment event, Emitter<TwoGangState> emit) async {
|
||||
emit(ChangeSlidingSegmentState(value: event.value));
|
||||
}
|
||||
|
||||
void _setCounterValue(
|
||||
SetCounterValue event, Emitter<TwoGangState> emit) async {
|
||||
emit(LoadingNewSate(twoGangModel: deviceStatus));
|
||||
int seconds = 0;
|
||||
try {
|
||||
seconds = event.duration.inSeconds;
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangId,
|
||||
code: event.deviceCode,
|
||||
value: seconds),
|
||||
twoGangId);
|
||||
|
||||
if (response['success'] ?? false) {
|
||||
if (event.deviceCode == 'countdown_1') {
|
||||
deviceStatus.firstCountDown = seconds;
|
||||
}
|
||||
else if (event.deviceCode == 'countdown_2') {
|
||||
deviceStatus.secondCountDown = seconds;
|
||||
}
|
||||
} else {
|
||||
emit(const FailedState(error: 'Something went wrong'));
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
emit(FailedState(error: e.toString()));
|
||||
return;
|
||||
}
|
||||
if (seconds > 0) {
|
||||
_onStartTimer(seconds);
|
||||
} else {
|
||||
_timer?.cancel();
|
||||
emit(TimerRunComplete());
|
||||
}
|
||||
}
|
||||
|
||||
void _getCounterValue(
|
||||
GetCounterEvent event, Emitter<TwoGangState> emit) async {
|
||||
emit(LoadingInitialState());
|
||||
try {
|
||||
add(GetScheduleEvent());
|
||||
var response = await DevicesAPI.getDeviceStatus(twoGangId);
|
||||
List<StatusModel> statusModelList = [];
|
||||
for (var status in response['status']) {
|
||||
statusModelList.add(StatusModel.fromJson(status));
|
||||
}
|
||||
deviceStatus = TwoGangModel.fromJson(statusModelList);
|
||||
|
||||
if (event.deviceCode == 'countdown_1') {
|
||||
deviceStatus.firstCountDown > 0
|
||||
? _onStartTimer(deviceStatus.firstCountDown)
|
||||
: emit(UpdateTimerState(seconds: deviceStatus.firstCountDown));
|
||||
} else if (event.deviceCode == 'countdown_2') {
|
||||
deviceStatus.secondCountDown > 0
|
||||
? _onStartTimer(deviceStatus.secondCountDown)
|
||||
: emit(UpdateTimerState(seconds: deviceStatus.secondCountDown));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(FailedState(error: e.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void _onClose(OnClose event, Emitter<TwoGangState> emit) {
|
||||
_timer?.cancel();
|
||||
}
|
||||
|
||||
void _onStartTimer(int seconds) {
|
||||
_timer?.cancel();
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
add(TickTimer(seconds - timer.tick));
|
||||
});
|
||||
}
|
||||
|
||||
void _onTickTimer(TickTimer event, Emitter<TwoGangState> emit) {
|
||||
if (event.remainingTime > 0) {
|
||||
emit(TimerRunInProgress(event.remainingTime));
|
||||
} else {
|
||||
_timer?.cancel();
|
||||
emit(TimerRunComplete());
|
||||
}
|
||||
}
|
||||
|
||||
List<Map<String, String>> days = [
|
||||
{"day": "Sun", "key": "Sun"},
|
||||
{"day": "Mon", "key": "Mon"},
|
||||
{"day": "Tue", "key": "Tue"},
|
||||
{"day": "Wed", "key": "Wed"},
|
||||
{"day": "Thu", "key": "Thu"},
|
||||
{"day": "Fri", "key": "Fri"},
|
||||
{"day": "Sat", "key": "Sat"},
|
||||
];
|
||||
|
||||
Future<void> toggleDaySelection(
|
||||
ToggleDaySelectionEvent event,
|
||||
Emitter<TwoGangState> emit,
|
||||
) async {
|
||||
emit(LoadingInitialState());
|
||||
if (selectedDays.contains(event.key)) {
|
||||
selectedDays.remove(event.key);
|
||||
} else {
|
||||
selectedDays.add(event.key);
|
||||
}
|
||||
emit(ChangeTimeState());
|
||||
add(ChangeSlidingSegment(value: 1));
|
||||
}
|
||||
|
||||
Future<void> saveSchedule(TwoGangSave event, Emitter<TwoGangState> emit,) async {
|
||||
try {
|
||||
if(selectedDays.isNotEmpty) {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.postSchedule(
|
||||
category: switchCode,
|
||||
deviceId: twoGangId,
|
||||
time: getTimeStampWithoutSeconds(selectedTime).toString(),
|
||||
code: switchCode,
|
||||
value: toggleSchedule,
|
||||
days: selectedDays);
|
||||
CustomSnackBar.displaySnackBar('Save Successfully');
|
||||
add(GetScheduleEvent());
|
||||
emit(TwoGangSaveSchedule());
|
||||
toggleCreateSchedule();
|
||||
}else{
|
||||
CustomSnackBar.displaySnackBar('Please select days');
|
||||
}
|
||||
} catch (e) {
|
||||
emit(FailedState(error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getSchedule(GetScheduleEvent event, Emitter<TwoGangState> emit,) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.getSchedule(
|
||||
category: switchCode,
|
||||
deviceId: twoGangId,
|
||||
);
|
||||
List<dynamic> jsonData = response;
|
||||
listSchedule = jsonData.map((item) => ScheduleModel.fromJson(item)).toList();
|
||||
emit(InitialState());
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['message'];
|
||||
emit(FailedState(error: errorMessage.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
int? getTimeStampWithoutSeconds(DateTime? dateTime) {
|
||||
if (dateTime == null) return null;
|
||||
DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month,
|
||||
dateTime.day, dateTime.hour, dateTime.minute);
|
||||
return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000;
|
||||
}
|
||||
|
||||
Future toggleRepeat(
|
||||
ToggleScheduleEvent event, Emitter<TwoGangState> emit) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.changeSchedule(
|
||||
scheduleId: event.id,
|
||||
deviceUuid: twoGangId,
|
||||
enable: event.toggle);
|
||||
if (response == true) {
|
||||
add(GetScheduleEvent());
|
||||
toggleSchedule = event.toggle;
|
||||
return toggleSchedule;
|
||||
}
|
||||
emit(IsToggleState(onOff: toggleSchedule));
|
||||
add(const ChangeSlidingSegment(value: 1));
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['message'];
|
||||
emit(FailedState(error: errorMessage.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future deleteSchedule(
|
||||
DeleteScheduleEvent event, Emitter<TwoGangState> emit) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.deleteSchedule(
|
||||
scheduleId: event.id,
|
||||
deviceUuid: twoGangId, );
|
||||
if (response == true) {
|
||||
add(GetScheduleEvent());
|
||||
return toggleSchedule;
|
||||
}
|
||||
emit(IsToggleState(onOff: toggleSchedule));
|
||||
add(const ChangeSlidingSegment(value: 1));
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['message'];
|
||||
emit(FailedState(error: errorMessage.toString()));
|
||||
}
|
||||
}
|
||||
}
|
130
lib/features/devices/bloc/two_gang_bloc/two_gang_event.dart
Normal file
@ -0,0 +1,130 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class TwoGangEvent extends Equatable {
|
||||
const TwoGangEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class LoadingEvent extends TwoGangEvent {}
|
||||
|
||||
class TwoGangUpdated extends TwoGangEvent {}
|
||||
class TwoGangSave extends TwoGangEvent {}
|
||||
class ToggleScheduleEvent extends TwoGangEvent {
|
||||
final String id;
|
||||
final bool toggle;
|
||||
const ToggleScheduleEvent({required this.toggle,required this.id});
|
||||
@override
|
||||
List<Object> get props => [toggle,id];
|
||||
}
|
||||
class errorMessage extends TwoGangEvent {}
|
||||
class ToggleDaySelectionEvent extends TwoGangEvent {
|
||||
|
||||
final String key;
|
||||
|
||||
const ToggleDaySelectionEvent({required this.key});
|
||||
@override
|
||||
List<Object> get props => [key];
|
||||
}
|
||||
|
||||
class InitialEvent extends TwoGangEvent {
|
||||
final bool groupScreen;
|
||||
const InitialEvent({required this.groupScreen});
|
||||
@override
|
||||
List<Object> get props => [groupScreen];
|
||||
}
|
||||
|
||||
class ChangeFirstSwitchStatusEvent extends TwoGangEvent {
|
||||
final bool value;
|
||||
final String deviceId;
|
||||
const ChangeFirstSwitchStatusEvent({required this.value, this.deviceId = ''});
|
||||
@override
|
||||
List<Object> get props => [value, deviceId];
|
||||
}
|
||||
|
||||
class ChangeSecondSwitchStatusEvent extends TwoGangEvent {
|
||||
final bool value;
|
||||
final String deviceId;
|
||||
const ChangeSecondSwitchStatusEvent({required this.value, this.deviceId = ''});
|
||||
@override
|
||||
List<Object> get props => [value, deviceId];
|
||||
}
|
||||
|
||||
|
||||
class AllOffEvent extends TwoGangEvent {}
|
||||
|
||||
class AllOnEvent extends TwoGangEvent {}
|
||||
|
||||
class GroupAllOnEvent extends TwoGangEvent {}
|
||||
|
||||
class GroupAllOffEvent extends TwoGangEvent {}
|
||||
|
||||
|
||||
// two_gang_event.dart
|
||||
class ToggleCreateScheduleEvent extends TwoGangEvent {}
|
||||
|
||||
|
||||
|
||||
|
||||
class ChangeSlidingSegment extends TwoGangEvent {
|
||||
final int value;
|
||||
const ChangeSlidingSegment({required this.value});
|
||||
@override
|
||||
List<Object> get props => [value];
|
||||
}
|
||||
|
||||
class GetCounterEvent extends TwoGangEvent {
|
||||
final String deviceCode;
|
||||
const GetCounterEvent({required this.deviceCode});
|
||||
@override
|
||||
List<Object> get props => [deviceCode];
|
||||
}
|
||||
|
||||
class SetCounterValue extends TwoGangEvent {
|
||||
final Duration duration;
|
||||
final String deviceCode;
|
||||
const SetCounterValue({required this.duration, required this.deviceCode});
|
||||
@override
|
||||
List<Object> get props => [duration, deviceCode];
|
||||
}
|
||||
|
||||
class StartTimer extends TwoGangEvent {
|
||||
final int duration;
|
||||
|
||||
const StartTimer(this.duration);
|
||||
|
||||
@override
|
||||
List<Object> get props => [duration];
|
||||
}
|
||||
|
||||
class TickTimer extends TwoGangEvent {
|
||||
final int remainingTime;
|
||||
|
||||
const TickTimer(this.remainingTime);
|
||||
|
||||
@override
|
||||
List<Object> get props => [remainingTime];
|
||||
}
|
||||
|
||||
class StopTimer extends TwoGangEvent {}
|
||||
|
||||
class OnClose extends TwoGangEvent {}
|
||||
|
||||
|
||||
class GetScheduleEvent extends TwoGangEvent {}
|
||||
class DeleteScheduleEvent extends TwoGangEvent {
|
||||
final String id;
|
||||
const DeleteScheduleEvent({required this.id});
|
||||
@override
|
||||
List<Object> get props => [id];
|
||||
}
|
||||
class TabChangedEvent extends TwoGangEvent {
|
||||
final int index;
|
||||
TabChangedEvent({required this.index});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
92
lib/features/devices/bloc/two_gang_bloc/two_gang_state.dart
Normal file
@ -0,0 +1,92 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_app/features/devices/model/groupTwoGangModel.dart';
|
||||
import 'package:syncrow_app/features/devices/model/two_gang_model.dart';
|
||||
|
||||
|
||||
class TwoGangState extends Equatable {
|
||||
const TwoGangState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class InitialState extends TwoGangState {}
|
||||
class TwoGangSaveSchedule extends TwoGangState {}
|
||||
class ChangeTimeState extends TwoGangState {}
|
||||
|
||||
class LoadingInitialState extends TwoGangState {}
|
||||
|
||||
class UpdateState extends TwoGangState {
|
||||
final TwoGangModel twoGangModel;
|
||||
const UpdateState({required this.twoGangModel});
|
||||
|
||||
@override
|
||||
List<Object> get props => [TwoGangModel];
|
||||
}
|
||||
|
||||
class LoadingNewSate extends TwoGangState {
|
||||
final TwoGangModel twoGangModel;
|
||||
const LoadingNewSate({required this.twoGangModel});
|
||||
|
||||
@override
|
||||
List<Object> get props => [TwoGangModel];
|
||||
}
|
||||
|
||||
class UpdateGroupState extends TwoGangState {
|
||||
final List<GroupTwoGangModel> twoGangList;
|
||||
final bool allSwitches;
|
||||
|
||||
const UpdateGroupState({required this.twoGangList, required this.allSwitches});
|
||||
|
||||
@override
|
||||
List<Object> get props => [twoGangList, allSwitches];
|
||||
}
|
||||
|
||||
class FailedState extends TwoGangState {
|
||||
final String error;
|
||||
|
||||
const FailedState({required this.error});
|
||||
|
||||
@override
|
||||
List<Object> get props => [error];
|
||||
}
|
||||
|
||||
class ChangeSlidingSegmentState extends TwoGangState {
|
||||
final int value;
|
||||
|
||||
const ChangeSlidingSegmentState({required this.value});
|
||||
|
||||
@override
|
||||
List<Object> get props => [value];
|
||||
}
|
||||
|
||||
class UpdateTimerState extends TwoGangState {
|
||||
final int seconds;
|
||||
const UpdateTimerState({required this.seconds});
|
||||
|
||||
@override
|
||||
List<Object> get props => [seconds];
|
||||
}
|
||||
|
||||
class TimerRunInProgress extends TwoGangState {
|
||||
final int remainingTime;
|
||||
|
||||
const TimerRunInProgress(this.remainingTime);
|
||||
|
||||
@override
|
||||
List<Object> get props => [remainingTime];
|
||||
}
|
||||
|
||||
class TimerRunComplete extends TwoGangState {}
|
||||
class IsToggleState extends TwoGangState {
|
||||
final bool? onOff;
|
||||
const IsToggleState({this.onOff});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// two_gang_state.dart
|
||||
class UpdateCreateScheduleState extends TwoGangState {
|
||||
final bool createSchedule;
|
||||
UpdateCreateScheduleState(this.createSchedule);
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import 'package:firebase_database/firebase_database.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_event.dart';
|
||||
@ -16,6 +17,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
on<InitialEvent>(_fetchCeilingSensorStatus);
|
||||
on<ChangeIndicatorEvent>(_changeIndicator);
|
||||
on<ChangeValueEvent>(_changeValue);
|
||||
on<WallSensorUpdatedEvent>(_wallSensorUpdated);
|
||||
}
|
||||
|
||||
void _fetchCeilingSensorStatus(InitialEvent event, Emitter<WallSensorState> emit) async {
|
||||
@ -28,12 +30,36 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
}
|
||||
deviceStatus = WallSensorModel.fromJson(statusModelList);
|
||||
emit(UpdateState(wallSensorModel: deviceStatus));
|
||||
_listenToChanges();
|
||||
} catch (e) {
|
||||
emit(FailedState(error: 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 = WallSensorModel.fromJson(statusList);
|
||||
add(WallSensorUpdatedEvent());
|
||||
});
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
_wallSensorUpdated(WallSensorUpdatedEvent event, Emitter<WallSensorState> emit) {
|
||||
emit(UpdateState(wallSensorModel: deviceStatus));
|
||||
}
|
||||
|
||||
void _changeIndicator(ChangeIndicatorEvent event, Emitter<WallSensorState> emit) async {
|
||||
emit(LoadingNewSate(wallSensorModel: deviceStatus));
|
||||
try {
|
||||
|
@ -11,6 +11,8 @@ class LoadingEvent extends WallSensorEvent {}
|
||||
|
||||
class InitialEvent extends WallSensorEvent {}
|
||||
|
||||
class WallSensorUpdatedEvent extends WallSensorEvent {}
|
||||
|
||||
class ChangeIndicatorEvent extends WallSensorEvent {
|
||||
final bool value;
|
||||
const ChangeIndicatorEvent({required this.value});
|
||||
|
@ -4,28 +4,47 @@ class CeilingSensorModel {
|
||||
String presenceState;
|
||||
int sensitivity;
|
||||
String checkingResult;
|
||||
int presenceRange;
|
||||
int sportsPara;
|
||||
String bodyMovement;
|
||||
|
||||
CeilingSensorModel({
|
||||
required this.presenceState,
|
||||
required this.sensitivity,
|
||||
required this.checkingResult,
|
||||
});
|
||||
CeilingSensorModel(
|
||||
{required this.presenceState,
|
||||
required this.sensitivity,
|
||||
required this.checkingResult,
|
||||
required this.presenceRange,
|
||||
required this.sportsPara,
|
||||
required this.bodyMovement});
|
||||
|
||||
factory CeilingSensorModel.fromJson(List<StatusModel> jsonList) {
|
||||
late String _presenceState;
|
||||
late int _sensitivity;
|
||||
late String _checkingResult;
|
||||
int _presenceRange = 1;
|
||||
int _sportsPara = 1;
|
||||
String _bodyMovement = 'none';
|
||||
|
||||
for (int i = 0; i < jsonList.length; i++) {
|
||||
if (jsonList[i].code == 'presence_state') {
|
||||
_presenceState = jsonList[i].value ?? 'none';
|
||||
} else if (jsonList[i].code == 'sensitivity') {
|
||||
_sensitivity = jsonList[i].value ?? false;
|
||||
_sensitivity = jsonList[i].value ?? 1;
|
||||
} 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(
|
||||
presenceState: _presenceState, sensitivity: _sensitivity, checkingResult: _checkingResult);
|
||||
presenceState: _presenceState,
|
||||
sensitivity: _sensitivity,
|
||||
checkingResult: _checkingResult,
|
||||
presenceRange: _presenceRange,
|
||||
sportsPara: _sportsPara,
|
||||
bodyMovement: _bodyMovement);
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,10 @@ class DeviceModel {
|
||||
tempIcon = Assets.assetsIcons3GangSwitch;
|
||||
} else if (type == DeviceType.Gateway) {
|
||||
tempIcon = Assets.assetsIconsGateway;
|
||||
} else if (type == DeviceType.OneGang) {
|
||||
tempIcon = Assets.oneGang;
|
||||
} else if (type == DeviceType.TwoGang) {
|
||||
tempIcon = Assets.twoGang;
|
||||
} else {
|
||||
tempIcon = Assets.assetsIconsLogo;
|
||||
}
|
||||
|
13
lib/features/devices/model/groupTwoGangModel.dart
Normal file
@ -0,0 +1,13 @@
|
||||
class GroupTwoGangModel {
|
||||
final String deviceId;
|
||||
final String deviceName;
|
||||
bool firstSwitch;
|
||||
bool secondSwitch;
|
||||
|
||||
GroupTwoGangModel({
|
||||
required this.deviceId,
|
||||
required this.deviceName,
|
||||
required this.firstSwitch,
|
||||
required this.secondSwitch,
|
||||
});
|
||||
}
|
30
lib/features/devices/model/one_gang_model.dart
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
import 'package:syncrow_app/features/devices/model/status_model.dart';
|
||||
|
||||
class OneGangModel {
|
||||
bool firstSwitch;
|
||||
int firstCountDown;
|
||||
|
||||
OneGangModel(
|
||||
{required this.firstSwitch,
|
||||
required this.firstCountDown,
|
||||
});
|
||||
|
||||
factory OneGangModel.fromJson(List<StatusModel> jsonList) {
|
||||
late bool _switch;
|
||||
late int _count;
|
||||
|
||||
|
||||
for (int i = 0; i < jsonList.length; i++) {
|
||||
if (jsonList[i].code == 'switch_1') {
|
||||
_switch = jsonList[i].value ?? false;
|
||||
} else if (jsonList[i].code == 'countdown_1') {
|
||||
_count = jsonList[i].value ?? 0;
|
||||
}
|
||||
}
|
||||
return OneGangModel(
|
||||
firstSwitch: _switch,
|
||||
firstCountDown: _count,
|
||||
);
|
||||
}
|
||||
}
|
83
lib/features/devices/model/schedule_model.dart
Normal file
@ -0,0 +1,83 @@
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class ScheduleModel {
|
||||
String category;
|
||||
bool enable;
|
||||
ScheduleFunctionData function;
|
||||
String time;
|
||||
String scheduleId;
|
||||
String timezoneId;
|
||||
List<String> days;
|
||||
|
||||
ScheduleModel({
|
||||
required this.category,
|
||||
required this.enable,
|
||||
required this.function,
|
||||
required this.time,
|
||||
required this.scheduleId,
|
||||
required this.timezoneId,
|
||||
required this.days,
|
||||
});
|
||||
|
||||
// Factory method to create an instance from JSON
|
||||
factory ScheduleModel.fromJson(Map<String, dynamic> json) {
|
||||
return ScheduleModel(
|
||||
category: json['category'],
|
||||
enable: json['enable'],
|
||||
function: ScheduleFunctionData.fromJson(json['function']),
|
||||
time:getTimeIn12HourFormat( json['time']),
|
||||
scheduleId: json['scheduleId'],
|
||||
timezoneId: json['timezoneId'],
|
||||
days: List<String>.from(json['days']),
|
||||
);
|
||||
}
|
||||
|
||||
// Method to convert an instance into JSON
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'category': category,
|
||||
'enable': enable,
|
||||
'function': function.toJson(),
|
||||
'time': time,
|
||||
'scheduleId': scheduleId,
|
||||
'timezoneId': timezoneId,
|
||||
'days': days,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ScheduleFunctionData {
|
||||
String code;
|
||||
bool value;
|
||||
|
||||
ScheduleFunctionData({
|
||||
required this.code,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
// Factory method to create an instance from JSON
|
||||
factory ScheduleFunctionData.fromJson(Map<String, dynamic> json) {
|
||||
return ScheduleFunctionData(
|
||||
code: json['code'],
|
||||
value: json['value'],
|
||||
);
|
||||
}
|
||||
|
||||
// Method to convert an instance into JSON
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'code': code,
|
||||
'value': value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
String getTimeIn12HourFormat(time) {
|
||||
// Parse the time string (in HH:mm format)
|
||||
final DateFormat inputFormat = DateFormat("HH:mm");
|
||||
final DateFormat outputFormat = DateFormat("h:mm a"); // 12-hour format with AM/PM
|
||||
|
||||
// Convert the time string
|
||||
DateTime parsedTime = inputFormat.parse(time);
|
||||
return outputFormat.format(parsedTime);
|
||||
}
|
40
lib/features/devices/model/two_gang_model.dart
Normal file
@ -0,0 +1,40 @@
|
||||
import 'package:syncrow_app/features/devices/model/status_model.dart';
|
||||
|
||||
class TwoGangModel {
|
||||
bool firstSwitch;
|
||||
bool secondSwitch;
|
||||
int firstCountDown;
|
||||
int secondCountDown;
|
||||
|
||||
TwoGangModel(
|
||||
{required this.firstSwitch,
|
||||
required this.secondSwitch,
|
||||
required this.firstCountDown,
|
||||
required this.secondCountDown,
|
||||
});
|
||||
|
||||
factory TwoGangModel.fromJson(List<StatusModel> jsonList) {
|
||||
late bool _firstSwitch;
|
||||
late bool _secondSwitch;
|
||||
late int _firstCount;
|
||||
late int _secondCount;
|
||||
|
||||
for (int i = 0; i < jsonList.length; i++) {
|
||||
if (jsonList[i].code == 'switch_1') {
|
||||
_firstSwitch = jsonList[i].value ?? false;
|
||||
} else if (jsonList[i].code == 'switch_2') {
|
||||
_secondSwitch = jsonList[i].value ?? false;
|
||||
}else if (jsonList[i].code == 'countdown_1') {
|
||||
_firstCount = jsonList[i].value ?? 0;
|
||||
} else if (jsonList[i].code == 'countdown_2') {
|
||||
_secondCount = jsonList[i].value ?? 0;
|
||||
}
|
||||
}
|
||||
return TwoGangModel(
|
||||
firstSwitch: _firstSwitch,
|
||||
secondSwitch: _secondSwitch,
|
||||
firstCountDown: _firstCount,
|
||||
secondCountDown: _secondCount,
|
||||
);
|
||||
}
|
||||
}
|
@ -35,9 +35,9 @@ class WallSensorModel {
|
||||
if (jsonList[i].code == 'presence_state') {
|
||||
_presenceState = jsonList[i].value ?? 'none';
|
||||
} else if (jsonList[i].code == 'far_detection') {
|
||||
_farDetection = jsonList[i].value ?? false;
|
||||
_farDetection = jsonList[i].value ?? 0;
|
||||
} 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') {
|
||||
_motionSensitivity = jsonList[i].value ?? 0;
|
||||
} else if (jsonList[i].code == 'motionless_sensitivity') {
|
||||
@ -47,7 +47,7 @@ class WallSensorModel {
|
||||
} else if (jsonList[i].code == 'illuminance_value') {
|
||||
_illuminance = jsonList[i].value ?? 0;
|
||||
} else if (jsonList[i].code == 'indicator') {
|
||||
_indicator = jsonList[i].value ?? 0;
|
||||
_indicator = jsonList[i].value ?? false;
|
||||
}
|
||||
}
|
||||
return WallSensorModel(
|
||||
|
@ -34,8 +34,13 @@ class CeilingSensorInterface extends StatelessWidget {
|
||||
create: (context) =>
|
||||
CeilingSensorBloc(deviceId: ceilingSensor.uuid ?? '')..add(InitialEvent()),
|
||||
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(builder: (context, state) {
|
||||
CeilingSensorModel ceilingSensorModel =
|
||||
CeilingSensorModel(presenceState: 'none', sensitivity: 1, checkingResult: '');
|
||||
CeilingSensorModel ceilingSensorModel = CeilingSensorModel(
|
||||
presenceState: 'none',
|
||||
sensitivity: 1,
|
||||
checkingResult: '',
|
||||
presenceRange: 1,
|
||||
sportsPara: 1,
|
||||
bodyMovement: 'none');
|
||||
if (state is UpdateState) {
|
||||
ceilingSensorModel = state.ceilingSensorModel;
|
||||
} else if (state is LoadingNewSate) {
|
||||
@ -178,7 +183,8 @@ class CeilingSensorInterface extends StatelessWidget {
|
||||
children: [
|
||||
const BodySmall(text: 'Sports Para'),
|
||||
BodyLarge(
|
||||
text: '0',
|
||||
text: ceilingSensorModel.sportsPara
|
||||
.toString(),
|
||||
style: context.bodyLarge.copyWith(
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
@ -204,7 +210,8 @@ class CeilingSensorInterface extends StatelessWidget {
|
||||
textOverflow: TextOverflow.ellipsis,
|
||||
),
|
||||
BodyLarge(
|
||||
text: '0.0M',
|
||||
text:
|
||||
'${ceilingSensorModel.presenceRange}M',
|
||||
textOverflow: TextOverflow.ellipsis,
|
||||
style: context.bodyLarge.copyWith(
|
||||
fontWeight: FontsManager.bold,
|
||||
@ -231,7 +238,7 @@ class CeilingSensorInterface extends StatelessWidget {
|
||||
textOverflow: TextOverflow.ellipsis,
|
||||
),
|
||||
BodyLarge(
|
||||
text: 'none',
|
||||
text: ceilingSensorModel.bodyMovement,
|
||||
textOverflow: TextOverflow.ellipsis,
|
||||
style: context.bodyLarge.copyWith(
|
||||
fontWeight: FontsManager.bold,
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// }
|
@ -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 {
|
||||
const CurtainButtons({super.key, required this.curtain});
|
||||
final DeviceModel curtain;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
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).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(OpenCurtain(curtain.productType!)),
|
||||
iconPath: Assets.assetsIconsCurtainsIconOpenCurtain,
|
||||
),
|
||||
_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(
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
spreadRadius: 1,
|
||||
blurRadius: 5,
|
||||
offset: const Offset(3, 3),
|
||||
),
|
||||
]),
|
||||
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,
|
||||
),
|
||||
onTap: onTap,
|
||||
child: const SizedBox.square(dimension: 60),
|
||||
),
|
||||
),
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
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: 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,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 5, bottom: 5),
|
||||
child: InkWell(
|
||||
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||
onTap: onTap,
|
||||
child: isSvg
|
||||
? SvgPicture.asset(iconPath, width: 110, height: 110)
|
||||
: Image.asset(iconPath, width: 60, height: 60),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -1,86 +1,99 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/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/view/widgets/curtains/curtain_buttons.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
|
||||
part "curtain_buttons.dart";
|
||||
|
||||
class CurtainView extends StatelessWidget {
|
||||
const CurtainView({super.key, required this.curtain});
|
||||
final DeviceModel curtain;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => DevicesCubit.getInstance(),
|
||||
child: BlocBuilder<DevicesCubit, DevicesState>(
|
||||
builder: (context, state) => 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,
|
||||
create: (context) => CurtainBloc(curtain.uuid!)..add(InitCurtain()),
|
||||
child: BlocBuilder<CurtainBloc, CurtainState>(
|
||||
builder: (context, state) {
|
||||
double curtainWidth = 270;
|
||||
double blindHeight = 310;
|
||||
if (state is CurtainsOpening) {
|
||||
curtainWidth = state.curtainWidth;
|
||||
blindHeight = state.blindHeight;
|
||||
} else if (state is CurtainsClosing) {
|
||||
curtainWidth = state.curtainWidth;
|
||||
blindHeight = state.blindHeight;
|
||||
} else if (state is CurtainsPaused) {
|
||||
curtainWidth = state.curtainWidth;
|
||||
blindHeight = state.blindHeight;
|
||||
}
|
||||
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: const EdgeInsets.all(40),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.linear,
|
||||
height: 310,
|
||||
width: DevicesCubit.getInstance().curtainWidth,
|
||||
child: Stack(
|
||||
children: List.generate(
|
||||
10,
|
||||
(index) {
|
||||
double spacing =
|
||||
DevicesCubit.getInstance().curtainWidth / 9;
|
||||
double leftMostPosition = index * spacing;
|
||||
return AnimatedPositioned(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.linear,
|
||||
left: leftMostPosition,
|
||||
child: SizedBox(
|
||||
height: 320,
|
||||
width: 32,
|
||||
child: SvgPicture.asset(
|
||||
Assets.assetsIconsCurtainsIconVerticalBlade,
|
||||
fit: BoxFit.fill,
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(40),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.linear,
|
||||
height: 310,
|
||||
width: curtainWidth,
|
||||
child: Stack(
|
||||
children: List.generate(
|
||||
10, (index) {
|
||||
double spacing = curtainWidth / 9;
|
||||
double leftMostPosition = index * spacing;
|
||||
return AnimatedPositioned(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.linear,
|
||||
left: leftMostPosition,
|
||||
child: SizedBox(
|
||||
height: 320,
|
||||
width: 32,
|
||||
child: SvgPicture.asset(
|
||||
Assets.assetsIconsCurtainsIconVerticalBlade,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
Positioned(
|
||||
top: 27,
|
||||
left: 43,
|
||||
child: SvgPicture.asset(
|
||||
Assets.assetsIconsCurtainsIconCurtainHolder)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
CurtainButtons(
|
||||
curtain: curtain,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Assets.assetsIconsCurtainsIconCurtainHolder,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
CurtainButtons(curtain: curtain),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ class DevicesViewBody extends StatelessWidget {
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: PageView(
|
||||
controller: HomeCubit.getInstance().devicesPageController,
|
||||
@ -78,8 +79,7 @@ class DevicesViewBody extends StatelessWidget {
|
||||
),
|
||||
if (HomeCubit.getInstance().selectedSpace != null)
|
||||
if (HomeCubit.getInstance().selectedSpace!.rooms != null)
|
||||
...HomeCubit.getInstance().selectedSpace!.rooms!.map(
|
||||
(room) {
|
||||
...HomeCubit.getInstance().selectedSpace!.rooms!.map((room) {
|
||||
return RoomPage(
|
||||
room: room,
|
||||
);
|
||||
|
@ -0,0 +1,68 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_screen.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_screen.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/constants.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
|
||||
class OneGangInterface extends StatelessWidget {
|
||||
const OneGangInterface({super.key, this.gangSwitch});
|
||||
|
||||
final DeviceModel? gangSwitch;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnnotatedRegion(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: ColorsManager.backgroundColor,
|
||||
extendBodyBehindAppBar: true,
|
||||
extendBody: true,
|
||||
appBar: gangSwitch != null
|
||||
? DeviceAppbar(
|
||||
deviceName: gangSwitch!.name!,
|
||||
deviceUuid: gangSwitch!.uuid!,
|
||||
)
|
||||
: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
centerTitle: true,
|
||||
title: BodyLarge(
|
||||
text: gangSwitch?.name ?? 'Lights',
|
||||
fontColor: ColorsManager.primaryColor,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
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(
|
||||
left: Constants.defaultPadding,
|
||||
right: Constants.defaultPadding,
|
||||
bottom: Constants.bottomNavBarHeight,
|
||||
),
|
||||
child: OneGangScreen(device: gangSwitch),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
171
lib/features/devices/view/widgets/one_gang/one_gang_screen.dart
Normal file
@ -0,0 +1,171 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/one_gang_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/three_gang/gang_switch.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.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';
|
||||
import '../../../bloc/one_gang_bloc/one_gang_event.dart';
|
||||
|
||||
class OneGangScreen extends StatelessWidget {
|
||||
const OneGangScreen({super.key, this.device});
|
||||
|
||||
final DeviceModel? device;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => OneGangBloc(
|
||||
switchCode: 'switch_1',
|
||||
oneGangId : device?.uuid ?? '')
|
||||
..add(const InitialEvent(groupScreen:false)),
|
||||
child: BlocBuilder<OneGangBloc, OneGangState>(
|
||||
builder: (context, state) {
|
||||
OneGangModel oneGangModel = OneGangModel(
|
||||
firstSwitch: false,
|
||||
firstCountDown: 0,
|
||||
);
|
||||
|
||||
if (state is LoadingNewSate) {
|
||||
oneGangModel = state.oneGangModel;
|
||||
} else if (state is UpdateState) {
|
||||
oneGangModel = state.oneGangModel;
|
||||
}
|
||||
return state is LoadingInitialState ?
|
||||
const Center(
|
||||
child:
|
||||
DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()),
|
||||
):
|
||||
RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
BlocProvider.of<OneGangBloc>(context)
|
||||
.add(InitialEvent(groupScreen: device != null ? false : true));
|
||||
},
|
||||
child: ListView(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Expanded(child: SizedBox.shrink()),
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
GangSwitch(
|
||||
threeGangSwitch: device!,
|
||||
value: oneGangModel.firstSwitch,
|
||||
action: () {
|
||||
BlocProvider.of<OneGangBloc>(context).add(
|
||||
ChangeFirstSwitchStatusEvent(
|
||||
value: oneGangModel.firstSwitch));
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(
|
||||
width: 70,
|
||||
child: BodySmall(
|
||||
text:" Entrance Light",
|
||||
fontColor: ColorsManager.textPrimaryColor,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Card(
|
||||
elevation: 3,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) => TimerScheduleScreen(
|
||||
switchCode:'switch_1' ,
|
||||
device: device!,
|
||||
deviceCode: 'countdown_1',
|
||||
)));
|
||||
},
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.access_time,
|
||||
color:
|
||||
ColorsManager.primaryColorWithOpacity,
|
||||
size: 25,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BodySmall(
|
||||
text: "Timer",
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.textPrimaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child: SizedBox())
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,258 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_event.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/create_schedule.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/schedule_list.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
|
||||
|
||||
class TimerScheduleScreen extends StatelessWidget {
|
||||
final DeviceModel device;
|
||||
final String deviceCode;
|
||||
final String switchCode;
|
||||
const TimerScheduleScreen(
|
||||
{required this.device,
|
||||
required this.deviceCode,
|
||||
required this.switchCode,
|
||||
super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnnotatedRegion(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
child: BlocProvider(
|
||||
create: (context) =>
|
||||
OneGangBloc(switchCode: switchCode, oneGangId: device.uuid ?? '')
|
||||
..add(GetCounterEvent(deviceCode: deviceCode))
|
||||
..add(GetScheduleEvent()),
|
||||
child: BlocBuilder<OneGangBloc, OneGangState>(
|
||||
builder: (context, state) {
|
||||
final oneGangBloc = BlocProvider.of<OneGangBloc>(context);
|
||||
Duration duration = Duration.zero;
|
||||
int countNum = 0;
|
||||
if (state is UpdateTimerState) {
|
||||
countNum = state.seconds;
|
||||
} else if (state is TimerRunInProgress) {
|
||||
countNum = state.remainingTime;
|
||||
} else if (state is TimerRunComplete) {
|
||||
countNum = 0;
|
||||
} else if (state is LoadingNewSate) {
|
||||
countNum = 0;
|
||||
}
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (didPop) {
|
||||
if (!didPop) {
|
||||
oneGangBloc.add(OnClose());
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: DefaultTabController(
|
||||
length: 2,
|
||||
child: DefaultScaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
centerTitle: true,
|
||||
title: const BodyLarge(
|
||||
text: 'Schedule',
|
||||
fontColor: ColorsManager.primaryColor,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
actions: [
|
||||
oneGangBloc.createSchedule == true ?
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
oneGangBloc.add(ThreeGangSave());
|
||||
},
|
||||
child: const Text('Save')
|
||||
) :
|
||||
oneGangBloc.selectedTabIndex==1? IconButton(
|
||||
onPressed: () {
|
||||
oneGangBloc.toggleCreateSchedule();
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
):SizedBox(),
|
||||
],
|
||||
),
|
||||
child:
|
||||
state is LoadingInitialState?
|
||||
const Center(child: CircularProgressIndicator()):
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: const ShapeDecoration(
|
||||
color: ColorsManager.onPrimaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
),
|
||||
),
|
||||
child: TabBar(
|
||||
onTap: (value) {
|
||||
if(value==0){
|
||||
if(oneGangBloc.createSchedule == true){
|
||||
oneGangBloc.toggleCreateSchedule();
|
||||
}
|
||||
oneGangBloc.toggleSelectedIndex(0);
|
||||
}else{
|
||||
oneGangBloc.toggleSelectedIndex(1);
|
||||
}
|
||||
},
|
||||
indicatorColor: Colors.white, // Customize the indicator
|
||||
dividerHeight: 0,
|
||||
indicatorSize: TabBarIndicatorSize.tab,
|
||||
indicator: const ShapeDecoration(
|
||||
color: ColorsManager.slidingBlueColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
),
|
||||
tabs: [
|
||||
Tab(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10),
|
||||
child: BodySmall(
|
||||
text: 'Countdown',
|
||||
style: context.bodySmall.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Text(
|
||||
'Schedule',
|
||||
style: context.bodySmall.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
children: [
|
||||
Center(
|
||||
child: Container(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
countNum > 0
|
||||
? BodyLarge(
|
||||
text: _formatDuration(countNum),
|
||||
fontColor:
|
||||
ColorsManager.slidingBlueColor,
|
||||
fontSize: 40,
|
||||
)
|
||||
: CupertinoTimerPicker(
|
||||
mode: CupertinoTimerPickerMode.hm,
|
||||
onTimerDurationChanged:
|
||||
(Duration newDuration) {
|
||||
duration = newDuration;
|
||||
},
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (state is LoadingNewSate) {
|
||||
return;
|
||||
}
|
||||
if (countNum > 0) {
|
||||
oneGangBloc.add(SetCounterValue(
|
||||
deviceCode: deviceCode,
|
||||
duration: Duration.zero));
|
||||
} else if (duration != Duration.zero) {
|
||||
oneGangBloc.add(SetCounterValue(
|
||||
deviceCode: deviceCode,
|
||||
duration: duration));
|
||||
}
|
||||
},
|
||||
child: SvgPicture.asset(countNum > 0
|
||||
? Assets.pauseIcon
|
||||
: Assets.playIcon)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment:oneGangBloc.listSchedule.isNotEmpty?
|
||||
MainAxisAlignment.start:MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
child: oneGangBloc.createSchedule == true ?
|
||||
CreateSchedule(
|
||||
onToggleChanged: (bool isOn) {
|
||||
oneGangBloc.toggleSchedule = isOn;
|
||||
},
|
||||
onDateTimeChanged: (DateTime dateTime) {
|
||||
oneGangBloc.selectedTime=dateTime;
|
||||
},
|
||||
days: oneGangBloc.days,
|
||||
selectDays: (List<String> selectedDays) {
|
||||
oneGangBloc.selectedDays = selectedDays;
|
||||
},
|
||||
)
|
||||
:
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: ScheduleListView(
|
||||
listSchedule: oneGangBloc.listSchedule, // Pass the schedule list here
|
||||
onDismissed: (scheduleId) {
|
||||
oneGangBloc.listSchedule.removeWhere((schedule) => schedule.scheduleId == scheduleId);
|
||||
oneGangBloc.add(DeleteScheduleEvent(id: scheduleId));
|
||||
},
|
||||
onToggleSchedule: (scheduleId, isEnabled) {
|
||||
oneGangBloc.add(ToggleScheduleEvent(
|
||||
id: scheduleId,
|
||||
toggle: isEnabled,
|
||||
));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
))
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatDuration(int seconds) {
|
||||
final hours = (seconds ~/ 3600).toString().padLeft(2, '0');
|
||||
final minutes = ((seconds % 3600) ~/ 60).toString().padLeft(2, '0');
|
||||
final secs = (seconds % 60).toString().padLeft(2, '0');
|
||||
return '$hours:$minutes:$secs';
|
||||
}
|
||||
}
|
@ -8,8 +8,13 @@ import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/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/lights/light_interface.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_Interface.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_screen.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_Interface.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_screen.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/wall_sensor/wall_sensor_interface.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_interface.dart';
|
||||
@ -102,6 +107,11 @@ void showDeviceInterface(DeviceModel device, BuildContext context) {
|
||||
// navigateToInterface(CeilingSensorInterface(ceilingSensor: device), context);
|
||||
break;
|
||||
case DeviceType.Curtain:
|
||||
Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
CurtainView(curtain: device,)));
|
||||
break;
|
||||
case DeviceType.Blind:
|
||||
break;
|
||||
@ -120,13 +130,24 @@ void showDeviceInterface(DeviceModel device, BuildContext context) {
|
||||
break;
|
||||
case DeviceType.LightBulb:
|
||||
navigateToInterface(LightInterface(light: device), context);
|
||||
case DeviceType.OneGang:
|
||||
Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
OneGangInterface(gangSwitch: device)));
|
||||
case DeviceType.TwoGang:
|
||||
Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
TwoGangInterface(gangSwitch: device)));
|
||||
case DeviceType.ThreeGang:
|
||||
Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
ThreeGangInterface(gangSwitch: device)));
|
||||
// navigateToInterface(ThreeGangInterface(gangSwitch: device), context);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ class RoomsSlider extends StatelessWidget {
|
||||
onPageChanged: (index) {
|
||||
HomeCubit.getInstance().roomSliderPageChanged(index);
|
||||
},
|
||||
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
|
@ -1,14 +1,10 @@
|
||||
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/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_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/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/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
@ -35,40 +31,36 @@ class _DoorLockButtonState extends State<DoorLockButton> with SingleTickerProvid
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_animationController = AnimationController(
|
||||
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(() {
|
||||
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(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@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
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
@ -88,14 +80,18 @@ class _DoorLockButtonState extends State<DoorLockButton> with SingleTickerProvid
|
||||
WidgetStateProperty.all(ColorsManager.primaryColorWithOpacity.withOpacity(0.1)),
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
onTapDown: (details) {
|
||||
if (_animationController.status == AnimationStatus.dismissed) {
|
||||
_animationController.forward();
|
||||
} else if (_animationController.status == AnimationStatus.completed) {
|
||||
_animationController.reverse();
|
||||
} else if (_animationController.status == AnimationStatus.forward) {
|
||||
_animationController.reverse();
|
||||
} else if (_animationController.status == AnimationStatus.reverse) {
|
||||
_animationController.forward();
|
||||
// if (_animationController.status == AnimationStatus.dismissed) {
|
||||
// _animationController.forward();
|
||||
// } else if (_animationController.status == AnimationStatus.completed) {
|
||||
// _animationController.reverse();
|
||||
// } else if (_animationController.status == AnimationStatus.forward) {
|
||||
// _animationController.reverse();
|
||||
// } else if (_animationController.status == AnimationStatus.reverse) {
|
||||
// _animationController.forward();
|
||||
// }
|
||||
if (context.read<SmartDoorBloc>().unlockRequest > 0) {
|
||||
BlocProvider.of<SmartDoorBloc>(context)
|
||||
.add(UpdateLockEvent(value: smartDoorModel.normalOpenSwitch));
|
||||
}
|
||||
},
|
||||
onTapUp: (details) {
|
||||
|
@ -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/smart_door_model.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_grid.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/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/constants.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
|
||||
class DoorInterface extends StatelessWidget {
|
||||
|
||||
DoorInterface({super.key, required this.doorLock});
|
||||
const DoorInterface({super.key, required this.doorLock});
|
||||
|
||||
final DeviceModel doorLock;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => SmartDoorBloc(deviceId: doorLock.uuid ?? '')..add(InitialEvent()),
|
||||
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(
|
||||
listener: (context, state) {
|
||||
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(listener: (context, state) {
|
||||
if (state is FailedState) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
@ -63,32 +58,32 @@ class DoorInterface extends StatelessWidget {
|
||||
}
|
||||
|
||||
return AnnotatedRegion(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: ColorsManager.backgroundColor,
|
||||
extendBodyBehindAppBar: true,
|
||||
extendBody: true,
|
||||
appBar: DeviceAppbar(
|
||||
deviceName: doorLock.name!,
|
||||
deviceUuid: doorLock.uuid!,
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
body: Container(
|
||||
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: Scaffold(
|
||||
backgroundColor: ColorsManager.backgroundColor,
|
||||
extendBodyBehindAppBar: true,
|
||||
extendBody: true,
|
||||
appBar: DeviceAppbar(
|
||||
deviceName: doorLock.name!,
|
||||
deviceUuid: doorLock.uuid!,
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
body: Container(
|
||||
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(
|
||||
top: Constants.appBarHeight,
|
||||
left: Constants.defaultPadding,
|
||||
@ -98,38 +93,35 @@ class DoorInterface extends StatelessWidget {
|
||||
? const Center(
|
||||
child: RefreshProgressIndicator(),
|
||||
)
|
||||
:
|
||||
|
||||
RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
BlocProvider.of<SmartDoorBloc>(context).add(InitialEvent());
|
||||
},
|
||||
child:
|
||||
ListView(
|
||||
children: [
|
||||
DoorLockStatusBar(
|
||||
smartDoorModel: smartDoorModel,
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
DoorLockButton(
|
||||
doorLock: doorLock,
|
||||
smartDoorModel: smartDoorModel,
|
||||
),
|
||||
DoorLockGrid( uuid: doorLock.uuid!, ),
|
||||
|
||||
],
|
||||
)
|
||||
],
|
||||
)),
|
||||
: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
BlocProvider.of<SmartDoorBloc>(context).add(InitialEvent());
|
||||
},
|
||||
child: ListView(
|
||||
children: [
|
||||
DoorLockStatusBar(
|
||||
smartDoorModel: smartDoorModel,
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
DoorLockButton(
|
||||
doorLock: doorLock,
|
||||
smartDoorModel: smartDoorModel,
|
||||
),
|
||||
DoorLockGrid(
|
||||
uuid: doorLock.uuid!,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
));
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
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/three_gang_bloc/three_gang_bloc.dart';
|
||||
// import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_state.dart';
|
||||
// import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/two_gang_bloc.dart';
|
||||
// import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/two_gang_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
@ -63,7 +63,8 @@ class ScheduleScreen extends StatelessWidget {
|
||||
Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) => TimerScreen(
|
||||
pageBuilder: (context, animation1, animation2) => TimerScheduleScreen(
|
||||
switchCode :"switch_1",
|
||||
device: device,
|
||||
deviceCode: 'countdown_1',
|
||||
)));
|
||||
@ -99,10 +100,11 @@ class ScheduleScreen extends StatelessWidget {
|
||||
Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) => TimerScreen(
|
||||
device: device,
|
||||
deviceCode: 'countdown_2',
|
||||
)));
|
||||
pageBuilder: (context, animation1, animation2) => TimerScheduleScreen(
|
||||
switchCode :"switch_2",
|
||||
device: device,
|
||||
deviceCode: 'countdown_2',
|
||||
)));
|
||||
},
|
||||
child: Container(
|
||||
padding:
|
||||
@ -135,8 +137,9 @@ class ScheduleScreen extends StatelessWidget {
|
||||
Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) => TimerScreen(
|
||||
device: device,
|
||||
pageBuilder: (context, animation1, animation2) => TimerScheduleScreen(
|
||||
switchCode :"switch_3",
|
||||
device: device,
|
||||
deviceCode: 'countdown_3',
|
||||
)));
|
||||
},
|
||||
|
@ -22,7 +22,9 @@ class ThreeGangScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => ThreeGangBloc(threeGangId: device?.uuid ?? '')
|
||||
create: (context) => ThreeGangBloc(
|
||||
switchCode: '',
|
||||
threeGangId: device?.uuid ?? '')
|
||||
..add(InitialEvent(groupScreen: device != null ? false : true)),
|
||||
child: BlocBuilder<ThreeGangBloc, ThreeGangState>(
|
||||
builder: (context, state) {
|
||||
|
@ -7,7 +7,10 @@ import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_blo
|
||||
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_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/create_schedule.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/schedule_list.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
@ -15,10 +18,17 @@ 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/font_manager.dart';
|
||||
|
||||
class TimerScreen extends StatelessWidget {
|
||||
|
||||
|
||||
class TimerScheduleScreen extends StatelessWidget {
|
||||
final DeviceModel device;
|
||||
final String deviceCode;
|
||||
const TimerScreen({required this.device, required this.deviceCode, super.key});
|
||||
final String switchCode;
|
||||
const TimerScheduleScreen(
|
||||
{required this.device,
|
||||
required this.deviceCode,
|
||||
required this.switchCode,
|
||||
super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -27,150 +37,217 @@ class TimerScreen extends StatelessWidget {
|
||||
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: ColorsManager.backgroundColor,
|
||||
extendBodyBehindAppBar: true,
|
||||
extendBody: true,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
centerTitle: true,
|
||||
title: const BodyLarge(
|
||||
text: 'Schedule',
|
||||
fontColor: ColorsManager.primaryColor,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: BlocProvider(
|
||||
create: (context) => ThreeGangBloc(threeGangId: device.uuid ?? '')
|
||||
..add(GetCounterEvent(deviceCode: deviceCode)),
|
||||
child: BlocBuilder<ThreeGangBloc, ThreeGangState>(
|
||||
builder: (context, state) {
|
||||
Duration duration = Duration.zero;
|
||||
int countNum = 0;
|
||||
int tabNum = 0;
|
||||
if (state is UpdateTimerState) {
|
||||
countNum = state.seconds;
|
||||
} else if (state is TimerRunInProgress) {
|
||||
countNum = state.remainingTime;
|
||||
} else if (state is TimerRunComplete) {
|
||||
countNum = 0;
|
||||
} else if (state is LoadingNewSate) {
|
||||
countNum = 0;
|
||||
}
|
||||
|
||||
if (state is ChangeSlidingSegmentState) {
|
||||
tabNum = state.value;
|
||||
}
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (didPop) {
|
||||
if (!didPop) {
|
||||
BlocProvider.of<ThreeGangBloc>(context).add(OnClose());
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
height: MediaQuery.sizeOf(context).height,
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(
|
||||
Assets.assetsImagesBackground,
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
opacity: 0.4,
|
||||
),
|
||||
child: BlocProvider(
|
||||
create: (context) =>
|
||||
ThreeGangBloc(switchCode: switchCode, threeGangId: device.uuid ?? '')
|
||||
..add(GetCounterEvent(deviceCode: deviceCode))
|
||||
..add(GetScheduleEvent()),
|
||||
child: BlocBuilder<ThreeGangBloc, ThreeGangState>(
|
||||
builder: (context, state) {
|
||||
final threeGangBloc = BlocProvider.of<ThreeGangBloc>(context);
|
||||
Duration duration = Duration.zero;
|
||||
int countNum = 0;
|
||||
if (state is UpdateTimerState) {
|
||||
countNum = state.seconds;
|
||||
} else if (state is TimerRunInProgress) {
|
||||
countNum = state.remainingTime;
|
||||
} else if (state is TimerRunComplete) {
|
||||
countNum = 0;
|
||||
} else if (state is LoadingNewSate) {
|
||||
countNum = 0;
|
||||
}
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (didPop) {
|
||||
if (!didPop) {
|
||||
threeGangBloc.add(OnClose());
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: DefaultTabController(
|
||||
length: 2,
|
||||
child: DefaultScaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
centerTitle: true,
|
||||
title: const BodyLarge(
|
||||
text: 'Schedule',
|
||||
fontColor: ColorsManager.primaryColor,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
child: state is LoadingInitialState || state is LoadingNewSate
|
||||
? const Center(
|
||||
child: DefaultContainer(
|
||||
width: 50, height: 50, child: CircularProgressIndicator()),
|
||||
)
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
decoration: const ShapeDecoration(
|
||||
color: ColorsManager.slidingBlueColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
actions: [
|
||||
threeGangBloc.createSchedule == true ?
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
threeGangBloc.add(ThreeGangSave());
|
||||
},
|
||||
child: const Text('Save')
|
||||
) :
|
||||
threeGangBloc.selectedTabIndex==1? IconButton(
|
||||
onPressed: () {
|
||||
threeGangBloc.toggleCreateSchedule();
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
):SizedBox(),
|
||||
],
|
||||
),
|
||||
child:
|
||||
state is LoadingInitialState?
|
||||
const Center(child: CircularProgressIndicator()):
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: const ShapeDecoration(
|
||||
color: ColorsManager.onPrimaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
),
|
||||
),
|
||||
child: TabBar(
|
||||
onTap: (value) {
|
||||
if(value==0){
|
||||
if(threeGangBloc.createSchedule == true){
|
||||
threeGangBloc.toggleCreateSchedule();
|
||||
}
|
||||
threeGangBloc.toggleSelectedIndex(0);
|
||||
|
||||
}else{
|
||||
threeGangBloc.toggleSelectedIndex(1);
|
||||
}
|
||||
},
|
||||
indicatorColor: Colors.white, // Customize the indicator
|
||||
dividerHeight: 0,
|
||||
indicatorSize: TabBarIndicatorSize.tab,
|
||||
indicator: const ShapeDecoration(
|
||||
color: ColorsManager.slidingBlueColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
),
|
||||
tabs: [
|
||||
Tab(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10),
|
||||
child: BodySmall(
|
||||
text: 'Countdown',
|
||||
style: context.bodySmall.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
child: CupertinoSlidingSegmentedControl<int>(
|
||||
thumbColor: ColorsManager.slidingBlueColor,
|
||||
onValueChanged: (value) {
|
||||
BlocProvider.of<ThreeGangBloc>(context)
|
||||
.add(ChangeSlidingSegment(value: value ?? 0));
|
||||
},
|
||||
groupValue:
|
||||
state is ChangeSlidingSegmentState ? state.value : 0,
|
||||
backgroundColor: Colors.white,
|
||||
children: <int, Widget>{
|
||||
0: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: BodySmall(
|
||||
text: 'Countdown',
|
||||
style: context.bodySmall.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
1: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Text(
|
||||
'Schedule',
|
||||
style: context.bodySmall.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Text(
|
||||
'Schedule',
|
||||
style: context.bodySmall.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (tabNum == 0)
|
||||
countNum > 0
|
||||
? BodyLarge(
|
||||
text: _formatDuration(countNum),
|
||||
fontColor: ColorsManager.slidingBlueColor,
|
||||
fontSize: 40,
|
||||
)
|
||||
: CupertinoTimerPicker(
|
||||
mode: CupertinoTimerPickerMode.hm,
|
||||
onTimerDurationChanged: (Duration newDuration) {
|
||||
duration = newDuration;
|
||||
},
|
||||
),
|
||||
if (tabNum == 0)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (state is LoadingNewSate) {
|
||||
return;
|
||||
}
|
||||
if (countNum > 0) {
|
||||
BlocProvider.of<ThreeGangBloc>(context).add(
|
||||
SetCounterValue(
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
children: [
|
||||
Center(
|
||||
child: Container(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
countNum > 0
|
||||
? BodyLarge(
|
||||
text: _formatDuration(countNum),
|
||||
fontColor:
|
||||
ColorsManager.slidingBlueColor,
|
||||
fontSize: 40,
|
||||
)
|
||||
: CupertinoTimerPicker(
|
||||
mode: CupertinoTimerPickerMode.hm,
|
||||
onTimerDurationChanged:
|
||||
(Duration newDuration) {
|
||||
duration = newDuration;
|
||||
},
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (state is LoadingNewSate) {
|
||||
return;
|
||||
}
|
||||
if (countNum > 0) {
|
||||
threeGangBloc.add(SetCounterValue(
|
||||
deviceCode: deviceCode,
|
||||
duration: Duration.zero));
|
||||
} else if (duration != Duration.zero) {
|
||||
BlocProvider.of<ThreeGangBloc>(context).add(
|
||||
SetCounterValue(
|
||||
deviceCode: deviceCode, duration: duration));
|
||||
}
|
||||
} else if (duration != Duration.zero) {
|
||||
threeGangBloc.add(SetCounterValue(
|
||||
deviceCode: deviceCode,
|
||||
duration: duration));
|
||||
}
|
||||
},
|
||||
child: SvgPicture.asset(countNum > 0
|
||||
? Assets.pauseIcon
|
||||
: Assets.playIcon)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment:threeGangBloc.listSchedule.isNotEmpty?
|
||||
MainAxisAlignment.start:MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
child: threeGangBloc.createSchedule == true ?
|
||||
CreateSchedule(
|
||||
onToggleChanged: (bool isOn) {
|
||||
threeGangBloc.toggleSchedule = isOn;
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
countNum > 0 ? Assets.pauseIcon : Assets.playIcon))
|
||||
],
|
||||
)));
|
||||
},
|
||||
),
|
||||
),
|
||||
onDateTimeChanged: (DateTime dateTime) {
|
||||
threeGangBloc.selectedTime=dateTime;
|
||||
},
|
||||
days: threeGangBloc.days,
|
||||
selectDays: (List<String> selectedDays) {
|
||||
threeGangBloc.selectedDays = selectedDays;
|
||||
},
|
||||
)
|
||||
:
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: ScheduleListView(
|
||||
listSchedule: threeGangBloc.listSchedule, // Pass the schedule list here
|
||||
onDismissed: (scheduleId) {
|
||||
threeGangBloc.listSchedule.removeWhere((schedule) => schedule.scheduleId == scheduleId);
|
||||
threeGangBloc.add(DeleteScheduleEvent(id: scheduleId));
|
||||
},
|
||||
onToggleSchedule: (scheduleId, isEnabled) {
|
||||
threeGangBloc.add(ToggleScheduleEvent(
|
||||
id: scheduleId,
|
||||
toggle: isEnabled,
|
||||
));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
))
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -0,0 +1,67 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_screen.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/constants.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
|
||||
class TwoGangInterface extends StatelessWidget {
|
||||
const TwoGangInterface({super.key, this.gangSwitch});
|
||||
|
||||
final DeviceModel? gangSwitch;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnnotatedRegion(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: ColorsManager.backgroundColor,
|
||||
extendBodyBehindAppBar: true,
|
||||
extendBody: true,
|
||||
appBar: gangSwitch != null
|
||||
? DeviceAppbar(
|
||||
deviceName: gangSwitch!.name!,
|
||||
deviceUuid: gangSwitch!.uuid!,
|
||||
)
|
||||
: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
centerTitle: true,
|
||||
title: BodyLarge(
|
||||
text: gangSwitch?.name ?? 'Lights',
|
||||
fontColor: ColorsManager.primaryColor,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
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(
|
||||
left: Constants.defaultPadding,
|
||||
right: Constants.defaultPadding,
|
||||
bottom: Constants.bottomNavBarHeight,
|
||||
),
|
||||
child: TwoGangScreen(device: gangSwitch),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_bloc.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_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/groupTwoGangModel.dart';
|
||||
import 'package:syncrow_app/features/devices/model/group_three_gang_model.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/devices_default_switch.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
|
||||
|
||||
class TwoGangList extends StatelessWidget {
|
||||
const TwoGangList({super.key, required this.twoGangList, required this.allSwitches});
|
||||
|
||||
final List<GroupTwoGangModel> twoGangList;
|
||||
final bool allSwitches;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<ThreeGangBloc, ThreeGangState>(
|
||||
builder: (context, state) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const BodySmall(text: 'All Lights'),
|
||||
const SizedBox(height: 5),
|
||||
DevicesDefaultSwitch(
|
||||
switchValue: allSwitches,
|
||||
action: () {
|
||||
BlocProvider.of<ThreeGangBloc>(context).add(GroupAllOnEvent());
|
||||
},
|
||||
secondAction: () {
|
||||
BlocProvider.of<ThreeGangBloc>(context).add(GroupAllOffEvent());
|
||||
},
|
||||
),
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.all(0),
|
||||
itemCount: twoGangList.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
BodySmall(text: '${twoGangList[index].deviceName} beside light'),
|
||||
const SizedBox(height: 5),
|
||||
DevicesDefaultSwitch(
|
||||
switchValue: twoGangList[index].firstSwitch,
|
||||
action: () {
|
||||
BlocProvider.of<ThreeGangBloc>(context).add(ChangeFirstSwitchStatusEvent(
|
||||
value: twoGangList[index].firstSwitch,
|
||||
deviceId: twoGangList[index].deviceId));
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BodySmall(text: '${twoGangList[index].deviceName} ceiling light'),
|
||||
const SizedBox(height: 5),
|
||||
DevicesDefaultSwitch(
|
||||
switchValue: twoGangList[index].secondSwitch,
|
||||
action: () {
|
||||
BlocProvider.of<ThreeGangBloc>(context).add(ChangeSecondSwitchStatusEvent(
|
||||
value: twoGangList[index].secondSwitch,
|
||||
deviceId: twoGangList[index].deviceId));
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BodySmall(text: '${twoGangList[index].deviceName} spotlight'),
|
||||
const SizedBox(height: 5),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
339
lib/features/devices/view/widgets/two_gang/two_gang_screen.dart
Normal file
@ -0,0 +1,339 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_event.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/groupTwoGangModel.dart';
|
||||
import 'package:syncrow_app/features/devices/model/two_gang_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/three_gang/gang_switch.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_list.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_schedule_screen.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.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 TwoGangScreen extends StatelessWidget {
|
||||
const TwoGangScreen({super.key, this.device, this.switchCode});
|
||||
final DeviceModel? device;
|
||||
final String? switchCode;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => TwoGangBloc(
|
||||
switchCode: switchCode??'',
|
||||
twoGangId: device?.uuid ?? '')
|
||||
..add(InitialEvent(groupScreen: device != null ? false : true)),
|
||||
child: BlocBuilder<TwoGangBloc, TwoGangState>(
|
||||
builder: (context, state) {
|
||||
TwoGangModel twoGangModel = TwoGangModel(
|
||||
firstSwitch: false,
|
||||
secondSwitch: false,
|
||||
firstCountDown: 0,
|
||||
secondCountDown: 0,
|
||||
);
|
||||
|
||||
List<GroupTwoGangModel> groupTwoGangModel = [];
|
||||
bool allSwitchesOn = false;
|
||||
if (state is LoadingNewSate) {
|
||||
twoGangModel = state.twoGangModel;
|
||||
} else if (state is UpdateState) {
|
||||
twoGangModel = state.twoGangModel;
|
||||
} else if (state is UpdateGroupState) {
|
||||
groupTwoGangModel = state.twoGangList;
|
||||
allSwitchesOn = state.allSwitches;
|
||||
}
|
||||
return state is LoadingInitialState
|
||||
? const Center(
|
||||
child: DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()),)
|
||||
: device == null
|
||||
? TwoGangList(
|
||||
twoGangList: groupTwoGangModel,
|
||||
allSwitches: allSwitchesOn,
|
||||
)
|
||||
: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
BlocProvider.of<TwoGangBloc>(context)
|
||||
.add(InitialEvent(groupScreen: device != null ? false : true));
|
||||
},
|
||||
child: ListView(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Expanded(child: SizedBox.shrink()),
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
GangSwitch(
|
||||
threeGangSwitch: device!,
|
||||
value: twoGangModel.firstSwitch,
|
||||
action: () {
|
||||
BlocProvider.of<TwoGangBloc>(context).add(
|
||||
ChangeFirstSwitchStatusEvent(
|
||||
value: twoGangModel.firstSwitch));
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(
|
||||
width: 77,
|
||||
child: BodySmall(
|
||||
text: "Cove Light",
|
||||
fontColor: ColorsManager.textPrimaryColor,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
GangSwitch(
|
||||
threeGangSwitch: device!,
|
||||
value: twoGangModel.secondSwitch,
|
||||
action: () {
|
||||
BlocProvider.of<TwoGangBloc>(context).add(
|
||||
ChangeSecondSwitchStatusEvent(
|
||||
value: twoGangModel.secondSwitch));
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(
|
||||
width: 77,
|
||||
child: BodySmall(
|
||||
text: "Chandelier",
|
||||
fontColor: ColorsManager.textPrimaryColor,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Card(
|
||||
elevation: 3,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
BlocProvider.of<TwoGangBloc>(context)
|
||||
.add(AllOnEvent());
|
||||
},
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
child: Center(
|
||||
child: BodySmall(
|
||||
text: "On",
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager
|
||||
.primaryColorWithOpacity,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BodySmall(
|
||||
text: "All On",
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.textPrimaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Card(
|
||||
elevation: 3,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder:
|
||||
(context, animation1, animation2) =>
|
||||
TwoGangScheduleScreen(
|
||||
device: device!,
|
||||
)));
|
||||
},
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.access_time,
|
||||
color:
|
||||
ColorsManager.primaryColorWithOpacity,
|
||||
size: 25,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BodySmall(
|
||||
text: "Timer",
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.textPrimaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Card(
|
||||
elevation: 3,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// DevicesCubit.getInstance().deviceControl(
|
||||
// DeviceControlModel(
|
||||
// deviceId: device.uuid,
|
||||
// code: 'switch_1',
|
||||
// value: false,
|
||||
// ),
|
||||
// device.uuid!,
|
||||
// );
|
||||
// DevicesCubit.getInstance().deviceControl(
|
||||
// DeviceControlModel(
|
||||
// deviceId: device.uuid,
|
||||
// code: 'switch_2',
|
||||
// value: false,
|
||||
// ),
|
||||
// device.uuid!,
|
||||
// );
|
||||
// DevicesCubit.getInstance().deviceControl(
|
||||
// DeviceControlModel(
|
||||
// deviceId: device.uuid,
|
||||
// code: 'switch_3',
|
||||
// value: false,
|
||||
// ),
|
||||
// device.uuid!,
|
||||
// );
|
||||
BlocProvider.of<TwoGangBloc>(context)
|
||||
.add(AllOffEvent());
|
||||
},
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
child: Center(
|
||||
child: BodySmall(
|
||||
text: "Off",
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager
|
||||
.primaryColorWithOpacity,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BodySmall(
|
||||
text: "All Off",
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.textPrimaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child: SizedBox())
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_timer_screen.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
|
||||
class TwoGangScheduleScreen extends StatelessWidget {
|
||||
final DeviceModel device;
|
||||
const TwoGangScheduleScreen({required this.device, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnnotatedRegion(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: ColorsManager.backgroundColor,
|
||||
extendBodyBehindAppBar: true,
|
||||
extendBody: true,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
centerTitle: true,
|
||||
title: const BodyLarge(
|
||||
text: 'Schedule',
|
||||
fontColor: ColorsManager.primaryColor,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
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: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 48,
|
||||
),
|
||||
DefaultContainer(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
child: Column(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) => TimerScheduleScreen(
|
||||
device: device,
|
||||
deviceCode: 'countdown_1',
|
||||
switchCode:'switch_1' ,
|
||||
)));
|
||||
},
|
||||
child: Container(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 25, right: 15, top: 20, bottom: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
BodySmall(
|
||||
text: "Cove Light",
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.textPrimaryColor,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: ColorsManager.greyColor,
|
||||
size: 18,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
color: ColorsManager.dividerColor,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) => TimerScheduleScreen(
|
||||
device: device,
|
||||
deviceCode: 'countdown_2',
|
||||
switchCode:'switch_2' ,
|
||||
)));
|
||||
},
|
||||
child: Container(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 25, right: 15, top: 20, bottom: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
BodySmall(
|
||||
text: "Chandelier",
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.textPrimaryColor,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: ColorsManager.greyColor,
|
||||
size: 18,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
259
lib/features/devices/view/widgets/two_gang/two_timer_screen.dart
Normal file
@ -0,0 +1,259 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/create_schedule.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/schedule_list.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_event.dart';
|
||||
|
||||
class TimerScheduleScreen extends StatelessWidget {
|
||||
final DeviceModel device;
|
||||
final String deviceCode;
|
||||
final String switchCode;
|
||||
const TimerScheduleScreen(
|
||||
{required this.device,
|
||||
required this.deviceCode,
|
||||
required this.switchCode,
|
||||
super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnnotatedRegion(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
child: BlocProvider(
|
||||
create: (context) =>
|
||||
TwoGangBloc(switchCode: switchCode, twoGangId: device.uuid ?? '')
|
||||
..add(GetCounterEvent(deviceCode: deviceCode))
|
||||
..add(GetScheduleEvent()),
|
||||
child: BlocBuilder<TwoGangBloc, TwoGangState>(
|
||||
builder: (context, state) {
|
||||
final twoGangBloc = BlocProvider.of<TwoGangBloc>(context);
|
||||
Duration duration = Duration.zero;
|
||||
int countNum = 0;
|
||||
if (state is UpdateTimerState) {
|
||||
countNum = state.seconds;
|
||||
} else if (state is TimerRunInProgress) {
|
||||
countNum = state.remainingTime;
|
||||
} else if (state is TimerRunComplete) {
|
||||
countNum = 0;
|
||||
} else if (state is LoadingNewSate) {
|
||||
countNum = 0;
|
||||
}
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (didPop) {
|
||||
if (!didPop) {
|
||||
twoGangBloc.add(OnClose());
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: DefaultTabController(
|
||||
length: 2,
|
||||
child: DefaultScaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
centerTitle: true,
|
||||
title: const BodyLarge(
|
||||
text: 'Schedule',
|
||||
fontColor: ColorsManager.primaryColor,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
actions: [
|
||||
twoGangBloc.createSchedule == true ?
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
twoGangBloc.add(TwoGangSave());
|
||||
},
|
||||
child: const Text('Save')
|
||||
) :
|
||||
twoGangBloc.selectedTabIndex==1? IconButton(
|
||||
onPressed: () {
|
||||
twoGangBloc.toggleCreateSchedule();
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
):SizedBox(),
|
||||
],
|
||||
),
|
||||
child:
|
||||
state is LoadingInitialState?
|
||||
const Center(child: CircularProgressIndicator()):
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: const ShapeDecoration(
|
||||
color: ColorsManager.onPrimaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
),
|
||||
),
|
||||
child: TabBar(
|
||||
onTap: (value) {
|
||||
print(value);
|
||||
if(value==0){
|
||||
if(twoGangBloc.createSchedule == true){
|
||||
twoGangBloc.toggleCreateSchedule();
|
||||
}
|
||||
twoGangBloc.toggleSelectedIndex(0);
|
||||
|
||||
}else{
|
||||
twoGangBloc.toggleSelectedIndex(1);
|
||||
}
|
||||
},
|
||||
indicatorColor: Colors.white, // Customize the indicator
|
||||
dividerHeight: 0,
|
||||
indicatorSize: TabBarIndicatorSize.tab,
|
||||
indicator: const ShapeDecoration(
|
||||
color: ColorsManager.slidingBlueColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
),
|
||||
tabs: [
|
||||
Tab(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10),
|
||||
child: BodySmall(
|
||||
text: 'Countdown',
|
||||
style: context.bodySmall.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Text(
|
||||
'Schedule',
|
||||
style: context.bodySmall.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
children: [
|
||||
Center(
|
||||
child: Container(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
countNum > 0
|
||||
? BodyLarge(
|
||||
text: _formatDuration(countNum),
|
||||
fontColor:
|
||||
ColorsManager.slidingBlueColor,
|
||||
fontSize: 40,
|
||||
)
|
||||
: CupertinoTimerPicker(
|
||||
mode: CupertinoTimerPickerMode.hm,
|
||||
onTimerDurationChanged:
|
||||
(Duration newDuration) {
|
||||
duration = newDuration;
|
||||
},
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (state is LoadingNewSate) {
|
||||
return;
|
||||
}
|
||||
if (countNum > 0) {
|
||||
twoGangBloc.add(SetCounterValue(
|
||||
deviceCode: deviceCode,
|
||||
duration: Duration.zero));
|
||||
} else if (duration != Duration.zero) {
|
||||
twoGangBloc.add(SetCounterValue(
|
||||
deviceCode: deviceCode,
|
||||
duration: duration));
|
||||
}
|
||||
},
|
||||
child: SvgPicture.asset(countNum > 0
|
||||
? Assets.pauseIcon
|
||||
: Assets.playIcon)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment:twoGangBloc.listSchedule.isNotEmpty?
|
||||
MainAxisAlignment.start:MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
child: twoGangBloc.createSchedule == true ?
|
||||
CreateSchedule(
|
||||
onToggleChanged: (bool isOn) {
|
||||
twoGangBloc.toggleSchedule = isOn;
|
||||
},
|
||||
onDateTimeChanged: (DateTime dateTime) {
|
||||
twoGangBloc.selectedTime=dateTime;
|
||||
},
|
||||
days: twoGangBloc.days,
|
||||
selectDays: (List<String> selectedDays) {
|
||||
twoGangBloc.selectedDays = selectedDays;
|
||||
},
|
||||
)
|
||||
:
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: ScheduleListView(
|
||||
listSchedule: twoGangBloc.listSchedule, // Pass the schedule list here
|
||||
onDismissed: (scheduleId) {
|
||||
twoGangBloc.listSchedule.removeWhere((schedule) => schedule.scheduleId == scheduleId);
|
||||
twoGangBloc.add(DeleteScheduleEvent(id: scheduleId));
|
||||
},
|
||||
onToggleSchedule: (scheduleId, isEnabled) {
|
||||
twoGangBloc.add(ToggleScheduleEvent(
|
||||
id: scheduleId,
|
||||
toggle: isEnabled,
|
||||
));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
))
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatDuration(int seconds) {
|
||||
final hours = (seconds ~/ 3600).toString().padLeft(2, '0');
|
||||
final minutes = ((seconds % 3600) ~/ 60).toString().padLeft(2, '0');
|
||||
final secs = (seconds % 60).toString().padLeft(2, '0');
|
||||
return '$hours:$minutes:$secs';
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ class ManageHomeView extends StatelessWidget {
|
||||
title: 'Manage Your Home',
|
||||
child: spaces == null
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
child: BodyMedium(text: 'No spaces found'),
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
@ -30,8 +30,7 @@ class ManageHomeView extends StatelessWidget {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children:
|
||||
List.generate(
|
||||
children: List.generate(
|
||||
spaces.length,
|
||||
(index) {
|
||||
if (index == spaces.length - 1) {
|
||||
@ -43,12 +42,10 @@ class ManageHomeView extends StatelessWidget {
|
||||
)));
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
BodyMedium(
|
||||
text: StringHelpers.toTitleCase(
|
||||
spaces[index].name ?? "")),
|
||||
text: StringHelpers.toTitleCase(spaces[index].name ?? "")),
|
||||
const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: ColorsManager.greyColor,
|
||||
@ -76,14 +73,10 @@ class ManageHomeView extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
BodyMedium(
|
||||
text: HomeCubit.getInstance()
|
||||
.spaces![index]
|
||||
.name ??
|
||||
""),
|
||||
text: HomeCubit.getInstance().spaces![index].name ?? ""),
|
||||
const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: ColorsManager.greyColor,
|
||||
@ -92,8 +85,7 @@ class ManageHomeView extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
Container(
|
||||
margin:
|
||||
const EdgeInsets.symmetric(vertical: 15),
|
||||
margin: const EdgeInsets.symmetric(vertical: 15),
|
||||
height: 1,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
|