Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9d27e20

Browse files
authored
feat: iLife798接水功能 (#125)
* feat: add dorm water card and empty dorm water page - add `DormWaterCard` to homepage small-function grid - add `DormWaterWindow` as placeholder page - add i18n key for dorm water card title * feat(dorm-water): add login form UI skeleton * feat(dorm-water): add captcha image loading and refresh * feat(dorm-water): SMS sending * feat(dorm-water): complete login flow with token persistence * feat(dorm-water): login flow and device list UI * feat(dorm_water): water dispensing control and state sync Add complete water dispensing control flow with robust state synchronization: Features: - Start/end water dispensing via API with user feedback - Real-time device status polling (1-second interval, max 60 seconds) - Consecutive status detection (requires 3x status==99) to prevent network fluctuations from causing false "device ready" signals - Direct play/stop icon toggle on device card for better UX - Automatic state cleanup and counter reset on water operations UI/UX Improvements: - Remove modal dialog approach, now using inline card button control - Play icon → Stop icon during water dispensing - Real-time status feedback via toast notifications - Support for 3 languages with 9 new i18n strings Robustness: - Handles network interruptions gracefully (retries polling) - Mounted widget checks to prevent setState after unmount - Timeout protection (60 seconds max polling duration) - Proper state synchronization between UI and backend device status * fix(dorm_water): correct button text when device list loading fails * refactor(dorm-water): use teacup icon * refactor(dorm-water): generate numeric-only session ID for captcha * refactor(dorm_water): add platform-specific user agents for API requests Implement platform detection and custom User-Agent headers for all dorm water API requests to comply with backend requirements. Changes: - Add platform detection via dart:io Platform - Implement _getUserAgent() method with platform-specific UA strings: - iOS captcha: iLife798/3.1.1 (iPhone; iOS 26.2; Scale/3.00) - iOS others: iOS_ilife798_3.1.1 - Android all: Android_ilife798_2.0.11 - Other platforms: default to iOS UA - Add User-Agent header to all 6 API requests: - getCaptcha() - uses special captcha UA - sendSmsCode(), login(), getDeviceList() - startWater(), endWater(), checkDeviceStatus() * refactor(dorm-water): complete captcha request headers * refactor(dorm-water): complete login request headers * refactor(dorm-water): complete getDeviceList request headers * refactor(dorm-water): perfect startWater request query * refactor(dorm-water): complete startWater request headers * refactor(dorm-water): complete sendSmsCode request headers * refactor(dorm-water): complete endWater request headers * feat: add toggleDeviceFavorite method for device favorites management * feat(dorm-water): implement QR code scanning and device favorites management - Integrate mobile_scanner for native QR code scanning - Implement toggleDeviceFavorite() API method in DormWaterSession - Create _QrCodeScannerPage with camera and torch toggle - Extract device ID from QR code URL format (https://i.hnkzy.com/q/1/{did}) - Add scan QR code button in AppBar (qrcode_line icon left of logout) - Auto-add scanned devices to favorites and refresh device list - Add i18n strings: scan_qr_code, device_id, add_device_failed - Support zh_CN, en_US, zh_TW languages - Lower mobile_scanner version to resolve MLKit pod conflicts * feat(dorm-water): add unfavorite functionality to star icon * fix(dorm-water): prevent page navigation when removing device from favorites - Change _loadDevices() call to setState with removeWhere - Only remove the unfavorited device from list instead of full refresh - Keep user on the dorm water page after unfavoriting - Improve UX by avoiding unnecessary navigation * refactor(dorm-water): replace mobile_scanner with qr_code_dart_scan * refactor(dorm-water): regenerate platform lock/registrant files * refactor(dorm-water): fix lifecycle usage and add QR domain validation - Move `_loadCaptcha` from `initState` to `didChangeDependencies` to follow lifecycle best practices. - Add domain validation for `hnkzy.com` in QR code scanner to prevent processing invalid links.
1 parent a5f64f9 commit 9d27e20

12 files changed

Lines changed: 1607 additions & 64 deletions

File tree

assets/flutter_i18n/en_US.yaml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ homepage:
417417
experiment: "Experiments"
418418
score: "Grades"
419419
sport: "PE Info"
420+
dorm_water: "Dorm Water"
420421
schoolnet: "Schoolnet Usage"
421422
toolbox: "Others"
422423
score_cannot_reach: "Offline mode with no cached score data, unable to access"
@@ -432,6 +433,47 @@ homepage:
432433
failed: "Failed to fetch club info"
433434
fetching: "Fetching club info"
434435

436+
dorm_water:
437+
title: "Dorm Water"
438+
phone: "Phone"
439+
image_code: "Image code"
440+
sms_code: "SMS code"
441+
send_sms: "Send SMS"
442+
login: "Login"
443+
logout: "Logout"
444+
refresh_captcha: "Refresh Captcha"
445+
loading_captcha: "Loading..."
446+
captcha_error: "Failed to load captcha"
447+
phone_required: "Please enter phone number"
448+
image_code_required: "Please enter image code"
449+
sms_sent: "SMS sent successfully"
450+
sms_failed: "Failed to send SMS"
451+
sms_code_required: "Please enter SMS code"
452+
login_success: "Login successful"
453+
login_failed: "Login failed"
454+
logout_success: "Logged out successfully"
455+
devices: "Device List"
456+
loading_devices: "Loading devices..."
457+
no_devices: "No devices"
458+
select_device: "Select Device"
459+
fetch_devices_failed: "Failed to fetch device list"
460+
retry_load_devices: "Retry Loading"
461+
start_water: "Start Water"
462+
end_water: "End Water"
463+
water_dispensing: "Water Dispensing"
464+
water_status: "Water Status"
465+
start_water_success: "Water dispensing started"
466+
end_water_success: "Water dispensing ended"
467+
start_water_failed: "Failed to start water"
468+
end_water_failed: "Failed to end water"
469+
device_status_checking: "Checking device status..."
470+
device_status_ready: "Device ready"
471+
scan_qr_code: "Scan QR Code"
472+
device_id: "Device ID"
473+
add_device_failed: "Failed to add device"
474+
device_removed_from_favorites: "Device removed from favorites"
475+
remove_from_favorites_failed: "Failed to remove from favorites"
476+
435477
# 图书馆
436478
library:
437479
title: "Library Information"

assets/flutter_i18n/zh_CN.yaml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ homepage:
408408
experiment: "实验信息"
409409
score: "成绩查询"
410410
sport: "体育信息"
411+
dorm_water: "宿舍水机"
411412
schoolnet: "网络查询"
412413
toolbox: "其他功能"
413414
score_cannot_reach: "脱机状态且无缓存成绩数据,无法访问"
@@ -423,6 +424,48 @@ homepage:
423424
failed: "社团信息获取失败"
424425
fetching: "社团信息清单正在加载"
425426

427+
# 宿舍水机
428+
dorm_water:
429+
title: "宿舍水机"
430+
phone: "手机号"
431+
image_code: "图形验证码"
432+
sms_code: "短信验证码"
433+
send_sms: "发送短信码"
434+
login: "登录"
435+
logout: "退出"
436+
refresh_captcha: "刷新验证码"
437+
loading_captcha: "加载中..."
438+
captcha_error: "验证码加载失败"
439+
phone_required: "请输入手机号"
440+
image_code_required: "请输入图形验证码"
441+
sms_sent: "短信已发送"
442+
sms_failed: "发送短信失败"
443+
sms_code_required: "请输入短信验证码"
444+
login_success: "登录成功"
445+
login_failed: "登录失败"
446+
logout_success: "退出成功"
447+
devices: "设备列表"
448+
loading_devices: "加载设备中..."
449+
no_devices: "暂无设备"
450+
select_device: "选择设备"
451+
fetch_devices_failed: "获取设备列表失败"
452+
retry_load_devices: "重试加载"
453+
start_water: "开始接水"
454+
end_water: "结束接水"
455+
water_dispensing: "接水中"
456+
water_status: "接水状态"
457+
start_water_success: "开始接水成功"
458+
end_water_success: "结束接水成功"
459+
start_water_failed: "开始接水失败"
460+
end_water_failed: "结束接水失败"
461+
device_status_checking: "检查设备状态中..."
462+
device_status_ready: "设备已就绪"
463+
scan_qr_code: "扫描二维码"
464+
device_id: "设备 ID"
465+
add_device_failed: "添加设备失败"
466+
device_removed_from_favorites: "已从收藏中移除"
467+
remove_from_favorites_failed: "移除收藏失败"
468+
426469
# 图书馆
427470
library:
428471
title: "图书馆信息"

assets/flutter_i18n/zh_TW.yaml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ homepage:
380380
experiment: 實驗信息
381381
score: 成績查詢
382382
sport: 體育信息
383+
dorm_water: 宿舍水機
383384
schoolnet: 網絡查詢
384385
toolbox: 其他功能
385386
score_cannot_reach: 脫機狀態且無緩存成績數據,無法訪問
@@ -394,6 +395,48 @@ homepage:
394395
club_promotion:
395396
failed: 社團信息獲取失敗
396397
fetching: 社團信息清單正在加載
398+
399+
dorm_water:
400+
title: 宿舍水機
401+
phone: 手機號
402+
image_code: 圖形驗證碼
403+
sms_code: 短信驗證碼
404+
send_sms: 發送短信碼
405+
login: 登錄
406+
logout: 登出
407+
refresh_captcha: 刷新驗證碼
408+
loading_captcha: 加載中...
409+
captcha_error: 驗證碼加載失敗
410+
phone_required: 請輸入手機號
411+
image_code_required: 請輸入圖形驗證碼
412+
sms_sent: 短信已發送
413+
sms_failed: 發送短信失敗
414+
sms_code_required: 請輸入短信驗證碼
415+
login_success: 登錄成功
416+
login_failed: 登錄失敗
417+
logout_success: 登出成功
418+
devices: 設備列表
419+
loading_devices: 加載設備中...
420+
no_devices: 暫無設備
421+
select_device: 選擇設備
422+
fetch_devices_failed: 獲取設備列表失敗
423+
retry_load_devices: 重試加載
424+
start_water: 開始接水
425+
end_water: 結束接水
426+
water_dispensing: 接水中
427+
water_status: 接水狀態
428+
start_water_success: 開始接水成功
429+
end_water_success: 結束接水成功
430+
start_water_failed: 開始接水失敗
431+
end_water_failed: 結束接水失敗
432+
device_status_checking: 検查設備狀態中...
433+
device_status_ready: 設備已就緒
434+
scan_qr_code: 掃描二維碼
435+
device_id: 設備 ID
436+
add_device_failed: 添加設備失敗
437+
device_removed_from_favorites: 已從收藏中移除
438+
remove_from_favorites_failed: 移除收藏失敗
439+
397440
library:
398441
title: 圖書館信息
399442
borrow_state_title: 借書狀態

ios/Podfile.lock

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,75 @@
11
PODS:
2+
- camera_avfoundation (0.0.1):
3+
- Flutter
24
- charset_converter (0.0.1):
35
- Flutter
46
- device_calendar (0.0.1):
57
- Flutter
8+
- device_info_plus (0.0.1):
9+
- Flutter
10+
- DKImagePickerController/Core (4.3.9):
11+
- DKImagePickerController/ImageDataManager
12+
- DKImagePickerController/Resource
13+
- DKImagePickerController/ImageDataManager (4.3.9)
14+
- DKImagePickerController/PhotoGallery (4.3.9):
15+
- DKImagePickerController/Core
16+
- DKPhotoGallery
17+
- DKImagePickerController/Resource (4.3.9)
18+
- DKPhotoGallery (0.0.19):
19+
- DKPhotoGallery/Core (= 0.0.19)
20+
- DKPhotoGallery/Model (= 0.0.19)
21+
- DKPhotoGallery/Preview (= 0.0.19)
22+
- DKPhotoGallery/Resource (= 0.0.19)
23+
- SDWebImage
24+
- SwiftyGif
25+
- DKPhotoGallery/Core (0.0.19):
26+
- DKPhotoGallery/Model
27+
- DKPhotoGallery/Preview
28+
- SDWebImage
29+
- SwiftyGif
30+
- DKPhotoGallery/Model (0.0.19):
31+
- SDWebImage
32+
- SwiftyGif
33+
- DKPhotoGallery/Preview (0.0.19):
34+
- DKPhotoGallery/Model
35+
- DKPhotoGallery/Resource
36+
- SDWebImage
37+
- SwiftyGif
38+
- DKPhotoGallery/Resource (0.0.19):
39+
- SDWebImage
40+
- SwiftyGif
41+
- file_picker (0.0.1):
42+
- DKImagePickerController/PhotoGallery
43+
- Flutter
644
- Flutter (1.0.0)
745
- flutter_local_notifications (0.0.1):
846
- Flutter
947
- flutter_mailer (0.0.1):
1048
- Flutter
49+
- flutter_native_splash (2.4.3):
50+
- Flutter
1151
- fluttertoast (0.0.2):
1252
- Flutter
53+
- home_widget (0.0.1):
54+
- Flutter
55+
- package_info_plus (0.4.5):
56+
- Flutter
1357
- permission_handler_apple (9.3.0):
1458
- Flutter
59+
- restart_app (1.7.3):
60+
- Flutter
61+
- SDWebImage (5.21.7):
62+
- SDWebImage/Core (= 5.21.7)
63+
- SDWebImage/Core (5.21.7)
64+
- share_plus (0.0.1):
65+
- Flutter
66+
- shared_preferences_foundation (0.0.1):
67+
- Flutter
68+
- FlutterMacOS
69+
- sqflite_darwin (0.0.4):
70+
- Flutter
71+
- FlutterMacOS
72+
- SwiftyGif (5.4.5)
1573
- TensorFlowLiteC (2.12.0):
1674
- TensorFlowLiteC/Core (= 2.12.0)
1775
- TensorFlowLiteC/Core (2.12.0)
@@ -34,51 +92,105 @@ PODS:
3492
- TensorFlowLiteSwift (= 2.12.0)
3593
- TensorFlowLiteSwift/CoreML (= 2.12.0)
3694
- TensorFlowLiteSwift/Metal (= 2.12.0)
95+
- url_launcher_ios (0.0.1):
96+
- Flutter
3797

3898
DEPENDENCIES:
99+
- camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`)
39100
- charset_converter (from `.symlinks/plugins/charset_converter/ios`)
40101
- device_calendar (from `.symlinks/plugins/device_calendar/ios`)
102+
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
103+
- file_picker (from `.symlinks/plugins/file_picker/ios`)
41104
- Flutter (from `Flutter`)
42105
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
43106
- flutter_mailer (from `.symlinks/plugins/flutter_mailer/ios`)
107+
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
44108
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
109+
- home_widget (from `.symlinks/plugins/home_widget/ios`)
110+
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
45111
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
112+
- restart_app (from `.symlinks/plugins/restart_app/ios`)
113+
- share_plus (from `.symlinks/plugins/share_plus/ios`)
114+
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
115+
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
46116
- tflite_flutter (from `.symlinks/plugins/tflite_flutter/ios`)
117+
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
47118

48119
SPEC REPOS:
49120
trunk:
121+
- DKImagePickerController
122+
- DKPhotoGallery
123+
- SDWebImage
124+
- SwiftyGif
50125
- TensorFlowLiteC
51126
- TensorFlowLiteSwift
52127

53128
EXTERNAL SOURCES:
129+
camera_avfoundation:
130+
:path: ".symlinks/plugins/camera_avfoundation/ios"
54131
charset_converter:
55132
:path: ".symlinks/plugins/charset_converter/ios"
56133
device_calendar:
57134
:path: ".symlinks/plugins/device_calendar/ios"
135+
device_info_plus:
136+
:path: ".symlinks/plugins/device_info_plus/ios"
137+
file_picker:
138+
:path: ".symlinks/plugins/file_picker/ios"
58139
Flutter:
59140
:path: Flutter
60141
flutter_local_notifications:
61142
:path: ".symlinks/plugins/flutter_local_notifications/ios"
62143
flutter_mailer:
63144
:path: ".symlinks/plugins/flutter_mailer/ios"
145+
flutter_native_splash:
146+
:path: ".symlinks/plugins/flutter_native_splash/ios"
64147
fluttertoast:
65148
:path: ".symlinks/plugins/fluttertoast/ios"
149+
home_widget:
150+
:path: ".symlinks/plugins/home_widget/ios"
151+
package_info_plus:
152+
:path: ".symlinks/plugins/package_info_plus/ios"
66153
permission_handler_apple:
67154
:path: ".symlinks/plugins/permission_handler_apple/ios"
155+
restart_app:
156+
:path: ".symlinks/plugins/restart_app/ios"
157+
share_plus:
158+
:path: ".symlinks/plugins/share_plus/ios"
159+
shared_preferences_foundation:
160+
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
161+
sqflite_darwin:
162+
:path: ".symlinks/plugins/sqflite_darwin/darwin"
68163
tflite_flutter:
69164
:path: ".symlinks/plugins/tflite_flutter/ios"
165+
url_launcher_ios:
166+
:path: ".symlinks/plugins/url_launcher_ios/ios"
70167

71168
SPEC CHECKSUMS:
169+
camera_avfoundation: 5675ca25298b6f81fa0a325188e7df62cc217741
72170
charset_converter: 82bc1d2e3c70dcb51bf769e9772e3ae5b2571695
73171
device_calendar: b55b2c5406cfba45c95a59f9059156daee1f74ed
172+
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
173+
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
174+
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
175+
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
74176
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
75177
flutter_local_notifications: 395056b3175ba4f08480a7c5de30cd36d69827e4
76178
flutter_mailer: 3a8cd4f36c960fb04528d5471097270c19fec1c4
179+
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
77180
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
181+
home_widget: 54b4f6b36ed8d64cfee594a476225c35c3e45091
182+
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
78183
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
184+
restart_app: 0714144901e260eae68f7afc2fc4aacc1a323ad2
185+
SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf
186+
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
187+
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
188+
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
189+
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
79190
TensorFlowLiteC: 20785a69299185a379ba9852b6625f00afd7984a
80191
TensorFlowLiteSwift: 3a4928286e9e35bdd3e17970f48e53c80d25e793
81192
tflite_flutter: 64b192e11352fe36943ab6656e1d49207f1a5595
193+
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
82194

83195
PODFILE CHECKSUM: 33ee58e3b6ad46b815d23ed7bf392f2a93d23dc3
84196

0 commit comments

Comments
 (0)