@rentlydev/rently-tuya 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +281 -0
  3. package/android/build.gradle +182 -0
  4. package/android/gradle.properties +7 -0
  5. package/android/libs/airbrake-android-1.4.0.jar +0 -0
  6. package/android/src/main/AndroidManifest.xml +1 -0
  7. package/android/src/main/AndroidManifestNew.xml +25 -0
  8. package/android/src/main/java/com/rentlytuya/RentlyTuyaModule.kt +503 -0
  9. package/android/src/main/java/com/rentlytuya/RentlyTuyaPackage.kt +18 -0
  10. package/android/src/main/java/com/rentlytuya/TuyaActivity.kt +45 -0
  11. package/android/src/main/java/com/rentlytuya/alerts/FormatSDCardAlert.kt +83 -0
  12. package/android/src/main/java/com/rentlytuya/alerts/PermissionAlert.kt +91 -0
  13. package/android/src/main/java/com/rentlytuya/alerts/RemoveDoorbellAlert.kt +83 -0
  14. package/android/src/main/java/com/rentlytuya/alerts/RemoveDoorbellFailureAlert.kt +71 -0
  15. package/android/src/main/java/com/rentlytuya/alerts/ResetWifiAlert.kt +83 -0
  16. package/android/src/main/java/com/rentlytuya/alerts/SuccessAlert.kt +74 -0
  17. package/android/src/main/java/com/rentlytuya/components/Alert.kt +47 -0
  18. package/android/src/main/java/com/rentlytuya/components/AnchorButton.kt +125 -0
  19. package/android/src/main/java/com/rentlytuya/components/Button.kt +147 -0
  20. package/android/src/main/java/com/rentlytuya/components/CustomDatePicker.kt +275 -0
  21. package/android/src/main/java/com/rentlytuya/components/Divider.kt +53 -0
  22. package/android/src/main/java/com/rentlytuya/components/EncryptedImageView.kt +59 -0
  23. package/android/src/main/java/com/rentlytuya/components/Label.kt +155 -0
  24. package/android/src/main/java/com/rentlytuya/components/LoadingIndicator.kt +75 -0
  25. package/android/src/main/java/com/rentlytuya/components/Slider.kt +66 -0
  26. package/android/src/main/java/com/rentlytuya/components/Switch.kt +51 -0
  27. package/android/src/main/java/com/rentlytuya/components/VideoView.kt +45 -0
  28. package/android/src/main/java/com/rentlytuya/dashboard/AdvancedSettings.kt +259 -0
  29. package/android/src/main/java/com/rentlytuya/dashboard/Dashboard.kt +551 -0
  30. package/android/src/main/java/com/rentlytuya/dashboard/DoorbellSettings.kt +874 -0
  31. package/android/src/main/java/com/rentlytuya/dashboard/Messages.kt +463 -0
  32. package/android/src/main/java/com/rentlytuya/dashboard/NavController.kt +358 -0
  33. package/android/src/main/java/com/rentlytuya/dashboard/OfflineScreen.kt +160 -0
  34. package/android/src/main/java/com/rentlytuya/dashboard/Playback.kt +685 -0
  35. package/android/src/main/java/com/rentlytuya/tuya/Messages.kt +234 -0
  36. package/android/src/main/java/com/rentlytuya/tuya/Playback.kt +385 -0
  37. package/android/src/main/java/com/rentlytuya/tuya/ScreenCapture.kt +244 -0
  38. package/android/src/main/java/com/rentlytuya/tuya/Settings.kt +809 -0
  39. package/android/src/main/java/com/rentlytuya/tuya/Tuya.kt +497 -0
  40. package/android/src/main/java/com/rentlytuya/ui/theme/LocalColor.kt +80 -0
  41. package/android/src/main/java/com/rentlytuya/ui/theme/LocalFont.kt +35 -0
  42. package/android/src/main/java/com/rentlytuya/ui/theme/Theme.kt +34 -0
  43. package/android/src/main/java/com/rentlytuya/ui/theme/Type.kt +34 -0
  44. package/android/src/main/java/com/rentlytuya/util/Airbrake.kt +40 -0
  45. package/android/src/main/java/com/rentlytuya/util/DpConstants.kt +53 -0
  46. package/android/src/main/java/com/rentlytuya/util/FrescoManager.kt +86 -0
  47. package/android/src/main/java/com/rentlytuya/util/HeapAnalytics.kt +16 -0
  48. package/android/src/main/java/com/rentlytuya/util/KeylessModule.kt +81 -0
  49. package/android/src/main/java/com/rentlytuya/util/PermissionHandler.kt +75 -0
  50. package/android/src/main/java/com/rentlytuya/util/ReactParamsCheck.kt +152 -0
  51. package/android/src/main/java/com/rentlytuya/util/Util.kt +128 -0
  52. package/android/src/main/res/drawable/ic_close.xml +12 -0
  53. package/android/src/main/res/drawable/ic_launcher_background.xml +170 -0
  54. package/android/src/main/res/drawable/ic_launcher_foreground.xml +30 -0
  55. package/android/src/main/res/drawable/img_place_holder.png +0 -0
  56. package/android/src/main/res/drawable-hdpi/ic_action_arrow_backward.png +0 -0
  57. package/android/src/main/res/drawable-hdpi/ic_arrow_backward_white.png +0 -0
  58. package/android/src/main/res/drawable-hdpi/ic_arrow_blue_right.png +0 -0
  59. package/android/src/main/res/drawable-hdpi/ic_calendar.png +0 -0
  60. package/android/src/main/res/drawable-hdpi/ic_camera.png +0 -0
  61. package/android/src/main/res/drawable-hdpi/ic_message.png +0 -0
  62. package/android/src/main/res/drawable-hdpi/ic_pause.png +0 -0
  63. package/android/src/main/res/drawable-hdpi/ic_play.png +0 -0
  64. package/android/src/main/res/drawable-hdpi/ic_play_back.png +0 -0
  65. package/android/src/main/res/drawable-hdpi/ic_record.png +0 -0
  66. package/android/src/main/res/drawable-hdpi/ic_retry.png +0 -0
  67. package/android/src/main/res/drawable-hdpi/ic_setting.png +0 -0
  68. package/android/src/main/res/drawable-hdpi/ic_sound_off.png +0 -0
  69. package/android/src/main/res/drawable-hdpi/ic_sound_on.png +0 -0
  70. package/android/src/main/res/drawable-mdpi/ic_action_arrow_backward.png +0 -0
  71. package/android/src/main/res/drawable-mdpi/ic_arrow_backward_white.png +0 -0
  72. package/android/src/main/res/drawable-mdpi/ic_arrow_blue_right.png +0 -0
  73. package/android/src/main/res/drawable-mdpi/ic_battery_average.png +0 -0
  74. package/android/src/main/res/drawable-mdpi/ic_battery_charging.png +0 -0
  75. package/android/src/main/res/drawable-mdpi/ic_battery_full.png +0 -0
  76. package/android/src/main/res/drawable-mdpi/ic_battery_good.png +0 -0
  77. package/android/src/main/res/drawable-mdpi/ic_battery_low.png +0 -0
  78. package/android/src/main/res/drawable-mdpi/ic_calendar.png +0 -0
  79. package/android/src/main/res/drawable-mdpi/ic_camera.png +0 -0
  80. package/android/src/main/res/drawable-mdpi/ic_message.png +0 -0
  81. package/android/src/main/res/drawable-mdpi/ic_pause.png +0 -0
  82. package/android/src/main/res/drawable-mdpi/ic_play.png +0 -0
  83. package/android/src/main/res/drawable-mdpi/ic_play_back.png +0 -0
  84. package/android/src/main/res/drawable-mdpi/ic_record.png +0 -0
  85. package/android/src/main/res/drawable-mdpi/ic_retry.png +0 -0
  86. package/android/src/main/res/drawable-mdpi/ic_setting.png +0 -0
  87. package/android/src/main/res/drawable-mdpi/ic_sound_off.png +0 -0
  88. package/android/src/main/res/drawable-mdpi/ic_sound_on.png +0 -0
  89. package/android/src/main/res/drawable-xhdpi/ic_action_arrow_backward.png +0 -0
  90. package/android/src/main/res/drawable-xhdpi/ic_arrow_backward_white.png +0 -0
  91. package/android/src/main/res/drawable-xhdpi/ic_arrow_blue_right.png +0 -0
  92. package/android/src/main/res/drawable-xhdpi/ic_battery_average.png +0 -0
  93. package/android/src/main/res/drawable-xhdpi/ic_battery_charging.png +0 -0
  94. package/android/src/main/res/drawable-xhdpi/ic_battery_full.png +0 -0
  95. package/android/src/main/res/drawable-xhdpi/ic_battery_good.png +0 -0
  96. package/android/src/main/res/drawable-xhdpi/ic_battery_low.png +0 -0
  97. package/android/src/main/res/drawable-xhdpi/ic_calendar.png +0 -0
  98. package/android/src/main/res/drawable-xhdpi/ic_camera.png +0 -0
  99. package/android/src/main/res/drawable-xhdpi/ic_message.png +0 -0
  100. package/android/src/main/res/drawable-xhdpi/ic_pause.png +0 -0
  101. package/android/src/main/res/drawable-xhdpi/ic_play.png +0 -0
  102. package/android/src/main/res/drawable-xhdpi/ic_play_back.png +0 -0
  103. package/android/src/main/res/drawable-xhdpi/ic_record.png +0 -0
  104. package/android/src/main/res/drawable-xhdpi/ic_retry.png +0 -0
  105. package/android/src/main/res/drawable-xhdpi/ic_setting.png +0 -0
  106. package/android/src/main/res/drawable-xhdpi/ic_sound_off.png +0 -0
  107. package/android/src/main/res/drawable-xhdpi/ic_sound_on.png +0 -0
  108. package/android/src/main/res/drawable-xxhdpi/ic_action_arrow_backward.png +0 -0
  109. package/android/src/main/res/drawable-xxhdpi/ic_arrow_backward_white.png +0 -0
  110. package/android/src/main/res/drawable-xxhdpi/ic_arrow_blue_right.png +0 -0
  111. package/android/src/main/res/drawable-xxhdpi/ic_battery_average.png +0 -0
  112. package/android/src/main/res/drawable-xxhdpi/ic_battery_charging.png +0 -0
  113. package/android/src/main/res/drawable-xxhdpi/ic_battery_full.png +0 -0
  114. package/android/src/main/res/drawable-xxhdpi/ic_battery_good.png +0 -0
  115. package/android/src/main/res/drawable-xxhdpi/ic_battery_low.png +0 -0
  116. package/android/src/main/res/drawable-xxhdpi/ic_calendar.png +0 -0
  117. package/android/src/main/res/drawable-xxhdpi/ic_camera.png +0 -0
  118. package/android/src/main/res/drawable-xxhdpi/ic_message.png +0 -0
  119. package/android/src/main/res/drawable-xxhdpi/ic_pause.png +0 -0
  120. package/android/src/main/res/drawable-xxhdpi/ic_play.png +0 -0
  121. package/android/src/main/res/drawable-xxhdpi/ic_play_back.png +0 -0
  122. package/android/src/main/res/drawable-xxhdpi/ic_record.png +0 -0
  123. package/android/src/main/res/drawable-xxhdpi/ic_retry.png +0 -0
  124. package/android/src/main/res/drawable-xxhdpi/ic_setting.png +0 -0
  125. package/android/src/main/res/drawable-xxhdpi/ic_sound_off.png +0 -0
  126. package/android/src/main/res/drawable-xxhdpi/ic_sound_on.png +0 -0
  127. package/android/src/main/res/drawable-xxxhdpi/ic_action_arrow_backward.png +0 -0
  128. package/android/src/main/res/drawable-xxxhdpi/ic_arrow_backward_white.png +0 -0
  129. package/android/src/main/res/drawable-xxxhdpi/ic_arrow_blue_right.png +0 -0
  130. package/android/src/main/res/drawable-xxxhdpi/ic_calendar.png +0 -0
  131. package/android/src/main/res/drawable-xxxhdpi/ic_camera.png +0 -0
  132. package/android/src/main/res/drawable-xxxhdpi/ic_message.png +0 -0
  133. package/android/src/main/res/drawable-xxxhdpi/ic_pause.png +0 -0
  134. package/android/src/main/res/drawable-xxxhdpi/ic_play.png +0 -0
  135. package/android/src/main/res/drawable-xxxhdpi/ic_play_back.png +0 -0
  136. package/android/src/main/res/drawable-xxxhdpi/ic_record.png +0 -0
  137. package/android/src/main/res/drawable-xxxhdpi/ic_retry.png +0 -0
  138. package/android/src/main/res/drawable-xxxhdpi/ic_setting.png +0 -0
  139. package/android/src/main/res/drawable-xxxhdpi/ic_sound_off.png +0 -0
  140. package/android/src/main/res/drawable-xxxhdpi/ic_sound_on.png +0 -0
  141. package/android/src/main/res/font/quicksand_bold.ttf +0 -0
  142. package/android/src/main/res/font/quicksand_light.ttf +0 -0
  143. package/android/src/main/res/font/quicksand_medium.ttf +0 -0
  144. package/android/src/main/res/font/quicksand_regular.ttf +0 -0
  145. package/android/src/main/res/font/quicksand_semi_bold.ttf +0 -0
  146. package/android/src/main/res/values/strings.xml +121 -0
  147. package/android/thingMapping.gradle +200 -0
  148. package/android/thingMapping.json +2014 -0
  149. package/ios/components/Alert.swift +54 -0
  150. package/ios/components/AnchorButton.swift +100 -0
  151. package/ios/components/Button.swift +118 -0
  152. package/ios/components/CustomDatePicker.swift +275 -0
  153. package/ios/components/Divider.swift +28 -0
  154. package/ios/components/Image.swift +72 -0
  155. package/ios/components/Label.swift +105 -0
  156. package/ios/components/Switch.swift +61 -0
  157. package/ios/dashboard/AdvanceSettings.swift +127 -0
  158. package/ios/dashboard/Dashboard.swift +354 -0
  159. package/ios/dashboard/Messages.swift +361 -0
  160. package/ios/dashboard/NavigationController.swift +173 -0
  161. package/ios/dashboard/OfflineScreen.swift +109 -0
  162. package/ios/dashboard/Playback.swift +543 -0
  163. package/ios/dashboard/PreviewManager.swift +117 -0
  164. package/ios/dashboard/Settings.swift +672 -0
  165. package/ios/icons/Charging.png +0 -0
  166. package/ios/icons/Charging@2x.png +0 -0
  167. package/ios/icons/Charging@3x.png +0 -0
  168. package/ios/icons/back_arraow.png +0 -0
  169. package/ios/icons/back_arraow@2x.png +0 -0
  170. package/ios/icons/back_arraow@3x.png +0 -0
  171. package/ios/icons/battery_average.png +0 -0
  172. package/ios/icons/battery_average@2x.png +0 -0
  173. package/ios/icons/battery_average@3x.png +0 -0
  174. package/ios/icons/battery_full.png +0 -0
  175. package/ios/icons/battery_full@2x.png +0 -0
  176. package/ios/icons/battery_full@3x.png +0 -0
  177. package/ios/icons/battery_good.png +0 -0
  178. package/ios/icons/battery_good@2x.png +0 -0
  179. package/ios/icons/battery_good@3x.png +0 -0
  180. package/ios/icons/battery_low.png +0 -0
  181. package/ios/icons/battery_low@2x.png +0 -0
  182. package/ios/icons/battery_low@3x.png +0 -0
  183. package/ios/icons/calendar.png +0 -0
  184. package/ios/icons/calendar@2x.png +0 -0
  185. package/ios/icons/calendar@3x.png +0 -0
  186. package/ios/icons/cloud.png +0 -0
  187. package/ios/icons/cloud@2x.png +0 -0
  188. package/ios/icons/cloud@3x.png +0 -0
  189. package/ios/icons/down_update.png +0 -0
  190. package/ios/icons/down_update@2x.png +0 -0
  191. package/ios/icons/down_update@3x.png +0 -0
  192. package/ios/icons/exit_fullscreen.png +0 -0
  193. package/ios/icons/exit_fullscreen@2x.png +0 -0
  194. package/ios/icons/exit_fullscreen@3x.png +0 -0
  195. package/ios/icons/expand.png +0 -0
  196. package/ios/icons/fullscreen.png +0 -0
  197. package/ios/icons/fullscreen@2x.png +0 -0
  198. package/ios/icons/fullscreen@3x.png +0 -0
  199. package/ios/icons/image_placeholder.png +0 -0
  200. package/ios/icons/information.png +0 -0
  201. package/ios/icons/information@2x.png +0 -0
  202. package/ios/icons/information@3x.png +0 -0
  203. package/ios/icons/keyless_Setting.png +0 -0
  204. package/ios/icons/keyless_Setting@2x.png +0 -0
  205. package/ios/icons/keyless_Setting@3x.png +0 -0
  206. package/ios/icons/keyless_back@1x.png +0 -0
  207. package/ios/icons/keyless_back@2x.png +0 -0
  208. package/ios/icons/keyless_back@3x.png +0 -0
  209. package/ios/icons/keyless_filter.png +0 -0
  210. package/ios/icons/keyless_filter@2x.png +0 -0
  211. package/ios/icons/keyless_filter@3x.png +0 -0
  212. package/ios/icons/keyless_message.png +0 -0
  213. package/ios/icons/keyless_message@2x.png +0 -0
  214. package/ios/icons/keyless_message@3x.png +0 -0
  215. package/ios/icons/keyless_pause.png +0 -0
  216. package/ios/icons/keyless_pause@2x.png +0 -0
  217. package/ios/icons/keyless_pause@3x.png +0 -0
  218. package/ios/icons/keyless_photo.png +0 -0
  219. package/ios/icons/keyless_photo@2x.png +0 -0
  220. package/ios/icons/keyless_photo@3x.png +0 -0
  221. package/ios/icons/keyless_play.png +0 -0
  222. package/ios/icons/keyless_play@2x.png +0 -0
  223. package/ios/icons/keyless_play@3x.png +0 -0
  224. package/ios/icons/keyless_play_list.png +0 -0
  225. package/ios/icons/keyless_play_list@2x.png +0 -0
  226. package/ios/icons/keyless_play_list@3x.png +0 -0
  227. package/ios/icons/keyless_play_playback.png +0 -0
  228. package/ios/icons/keyless_play_playback@2x.png +0 -0
  229. package/ios/icons/keyless_play_playback@3x.png +0 -0
  230. package/ios/icons/keyless_record.png +0 -0
  231. package/ios/icons/keyless_record@2x.png +0 -0
  232. package/ios/icons/keyless_record@3x.png +0 -0
  233. package/ios/icons/keyless_record_on.png +0 -0
  234. package/ios/icons/keyless_record_on@2x.png +0 -0
  235. package/ios/icons/keyless_record_on@3x.png +0 -0
  236. package/ios/icons/keyless_sound_off.png +0 -0
  237. package/ios/icons/keyless_sound_off@2x.png +0 -0
  238. package/ios/icons/keyless_sound_off@3x.png +0 -0
  239. package/ios/icons/keyless_sound_on.png +0 -0
  240. package/ios/icons/keyless_sound_on@2x.png +0 -0
  241. package/ios/icons/keyless_sound_on@3x.png +0 -0
  242. package/ios/icons/keyless_speak_off.png +0 -0
  243. package/ios/icons/keyless_speak_off@2x.png +0 -0
  244. package/ios/icons/keyless_speak_on.png +0 -0
  245. package/ios/icons/keyless_speak_on@2x.png +0 -0
  246. package/ios/icons/keyless_speak_on@3x.png +0 -0
  247. package/ios/icons/refresh.png +0 -0
  248. package/ios/icons/refresh@2x.png +0 -0
  249. package/ios/icons/refresh@3x.png +0 -0
  250. package/ios/icons/ty_camera_control_hd_normal@2x.png +0 -0
  251. package/ios/icons/ty_camera_control_hd_normal@3x.png +0 -0
  252. package/ios/icons/ty_camera_control_sd_normal@2x.png +0 -0
  253. package/ios/icons/ty_camera_control_sd_normal@3x.png +0 -0
  254. package/ios/icons/upgrade_success.png +0 -0
  255. package/ios/icons/upgrade_success@2x.png +0 -0
  256. package/ios/icons/upgrade_success@3x.png +0 -0
  257. package/ios/listener/KeylessListener.m +8 -0
  258. package/ios/listener/KeylessListener.swift +56 -0
  259. package/ios/listener/RentlyTuya-Bridging-Header.h +5 -0
  260. package/ios/listener/RentlyTuya.mm +56 -0
  261. package/ios/listener/RentlyTuya.swift +544 -0
  262. package/ios/theme/LocalColor.swift +33 -0
  263. package/ios/theme/LocalFont.swift +32 -0
  264. package/ios/theme/Theme.swift +71 -0
  265. package/ios/tuya/CameraManager.swift +296 -0
  266. package/ios/tuya/SettingsManager.swift +475 -0
  267. package/ios/tuya/Tuya.swift +570 -0
  268. package/ios/tuya/TuyaMessages.swift +98 -0
  269. package/ios/utils/Airbrake.swift +31 -0
  270. package/ios/utils/AppUtility.swift +76 -0
  271. package/ios/utils/HeapAnalytics.swift +8 -0
  272. package/ios/utils/OrientationManager.swift +60 -0
  273. package/ios/utils/PromiseHandler.swift +36 -0
  274. package/lib/commonjs/index.js +90 -0
  275. package/lib/commonjs/index.js.map +1 -0
  276. package/lib/commonjs/package.json +1 -0
  277. package/lib/module/index.js +83 -0
  278. package/lib/module/index.js.map +1 -0
  279. package/lib/module/package.json +1 -0
  280. package/lib/typescript/commonjs/package.json +1 -0
  281. package/lib/typescript/commonjs/src/index.d.ts +75 -0
  282. package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
  283. package/lib/typescript/module/package.json +1 -0
  284. package/lib/typescript/module/src/index.d.ts +75 -0
  285. package/lib/typescript/module/src/index.d.ts.map +1 -0
  286. package/package.json +188 -0
  287. package/rently-tuya.podspec +52 -0
  288. package/src/index.tsx +158 -0
@@ -0,0 +1,361 @@
1
+ import SwiftUI
2
+ import ThingSmartCameraKit
3
+
4
+ struct Messages: View {
5
+
6
+ @State private var messages: [ThingSmartCameraMessageModel] = []
7
+ @State private var offset: Int = 0
8
+ @State private var noMoreMessages: Bool = false
9
+ @State private var selectedMessage: ThingSmartCameraMessageModel? = nil
10
+ @State private var showOverlay = false
11
+ @State var selectedDate: Date = DateUtil.shared.getStartOfDay(from: Date())
12
+ @Binding var isDatePickerVisible: Bool
13
+ @State var availableDates: [Int]? = nil
14
+ @ObservedObject var tuya = Tuya.shared
15
+ @EnvironmentObject var orientationManager: OrientationManager
16
+
17
+ @State private var lastMoveOutDate: Date? = {
18
+ let dateFormatter = ISO8601DateFormatter()
19
+ return Tuya.shared.lastMoveoutAt.flatMap { dateFormatter.date(from: $0) }
20
+ }()
21
+
22
+ var body: some View {
23
+ VStack {
24
+ ZStack {
25
+ if tuya.messagesLoader && messages.isEmpty {
26
+ ProgressView()
27
+ .progressViewStyle(CircularProgressViewStyle(tint: LocalColor.Monochrome.black))
28
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
29
+ } else if messages.isEmpty {
30
+ NoMessages()
31
+ } else {
32
+ ScrollView {
33
+ LazyVStack {
34
+ ForEach(messages, id: \.self) { message in
35
+ MessageItem(message: message)
36
+ .onTapGesture {
37
+ if message.attachPic != nil {
38
+ selectedMessage = message
39
+ showOverlay = true
40
+ }
41
+ }
42
+ .onAppear {
43
+ if message == messages.last && !noMoreMessages {
44
+ fetchMessages()
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+ if tuya.messagesLoader && !noMoreMessages {
51
+ ProgressView()
52
+ .progressViewStyle(CircularProgressViewStyle(tint: LocalColor.Monochrome.black))
53
+ }
54
+ }
55
+
56
+ if let selectedMessage = selectedMessage, showOverlay {
57
+ MessageOverlay(
58
+ selectedMessage: $selectedMessage,
59
+ showOverlay: $showOverlay
60
+ )
61
+ }
62
+
63
+ CustomDatePicker(
64
+ isVisible: $isDatePickerVisible,
65
+ selectedDate: $selectedDate,
66
+ availableDays: availableDates ?? [],
67
+ minDate: lastMoveOutDate
68
+ ) { date in
69
+ } onMonthChanged: { date in
70
+ TuyaMessages.shared.getAvailableMessageDates(
71
+ year: Calendar.current.component(.year, from: date),
72
+ month: Calendar.current.component(.month, from: date),
73
+ success: { dates in
74
+ availableDates = dates
75
+ },
76
+ failure: { error in
77
+ availableDates = []
78
+ }
79
+ )
80
+ } onCancel: {
81
+ isDatePickerVisible = false
82
+ } onOk: {
83
+ isDatePickerVisible = false
84
+ } onAppear: {
85
+ TuyaMessages.shared.getAvailableMessageDates(
86
+ year: Calendar.current.component(.year, from: selectedDate),
87
+ month: Calendar.current.component(.month, from: selectedDate),
88
+ success: { dates in
89
+ availableDates = dates
90
+ },
91
+ failure: { error in
92
+ availableDates = []
93
+ }
94
+ )
95
+ }
96
+ }
97
+ }
98
+ .background(LocalColor.Main.light)
99
+ .onAppear {
100
+ fetchMessages()
101
+ let currentMonth = Calendar.current.component(.month, from: selectedDate)
102
+ let currentYear = Calendar.current.component(.year, from: selectedDate)
103
+
104
+ TuyaMessages.shared.getAvailableMessageDates(
105
+ year: currentYear,
106
+ month: currentMonth,
107
+ success: { dates in
108
+ availableDates = dates
109
+ },
110
+ failure: { error in
111
+ availableDates = []
112
+ }
113
+ )
114
+ }
115
+ .onChange(of: orientationManager.isLandscape) { _ in
116
+ if orientationManager.isLandscape && isDatePickerVisible {
117
+ isDatePickerVisible = false
118
+ }
119
+ }
120
+ .onChange(of: selectedDate) { newDate in
121
+ offset = 0
122
+ noMoreMessages = false
123
+ messages = []
124
+ fetchMessages()
125
+ }
126
+ }
127
+
128
+ private func fetchMessages() {
129
+ guard !noMoreMessages else { return }
130
+
131
+ TuyaMessages.shared.getMessages(
132
+ startDate: selectedDate,
133
+ offset: offset
134
+ ) { resultArray in
135
+ DispatchQueue.main.async {
136
+ if let resultArray = resultArray, !resultArray.isEmpty {
137
+ messages.append(contentsOf: resultArray)
138
+ offset += resultArray.count
139
+
140
+ if resultArray.count < 20 {
141
+ noMoreMessages = true
142
+ }
143
+ } else {
144
+ noMoreMessages = true
145
+ }
146
+
147
+ tuya.messagesLoader = false
148
+ }
149
+ }
150
+ }
151
+ }
152
+
153
+ struct NoMessages: View {
154
+ var body: some View {
155
+ VStack {
156
+ Label(
157
+ id: "noMessages",
158
+ title: "No messages available at the moment.",
159
+ bold: true,
160
+ maxLines: 2
161
+ )
162
+ }
163
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
164
+ .padding()
165
+ }
166
+ }
167
+
168
+ struct MessageItem: View {
169
+ let message: ThingSmartCameraMessageModel?
170
+
171
+ var body: some View {
172
+ if let message = message {
173
+ VStack(alignment: .leading, spacing: 8) {
174
+ if let dateTime = message.dateTime {
175
+ Label(
176
+ title: dateTime,
177
+ m: true,
178
+ black: true
179
+ )
180
+ .padding(.bottom, 3)
181
+ }
182
+
183
+ Label(
184
+ title: (message.attachPic != nil) ? message.msgContent : "Unable to capture detected events because the doorbell's battery is too low. Please charge it to ensure events are detected and recorded.",
185
+ l: true
186
+ )
187
+ .padding(.bottom, 4)
188
+
189
+ if let imageUrl = message.attachPic, let url = URL(string: imageUrl) {
190
+ WebImage(
191
+ url: url,
192
+ placeholderImageName: "image_placeholder"
193
+ )
194
+ }
195
+ }
196
+ .padding()
197
+
198
+ Divider(thickness: 1)
199
+ }
200
+ }
201
+ }
202
+
203
+ struct MessageOverlay: View {
204
+ @Binding var selectedMessage: ThingSmartCameraMessageModel?
205
+ @Binding var showOverlay: Bool
206
+
207
+ @State private var currentScale: CGFloat = 1.0
208
+ @State private var lastScale: CGFloat = 1.0
209
+ @State private var loadedImage: UIImage? = nil
210
+ @State private var isLoading: Bool = true
211
+ @State private var dragOffset: CGSize = .zero
212
+ @State private var lastDragOffset: CGSize = .zero
213
+ @State private var containerSize: CGSize = CGSize(width: 350, height: 350)
214
+ @EnvironmentObject var orientationManager: OrientationManager
215
+
216
+ private let minScale: CGFloat = 1.0
217
+ private let maxScale: CGFloat = 4.0
218
+
219
+ var body: some View {
220
+ ZStack {
221
+ LocalColor.Monochrome.black.opacity(0.9)
222
+ .ignoresSafeArea()
223
+
224
+ VStack {
225
+ if isLoading {
226
+ ProgressView()
227
+ } else if let cachedImage = loadedImage {
228
+ GeometryReader { geometry in
229
+ Image(uiImage: cachedImage)
230
+ .resizable()
231
+ .aspectRatio(contentMode: .fit)
232
+ .scaleEffect(currentScale)
233
+ .offset(x: dragOffset.width, y: dragOffset.height)
234
+ .gesture(imageGestures())
235
+ .frame(width: containerSize.width, height: containerSize.height)
236
+ .position(x: geometry.size.width / 2, y: geometry.size.height / 2)
237
+ }
238
+ .frame(width: containerSize.width, height: containerSize.height)
239
+ .transition(.opacity)
240
+ .onAppear {
241
+ updateContainerSize()
242
+ }
243
+ } else {
244
+ Label(title: "Failed to load image.", white: true)
245
+ }
246
+ }
247
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
248
+ .overlay(closeButton(), alignment: .topTrailing)
249
+ }
250
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
251
+ .onAppear {
252
+ fetchImage(from: selectedMessage?.attachPic ?? "")
253
+ }
254
+ .onChange(of: selectedMessage) { _ in
255
+ loadedImage = nil
256
+ isLoading = true
257
+ fetchImage(from: selectedMessage?.attachPic ?? "")
258
+ }
259
+ .onChange(of: orientationManager.isLandscape) { _ in
260
+ updateContainerSize()
261
+ resetImage()
262
+ }
263
+ }
264
+
265
+ private func fetchImage(from url: String) {
266
+ guard let imageURL = URL(string: url) else {
267
+ isLoading = false
268
+ return
269
+ }
270
+
271
+ URLSession.shared.dataTask(with: imageURL) { data, _, _ in
272
+ DispatchQueue.main.async {
273
+ if let data = data, let image = UIImage(data: data) {
274
+ self.loadedImage = image
275
+ }
276
+ self.isLoading = false
277
+ }
278
+ }.resume()
279
+ }
280
+
281
+ private func imageGestures() -> some Gesture {
282
+ SimultaneousGesture(
283
+ MagnificationGesture()
284
+ .onChanged { value in
285
+ let newScale = lastScale * value
286
+ currentScale = min(max(newScale, minScale), maxScale)
287
+ }
288
+ .onEnded { _ in
289
+ lastScale = currentScale
290
+ if currentScale <= minScale {
291
+ resetImage()
292
+ } else {
293
+ recenterImage()
294
+ }
295
+ },
296
+ DragGesture()
297
+ .onChanged { value in
298
+ guard currentScale > 1 else { return }
299
+ let limitedOffset = limitDragOffset(value.translation)
300
+ dragOffset = CGSize(width: limitedOffset.width, height: limitedOffset.height)
301
+ }
302
+ .onEnded { _ in
303
+ lastDragOffset = dragOffset
304
+ recenterImage()
305
+ }
306
+ )
307
+ }
308
+
309
+ private func closeButton() -> some View {
310
+ Button(action: {
311
+ showOverlay = false
312
+ selectedMessage = nil
313
+ loadedImage = nil
314
+ HeapAnalytics.trackEvent(eventName: "Message_Overlay_Close")
315
+ }) {
316
+ Image(systemName: "xmark.circle.fill")
317
+ .resizable()
318
+ .frame(width: 24, height: 24)
319
+ .foregroundColor(.white)
320
+ .padding()
321
+ }
322
+ }
323
+
324
+ private func resetImage() {
325
+ withAnimation {
326
+ currentScale = minScale
327
+ dragOffset = .zero
328
+ lastDragOffset = .zero
329
+ }
330
+ }
331
+
332
+ private func recenterImage() {
333
+ let imageSize = CGSize(width: containerSize.width * currentScale, height: containerSize.height * currentScale)
334
+ let dragLimitX = max((imageSize.width - containerSize.width) / 2, 0)
335
+ let dragLimitY = max((imageSize.height - containerSize.height) / 2, 0)
336
+
337
+ let newOffsetX = max(min(dragOffset.width, dragLimitX), -dragLimitX)
338
+ let newOffsetY = max(min(dragOffset.height, dragLimitY), -dragLimitY)
339
+
340
+ withAnimation {
341
+ dragOffset = CGSize(width: newOffsetX, height: newOffsetY)
342
+ lastDragOffset = dragOffset
343
+ }
344
+ }
345
+
346
+ private func limitDragOffset(_ translation: CGSize) -> CGSize {
347
+ let imageSize = CGSize(width: containerSize.width * currentScale, height: containerSize.height * currentScale)
348
+ let dragLimitX = max((imageSize.width - containerSize.width) / 2, 0)
349
+ let dragLimitY = max((imageSize.height - containerSize.height) / 2, 0)
350
+
351
+ let newOffsetX = lastDragOffset.width + translation.width
352
+ let newOffsetY = lastDragOffset.height + translation.height
353
+
354
+ return CGSize(width: max(min(newOffsetX, dragLimitX), -dragLimitX),
355
+ height: max(min(newOffsetY, dragLimitY), -dragLimitY))
356
+ }
357
+
358
+ private func updateContainerSize() {
359
+ containerSize = UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height ? CGSize(width: 500, height: 350) : CGSize(width: 350, height: 500)
360
+ }
361
+ }
@@ -0,0 +1,173 @@
1
+ import SwiftUI
2
+
3
+ enum ScreenState: String {
4
+ case dashboard = "Dashboard"
5
+ case playback = "Playback"
6
+ case messages = "Messages"
7
+ case settings = "Settings"
8
+ case advancedSettings = "Advanced Settings"
9
+ case storageSettings = "SD Card"
10
+ }
11
+
12
+ struct NavigationController: View {
13
+
14
+ @State private var isSettings = false
15
+ @State private var isStorageSettings = false
16
+ @State private var isAdvancedSettings = false
17
+ @State private var isDatePickerVisible = false
18
+ @State private var isMessagesDatePickerVisible = false
19
+ @State private var currentScreen: ScreenState = .dashboard
20
+ @EnvironmentObject var orientationManager: OrientationManager
21
+ @ObservedObject var tuya = Tuya.shared
22
+
23
+ private var topBarTitle: String {
24
+ if isAdvancedSettings { return "Advanced Settings" }
25
+ if isStorageSettings { return "SD Card" }
26
+ return currentScreen == .dashboard ? (tuya.device?.deviceModel.name ?? "Doorbell") : currentScreen.rawValue
27
+ }
28
+
29
+ private var isSpecialTitle: Bool {
30
+ [.settings, .messages, .advancedSettings, .storageSettings].contains(currentScreen)
31
+ }
32
+
33
+ var body: some View {
34
+ ZStack {
35
+ VStack {
36
+ (isSpecialTitle ? LocalColor.Main.light : LocalColor.Monochrome.grey)
37
+ .edgesIgnoringSafeArea(/*@START_MENU_TOKEN@*/.all/*@END_MENU_TOKEN@*/)
38
+ }
39
+
40
+ VStack {
41
+ if (!orientationManager.isLandscape || (orientationManager.isLandscape && currentScreen != .dashboard && currentScreen != .playback && currentScreen != .messages)) {
42
+ TopBar(
43
+ title: topBarTitle,
44
+ isSpecialTitle: isSpecialTitle,
45
+ onBack: {
46
+ if tuya.playbackLoader {
47
+ print("Tuya: Playback is loading, hence disabling the back button to avoid preview failures.")
48
+ } else {
49
+ if ((currentScreen == .playback || currentScreen == .dashboard) && tuya.isRecording) {
50
+ tuya.stopRecording()
51
+ } else {
52
+ if isAdvancedSettings || isStorageSettings {
53
+ isAdvancedSettings = false
54
+ isStorageSettings = false
55
+ currentScreen = .settings
56
+ } else if currentScreen == .dashboard {
57
+ AppUtility.disconnectAndClose()
58
+ } else {
59
+ isSettings = false
60
+ currentScreen = .dashboard
61
+ }
62
+ }
63
+ }
64
+ },
65
+ onTrailingIconClick: {
66
+ switch currentScreen {
67
+ case .playback:
68
+ isDatePickerVisible = true
69
+ case .messages:
70
+ isMessagesDatePickerVisible = true
71
+ default:
72
+ currentScreen = .settings
73
+ }
74
+ }
75
+ )
76
+ }
77
+
78
+ ZStack {
79
+ switch currentScreen {
80
+ case .dashboard:
81
+ Dashboard(currentScreen: $currentScreen)
82
+ case .playback:
83
+ Playback(isDatePickerVisible: $isDatePickerVisible)
84
+ case .messages:
85
+ Messages(isDatePickerVisible: $isMessagesDatePickerVisible)
86
+ case .settings:
87
+ Settings(isAdvancedSettings: $isAdvancedSettings, isStorageSettings: $isStorageSettings)
88
+ default:
89
+ EmptyView()
90
+ }
91
+ }
92
+ .onAppear {
93
+ tuya.configureSettings()
94
+ if !tuya.isDefaultSettingsUpdated, tuya.isResident {
95
+ Task {
96
+ await DefaultSettings.shared.updateDoorbellSettings()
97
+ }
98
+ }
99
+ }
100
+ }
101
+ }
102
+ .onChange(of: currentScreen) { newValue in
103
+ HeapAnalytics.trackEvent(eventName: "\(newValue.rawValue) Screen")
104
+ }
105
+ .onReceive(NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification)) { _ in
106
+ if currentScreen == .playback {
107
+ tuya.pausePlayback()
108
+ } else {
109
+ tuya.disconnect()
110
+ }
111
+ }
112
+ .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
113
+ if currentScreen == .dashboard {
114
+ tuya.connect()
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ struct TopBar: View {
121
+ var title: String
122
+ var isSpecialTitle: Bool
123
+ var onBack: () -> Void
124
+ var onTrailingIconClick: () -> Void
125
+
126
+ var body: some View {
127
+ ZStack {
128
+ Label(
129
+ id: title + "Id",
130
+ title: title,
131
+ xl20: true,
132
+ white: !isSpecialTitle,
133
+ black: isSpecialTitle,
134
+ semiBold: true
135
+ )
136
+
137
+ HStack {
138
+ Button(action: onBack) {
139
+ Image(
140
+ imageName: "back_arraow",
141
+ useTemplateRendering: true
142
+ )
143
+ .resizable()
144
+ .aspectRatio(contentMode: .fit)
145
+ .frame(width: 22, height: 22)
146
+ .foregroundColor(isSpecialTitle ? LocalColor.Monochrome.black : LocalColor.Monochrome.white)
147
+ }
148
+ .padding(.leading, 5)
149
+
150
+ Spacer()
151
+
152
+ if !isSpecialTitle || title == "Playback" || title == "Messages" {
153
+ Button(action: onTrailingIconClick) {
154
+ Image(
155
+ imageName: title == "Playback" || title == "Messages" ? "calendar" : "keyless_Setting",
156
+ useTemplateRendering: true
157
+ )
158
+ .resizable()
159
+ .aspectRatio(contentMode: .fit)
160
+ .frame(width: 24, height: 24)
161
+ .foregroundColor(title == "Messages" ? LocalColor.Monochrome.grey : LocalColor.Monochrome.white)
162
+ }
163
+ .padding(.trailing, 5)
164
+ }
165
+ }
166
+ }
167
+ .padding()
168
+ .background(
169
+ (isSpecialTitle ? LocalColor.Main.light : LocalColor.Monochrome.grey)
170
+ .edgesIgnoringSafeArea(.all)
171
+ )
172
+ }
173
+ }
@@ -0,0 +1,109 @@
1
+ import SwiftUI
2
+
3
+ struct OfflineScreen: View {
4
+
5
+ @ObservedObject var tuya = Tuya.shared
6
+
7
+ var body: some View {
8
+ ScrollView {
9
+ VStack(spacing: 20) {
10
+ Label(
11
+ title: "Connection Failed!",
12
+ xl18: true,
13
+ white: true,
14
+ bold: true,
15
+ center: true
16
+ )
17
+ .padding(.bottom, 15)
18
+
19
+ VStack(alignment: .leading, spacing: 10) {
20
+ Label(
21
+ title: "If you're experiencing an issue, please check the following:",
22
+ l: true,
23
+ white: true,
24
+ left: true
25
+ )
26
+ .padding(.bottom, 10)
27
+
28
+ Label(
29
+ title: "• Move your Wi-Fi access point closer to the doorbell.",
30
+ l: true,
31
+ white: true,
32
+ left: true
33
+ )
34
+
35
+ Label(
36
+ title: SettingsManager.shared.is8s() ? """
37
+ • Check power source to doorbell, by pressing the button on the front of the doorbell. If the power is connected properly, you will hear a chime.
38
+ """ : """
39
+ • Check power source to doorbell, by pressing the button on the front of the doorbell. If the power is connected properly, you will hear a chime. If you do not hear a chime, recharge the doorbell.
40
+ """,
41
+ l: true,
42
+ white: true,
43
+ left: true,
44
+ maxLines: 6
45
+ )
46
+ }
47
+ .padding(.horizontal, 20)
48
+
49
+ Label(
50
+ title: "First time using the doorbell?",
51
+ l: true,
52
+ white: true,
53
+ center: true
54
+ )
55
+ .padding(.top, 15)
56
+
57
+ AnchorButton(
58
+ id: "Connect_Doorbell_To_WiFi",
59
+ title: "Connect Doorbell to Wi-Fi",
60
+ l: true,
61
+ primary: true,
62
+ center: true,
63
+ underLine: true
64
+ ) {
65
+ if let cameraType = tuya.cameraType {
66
+ KeylessListener.emitter.resetWifiForDoorbell(
67
+ withDeviceID: cameraType.devId) { success, message in
68
+ if success {
69
+ print("Tuya: Successfully navigated to the 'Add Doorbell' screen.")
70
+ AppUtility.disconnectAndClose()
71
+ } else {
72
+ print("Tuya: Failed to navigate to the 'Add Doorbell' screen.")
73
+ }
74
+ }
75
+ }
76
+ }
77
+ .frame(maxWidth: .infinity, alignment: .center)
78
+ .padding(.bottom, 5)
79
+
80
+ CustomButton(
81
+ id: "Tap_To_Retry",
82
+ title: "Tap to retry",
83
+ xl20: true,
84
+ black: true,
85
+ textColor: LocalColor.Primary.light,
86
+ borderColor: LocalColor.Primary.light,
87
+ icon: Image(imageName: "refresh")
88
+ ) {
89
+ tuya.connect()
90
+ }
91
+ .frame(width: 200, height: 80)
92
+ .padding(.bottom, 5)
93
+
94
+ Label(
95
+ title: "Contact Support for further assistance.",
96
+ l: true,
97
+ white: true,
98
+ center: true
99
+ )
100
+ .padding(.bottom, 5)
101
+ }
102
+ .padding(.top, 50)
103
+ .padding(.top, 10)
104
+ }
105
+ .padding(.horizontal, 20)
106
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
107
+ .background(LocalColor.Monochrome.black)
108
+ }
109
+ }