@iobroker/adapter-react-v5 7.2.4 → 7.2.6

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 (375) hide show
  1. package/README.md +6 -6
  2. package/build/AdminConnection.d.ts +1 -0
  3. package/build/AdminConnection.js +2 -0
  4. package/build/AdminConnection.js.map +1 -0
  5. package/{src/Components/404.tsx → build/Components/404.js} +14 -39
  6. package/build/Components/404.js.map +1 -0
  7. package/{Components → build/Components}/ColorPicker.d.ts +2 -2
  8. package/{Components → build/Components}/ColorPicker.js +50 -65
  9. package/build/Components/ColorPicker.js.map +1 -0
  10. package/{Components → build/Components}/ComplexCron.d.ts +2 -2
  11. package/{Components → build/Components}/ComplexCron.js +43 -71
  12. package/build/Components/ComplexCron.js.map +1 -0
  13. package/{Components → build/Components}/CopyToClipboard.d.ts +1 -2
  14. package/{src/Components/CopyToClipboard.tsx → build/Components/CopyToClipboard.js} +20 -38
  15. package/build/Components/CopyToClipboard.js.map +1 -0
  16. package/{Components → build/Components}/CustomModal.d.ts +3 -3
  17. package/build/Components/CustomModal.js +60 -0
  18. package/build/Components/CustomModal.js.map +1 -0
  19. package/{Components → build/Components}/FileBrowser.d.ts +2 -2
  20. package/{Components → build/Components}/FileBrowser.js +229 -236
  21. package/build/Components/FileBrowser.js.map +1 -0
  22. package/build/Components/FileViewer.d.ts +48 -0
  23. package/build/Components/FileViewer.js +277 -0
  24. package/build/Components/FileViewer.js.map +1 -0
  25. package/{Components → build/Components}/Icon.d.ts +1 -1
  26. package/build/Components/Icon.js +140 -0
  27. package/build/Components/Icon.js.map +1 -0
  28. package/{Components → build/Components}/IconPicker.d.ts +2 -2
  29. package/build/Components/IconPicker.js +73 -0
  30. package/build/Components/IconPicker.js.map +1 -0
  31. package/{Components → build/Components}/IconSelector.d.ts +3 -3
  32. package/{Components → build/Components}/IconSelector.js +29 -57
  33. package/build/Components/IconSelector.js.map +1 -0
  34. package/{Components → build/Components}/Image.d.ts +2 -2
  35. package/{Components → build/Components}/Image.js +16 -22
  36. package/build/Components/Image.js.map +1 -0
  37. package/{Components → build/Components}/Loader.d.ts +2 -2
  38. package/{Components → build/Components}/Loader.js +15 -40
  39. package/build/Components/Loader.js.map +1 -0
  40. package/{Components → build/Components}/Loaders/MV.d.ts +2 -2
  41. package/build/Components/Loaders/MV.js +61 -0
  42. package/build/Components/Loaders/MV.js.map +1 -0
  43. package/{Components → build/Components}/Loaders/PT.d.ts +2 -2
  44. package/{Components → build/Components}/Loaders/PT.js +10 -35
  45. package/build/Components/Loaders/PT.js.map +1 -0
  46. package/{Components → build/Components}/Loaders/Vendor.d.ts +2 -2
  47. package/build/Components/Loaders/Vendor.js +52 -0
  48. package/build/Components/Loaders/Vendor.js.map +1 -0
  49. package/{Components → build/Components}/Logo.d.ts +2 -2
  50. package/build/Components/Logo.js +108 -0
  51. package/build/Components/Logo.js.map +1 -0
  52. package/{Components → build/Components}/MDUtils.d.ts +1 -2
  53. package/{Components → build/Components}/MDUtils.js +4 -9
  54. package/build/Components/MDUtils.js.map +1 -0
  55. package/{Components → build/Components}/ObjectBrowser.d.ts +4 -4
  56. package/{Components → build/Components}/ObjectBrowser.js +1152 -1116
  57. package/build/Components/ObjectBrowser.js.map +1 -0
  58. package/{Components → build/Components}/Router.d.ts +1 -2
  59. package/{Components → build/Components}/Router.js +6 -7
  60. package/build/Components/Router.js.map +1 -0
  61. package/{Components → build/Components}/SaveCloseButtons.d.ts +2 -2
  62. package/build/Components/SaveCloseButtons.js +65 -0
  63. package/build/Components/SaveCloseButtons.js.map +1 -0
  64. package/{Components → build/Components}/Schedule.d.ts +2 -2
  65. package/{Components → build/Components}/Schedule.js +242 -257
  66. package/build/Components/Schedule.js.map +1 -0
  67. package/{Components → build/Components}/SelectWithIcon.d.ts +2 -2
  68. package/build/Components/SelectWithIcon.js +135 -0
  69. package/build/Components/SelectWithIcon.js.map +1 -0
  70. package/build/Components/SimpleCron/cronText.js +15 -0
  71. package/build/Components/SimpleCron/cronText.js.map +1 -0
  72. package/{Components → build/Components}/SimpleCron/index.d.ts +2 -2
  73. package/{Components → build/Components}/SimpleCron/index.js +55 -58
  74. package/build/Components/SimpleCron/index.js.map +1 -0
  75. package/{Components → build/Components}/TabContainer.d.ts +2 -2
  76. package/build/Components/TabContainer.js +23 -0
  77. package/build/Components/TabContainer.js.map +1 -0
  78. package/{Components → build/Components}/TabContent.d.ts +3 -2
  79. package/build/Components/TabContent.js +20 -0
  80. package/build/Components/TabContent.js.map +1 -0
  81. package/build/Components/TabHeader.d.ts +6 -0
  82. package/build/Components/TabHeader.js +6 -0
  83. package/build/Components/TabHeader.js.map +1 -0
  84. package/{Components → build/Components}/TableResize.d.ts +2 -2
  85. package/{src/Components/TableResize.tsx → build/Components/TableResize.js} +64 -134
  86. package/build/Components/TableResize.js.map +1 -0
  87. package/{Components → build/Components}/TextWithIcon.d.ts +2 -2
  88. package/{src/Components/TextWithIcon.tsx → build/Components/TextWithIcon.js} +30 -75
  89. package/build/Components/TextWithIcon.js.map +1 -0
  90. package/{Components → build/Components}/ToggleThemeMenu.d.ts +1 -1
  91. package/build/Components/ToggleThemeMenu.js +13 -0
  92. package/build/Components/ToggleThemeMenu.js.map +1 -0
  93. package/{Components → build/Components}/TreeTable.d.ts +3 -3
  94. package/{Components → build/Components}/TreeTable.js +87 -99
  95. package/build/Components/TreeTable.js.map +1 -0
  96. package/{Components → build/Components}/UploadImage.d.ts +2 -2
  97. package/{Components → build/Components}/UploadImage.js +46 -69
  98. package/build/Components/UploadImage.js.map +1 -0
  99. package/{Components → build/Components}/Utils.d.ts +2 -2
  100. package/{Components → build/Components}/Utils.js +47 -60
  101. package/build/Components/Utils.js.map +1 -0
  102. package/build/Components/withWidth.d.ts +2 -0
  103. package/build/Components/withWidth.js +22 -0
  104. package/build/Components/withWidth.js.map +1 -0
  105. package/build/Connection.d.ts +1 -0
  106. package/build/Connection.js +2 -0
  107. package/build/Connection.js.map +1 -0
  108. package/{Dialogs → build/Dialogs}/ComplexCron.d.ts +2 -2
  109. package/build/Dialogs/ComplexCron.js +85 -0
  110. package/build/Dialogs/ComplexCron.js.map +1 -0
  111. package/{Dialogs → build/Dialogs}/Confirm.d.ts +2 -2
  112. package/build/Dialogs/Confirm.js +83 -0
  113. package/build/Dialogs/Confirm.js.map +1 -0
  114. package/{Dialogs → build/Dialogs}/Cron.d.ts +2 -2
  115. package/build/Dialogs/Cron.js +72 -0
  116. package/build/Dialogs/Cron.js.map +1 -0
  117. package/{Dialogs → build/Dialogs}/Error.d.ts +2 -2
  118. package/build/Dialogs/Error.js +27 -0
  119. package/build/Dialogs/Error.js.map +1 -0
  120. package/{Dialogs → build/Dialogs}/Message.d.ts +2 -2
  121. package/build/Dialogs/Message.js +29 -0
  122. package/build/Dialogs/Message.js.map +1 -0
  123. package/{Dialogs → build/Dialogs}/SelectFile.d.ts +2 -2
  124. package/build/Dialogs/SelectFile.js +116 -0
  125. package/build/Dialogs/SelectFile.js.map +1 -0
  126. package/{Dialogs → build/Dialogs}/SelectID.d.ts +3 -3
  127. package/{Dialogs → build/Dialogs}/SelectID.js +28 -53
  128. package/build/Dialogs/SelectID.js.map +1 -0
  129. package/{Dialogs → build/Dialogs}/SimpleCron.d.ts +2 -2
  130. package/build/Dialogs/SimpleCron.js +46 -0
  131. package/build/Dialogs/SimpleCron.js.map +1 -0
  132. package/build/Dialogs/TextInput.d.ts +2 -0
  133. package/build/Dialogs/TextInput.js +31 -0
  134. package/build/Dialogs/TextInput.js.map +1 -0
  135. package/{GenericApp.d.ts → build/GenericApp.d.ts} +2 -3
  136. package/{GenericApp.js → build/GenericApp.js} +162 -176
  137. package/build/GenericApp.js.map +1 -0
  138. package/{LegacyConnection.d.ts → build/LegacyConnection.d.ts} +69 -4
  139. package/{LegacyConnection.js → build/LegacyConnection.js} +106 -99
  140. package/build/LegacyConnection.js.map +1 -0
  141. package/{Prompt.d.ts → build/Prompt.d.ts} +1 -1
  142. package/{Prompt.js → build/Prompt.js} +3 -4
  143. package/build/Prompt.js.map +1 -0
  144. package/build/Theme.d.ts +5 -0
  145. package/{Theme.js → build/Theme.js} +37 -32
  146. package/build/Theme.js.map +1 -0
  147. package/build/assets/devices/parseNames.d.ts +0 -0
  148. package/build/assets/devices/parseNames.js +35 -0
  149. package/build/assets/devices/parseNames.js.map +1 -0
  150. package/build/assets/rooms/parseNames.d.ts +0 -0
  151. package/build/assets/rooms/parseNames.js +35 -0
  152. package/build/assets/rooms/parseNames.js.map +1 -0
  153. package/build/dictionary.d.ts +1 -0
  154. package/build/dictionary.js +25 -0
  155. package/build/dictionary.js.map +1 -0
  156. package/build/i18n/de.json +449 -0
  157. package/build/i18n/en.json +449 -0
  158. package/build/i18n/es.json +449 -0
  159. package/build/i18n/fr.json +449 -0
  160. package/build/i18n/it.json +449 -0
  161. package/build/i18n/nl.json +449 -0
  162. package/build/i18n/pl.json +449 -0
  163. package/build/i18n/pt.json +449 -0
  164. package/build/i18n/ru.json +449 -0
  165. package/build/i18n/uk.json +449 -0
  166. package/build/i18n/zh-cn.json +449 -0
  167. package/{i18n.d.ts → build/i18n.d.ts} +2 -2
  168. package/{i18n.js → build/i18n.js} +9 -11
  169. package/build/i18n.js.map +1 -0
  170. package/build/icons/IconAdapter.d.ts +3 -0
  171. package/build/icons/IconAdapter.js +6 -0
  172. package/build/icons/IconAdapter.js.map +1 -0
  173. package/build/icons/IconAlias.d.ts +3 -0
  174. package/build/icons/IconAlias.js +6 -0
  175. package/build/icons/IconAlias.js.map +1 -0
  176. package/build/icons/IconChannel.d.ts +3 -0
  177. package/build/icons/IconChannel.js +9 -0
  178. package/build/icons/IconChannel.js.map +1 -0
  179. package/build/icons/IconClearFilter.d.ts +3 -0
  180. package/build/icons/IconClearFilter.js +7 -0
  181. package/build/icons/IconClearFilter.js.map +1 -0
  182. package/build/icons/IconClosed.d.ts +3 -0
  183. package/build/icons/IconClosed.js +6 -0
  184. package/build/icons/IconClosed.js.map +1 -0
  185. package/build/icons/IconCopy.d.ts +3 -0
  186. package/build/icons/IconCopy.js +5 -0
  187. package/build/icons/IconCopy.js.map +1 -0
  188. package/build/icons/IconDevice.d.ts +3 -0
  189. package/build/icons/IconDevice.js +15 -0
  190. package/build/icons/IconDevice.js.map +1 -0
  191. package/build/icons/IconDocument.d.ts +3 -0
  192. package/build/icons/IconDocument.js +6 -0
  193. package/build/icons/IconDocument.js.map +1 -0
  194. package/build/icons/IconDocumentReadOnly.d.ts +3 -0
  195. package/build/icons/IconDocumentReadOnly.js +7 -0
  196. package/build/icons/IconDocumentReadOnly.js.map +1 -0
  197. package/build/icons/IconExpert.d.ts +3 -0
  198. package/build/icons/IconExpert.js +6 -0
  199. package/build/icons/IconExpert.js.map +1 -0
  200. package/build/icons/IconFx.d.ts +3 -0
  201. package/build/icons/IconFx.js +5 -0
  202. package/build/icons/IconFx.js.map +1 -0
  203. package/build/icons/IconInstance.d.ts +3 -0
  204. package/build/icons/IconInstance.js +6 -0
  205. package/build/icons/IconInstance.js.map +1 -0
  206. package/build/icons/IconLogout.d.ts +3 -0
  207. package/build/icons/IconLogout.js +6 -0
  208. package/build/icons/IconLogout.js.map +1 -0
  209. package/build/icons/IconNoIcon.d.ts +3 -0
  210. package/build/icons/IconNoIcon.js +5 -0
  211. package/build/icons/IconNoIcon.js.map +1 -0
  212. package/build/icons/IconOpen.d.ts +3 -0
  213. package/build/icons/IconOpen.js +6 -0
  214. package/build/icons/IconOpen.js.map +1 -0
  215. package/{icons → build/icons}/IconProps.d.ts +1 -1
  216. package/build/icons/IconProps.js +2 -0
  217. package/build/icons/IconProps.js.map +1 -0
  218. package/build/icons/IconState.d.ts +3 -0
  219. package/build/icons/IconState.js +6 -0
  220. package/build/icons/IconState.js.map +1 -0
  221. package/build/index.d.ts +67 -0
  222. package/build/index.js +67 -0
  223. package/build/index.js.map +1 -0
  224. package/{types.d.ts → build/types.d.ts} +1 -1
  225. package/package.json +84 -48
  226. package/AdminConnection.d.ts +0 -2
  227. package/AdminConnection.js +0 -4
  228. package/Components/404.js +0 -101
  229. package/Components/CopyToClipboard.js +0 -163
  230. package/Components/CustomModal.js +0 -88
  231. package/Components/FileViewer.d.ts +0 -10
  232. package/Components/FileViewer.js +0 -305
  233. package/Components/Icon.js +0 -148
  234. package/Components/IconPicker.js +0 -98
  235. package/Components/Loaders/MV.js +0 -66
  236. package/Components/Loaders/Vendor.js +0 -77
  237. package/Components/Logo.js +0 -117
  238. package/Components/SaveCloseButtons.js +0 -69
  239. package/Components/SelectWithIcon.js +0 -168
  240. package/Components/SimpleCron/cronText.js +0 -19
  241. package/Components/TabContainer.js +0 -25
  242. package/Components/TabContent.js +0 -21
  243. package/Components/TabHeader.d.ts +0 -6
  244. package/Components/TabHeader.js +0 -11
  245. package/Components/TableResize.js +0 -226
  246. package/Components/TextWithIcon.js +0 -119
  247. package/Components/ToggleThemeMenu.js +0 -18
  248. package/Components/withWidth.d.ts +0 -3
  249. package/Components/withWidth.js +0 -27
  250. package/Connection.d.ts +0 -3
  251. package/Connection.js +0 -8
  252. package/Dialogs/ComplexCron.js +0 -90
  253. package/Dialogs/Confirm.js +0 -111
  254. package/Dialogs/Cron.js +0 -100
  255. package/Dialogs/Error.js +0 -55
  256. package/Dialogs/Message.js +0 -57
  257. package/Dialogs/SelectFile.js +0 -119
  258. package/Dialogs/SimpleCron.js +0 -51
  259. package/Dialogs/TextInput.d.ts +0 -3
  260. package/Dialogs/TextInput.js +0 -35
  261. package/Theme.d.ts +0 -6
  262. package/i18n/de.json +0 -449
  263. package/i18n/en.json +0 -449
  264. package/i18n/es.json +0 -449
  265. package/i18n/fr.json +0 -449
  266. package/i18n/it.json +0 -449
  267. package/i18n/nl.json +0 -449
  268. package/i18n/pl.json +0 -449
  269. package/i18n/pt.json +0 -449
  270. package/i18n/ru.json +0 -449
  271. package/i18n/uk.json +0 -449
  272. package/i18n/zh-cn.json +0 -449
  273. package/icons/IconAdapter.d.ts +0 -4
  274. package/icons/IconAdapter.js +0 -10
  275. package/icons/IconAlias.d.ts +0 -4
  276. package/icons/IconAlias.js +0 -10
  277. package/icons/IconChannel.d.ts +0 -4
  278. package/icons/IconChannel.js +0 -13
  279. package/icons/IconClearFilter.d.ts +0 -4
  280. package/icons/IconClearFilter.js +0 -11
  281. package/icons/IconClosed.d.ts +0 -4
  282. package/icons/IconClosed.js +0 -10
  283. package/icons/IconCopy.d.ts +0 -4
  284. package/icons/IconCopy.js +0 -9
  285. package/icons/IconDevice.d.ts +0 -4
  286. package/icons/IconDevice.js +0 -19
  287. package/icons/IconDocument.d.ts +0 -4
  288. package/icons/IconDocument.js +0 -10
  289. package/icons/IconDocumentReadOnly.d.ts +0 -4
  290. package/icons/IconDocumentReadOnly.js +0 -11
  291. package/icons/IconExpert.d.ts +0 -4
  292. package/icons/IconExpert.js +0 -10
  293. package/icons/IconFx.d.ts +0 -4
  294. package/icons/IconFx.js +0 -9
  295. package/icons/IconInstance.d.ts +0 -4
  296. package/icons/IconInstance.js +0 -10
  297. package/icons/IconLogout.d.ts +0 -4
  298. package/icons/IconLogout.js +0 -10
  299. package/icons/IconNoIcon.d.ts +0 -4
  300. package/icons/IconNoIcon.js +0 -9
  301. package/icons/IconOpen.d.ts +0 -4
  302. package/icons/IconOpen.js +0 -10
  303. package/icons/IconProps.js +0 -2
  304. package/icons/IconState.d.ts +0 -4
  305. package/icons/IconState.js +0 -10
  306. package/index.d.ts +0 -128
  307. package/index.js +0 -215
  308. package/src/AdminConnection.tsx +0 -3
  309. package/src/Components/ColorPicker.tsx +0 -343
  310. package/src/Components/ComplexCron.tsx +0 -561
  311. package/src/Components/CustomModal.tsx +0 -170
  312. package/src/Components/FileBrowser.tsx +0 -2560
  313. package/src/Components/FileViewer.tsx +0 -412
  314. package/src/Components/Icon.tsx +0 -238
  315. package/src/Components/IconPicker.tsx +0 -165
  316. package/src/Components/IconSelector.tsx +0 -2220
  317. package/src/Components/Image.tsx +0 -193
  318. package/src/Components/Loader.tsx +0 -328
  319. package/src/Components/Logo.tsx +0 -176
  320. package/src/Components/MDUtils.tsx +0 -104
  321. package/src/Components/ObjectBrowser.tsx +0 -8947
  322. package/src/Components/Router.tsx +0 -90
  323. package/src/Components/SaveCloseButtons.tsx +0 -117
  324. package/src/Components/Schedule.tsx +0 -1998
  325. package/src/Components/SelectWithIcon.tsx +0 -239
  326. package/src/Components/TabContainer.tsx +0 -57
  327. package/src/Components/TabContent.tsx +0 -38
  328. package/src/Components/TabHeader.tsx +0 -20
  329. package/src/Components/ToggleThemeMenu.tsx +0 -52
  330. package/src/Components/TreeTable.tsx +0 -1002
  331. package/src/Components/UploadImage.tsx +0 -643
  332. package/src/Components/Utils.tsx +0 -1802
  333. package/src/Components/loader.css +0 -231
  334. package/src/Components/withWidth.tsx +0 -32
  335. package/src/Connection.tsx +0 -5
  336. package/src/Dialogs/ComplexCron.tsx +0 -163
  337. package/src/Dialogs/Confirm.tsx +0 -185
  338. package/src/Dialogs/Cron.tsx +0 -192
  339. package/src/Dialogs/Error.tsx +0 -67
  340. package/src/Dialogs/Message.tsx +0 -73
  341. package/src/Dialogs/SelectFile.tsx +0 -280
  342. package/src/Dialogs/SelectID.tsx +0 -310
  343. package/src/Dialogs/SimpleCron.tsx +0 -101
  344. package/src/Dialogs/TextInput.tsx +0 -99
  345. package/src/GenericApp.tsx +0 -1076
  346. package/src/LegacyConnection.tsx +0 -3720
  347. package/src/Prompt.tsx +0 -22
  348. package/src/Theme.tsx +0 -472
  349. package/src/icons/IconAdapter.tsx +0 -22
  350. package/src/icons/IconAlias.tsx +0 -22
  351. package/src/icons/IconChannel.tsx +0 -60
  352. package/src/icons/IconClearFilter.tsx +0 -24
  353. package/src/icons/IconClosed.tsx +0 -22
  354. package/src/icons/IconCopy.tsx +0 -21
  355. package/src/icons/IconDevice.tsx +0 -126
  356. package/src/icons/IconDocument.tsx +0 -22
  357. package/src/icons/IconDocumentReadOnly.tsx +0 -27
  358. package/src/icons/IconExpert.tsx +0 -26
  359. package/src/icons/IconFx.tsx +0 -38
  360. package/src/icons/IconInstance.tsx +0 -22
  361. package/src/icons/IconLogout.tsx +0 -32
  362. package/src/icons/IconNoIcon.tsx +0 -21
  363. package/src/icons/IconOpen.tsx +0 -22
  364. package/src/icons/IconProps.tsx +0 -16
  365. package/src/icons/IconState.tsx +0 -38
  366. package/src/index.css +0 -56
  367. /package/{Components → build/Components}/404.d.ts +0 -0
  368. /package/{Components → build/Components}/SimpleCron/cronText.d.ts +0 -0
  369. /package/{assets → build/assets}/devices.json +0 -0
  370. /package/{assets → build/assets}/lamp_ceiling.svg +0 -0
  371. /package/{assets → build/assets}/lamp_table.svg +0 -0
  372. /package/{assets → build/assets}/no_icon.svg +0 -0
  373. /package/{assets → build/assets}/rooms.json +0 -0
  374. /package/{index.css → build/index.css} +0 -0
  375. /package/{tasks.js → tasksExample.js} +0 -0
@@ -1,1076 +0,0 @@
1
- /**
2
- * Copyright 2018-2024 Denis Haev (bluefox) <dogafox@gmail.com>
3
- *
4
- * MIT License
5
- *
6
- */
7
- import React, { type JSX } from 'react';
8
- import { PROGRESS, Connection, type AdminConnection } from '@iobroker/socket-client';
9
- import * as Sentry from '@sentry/browser';
10
-
11
- import { Snackbar, IconButton } from '@mui/material';
12
-
13
- import { Close as IconClose } from '@mui/icons-material';
14
-
15
- import printPrompt from './Prompt';
16
- import Theme from './Theme';
17
- import Loader from './Components/Loader';
18
- import Router from './Components/Router';
19
- import Utils from './Components/Utils';
20
- import SaveCloseButtons from './Components/SaveCloseButtons';
21
- import ConfirmDialog from './Dialogs/Confirm';
22
- import I18n from './i18n';
23
- import DialogError from './Dialogs/Error';
24
- import type {
25
- GenericAppProps,
26
- GenericAppState,
27
- GenericAppSettings,
28
- ThemeName,
29
- ThemeType,
30
- IobTheme,
31
- Width,
32
- } from './types';
33
-
34
- import langEn from './i18n/en.json';
35
- import langDe from './i18n/de.json';
36
- import langRu from './i18n/ru.json';
37
- import langPt from './i18n/pt.json';
38
- import langNl from './i18n/nl.json';
39
- import langFr from './i18n/fr.json';
40
- import langIt from './i18n/it.json';
41
- import langEs from './i18n/es.json';
42
- import langPl from './i18n/pl.json';
43
- import langUk from './i18n/uk.json';
44
- import langZhCn from './i18n/zh-cn.json';
45
-
46
- declare global {
47
- /** If config has been changed */
48
- // eslint-disable-next-line no-var
49
- var changed: boolean;
50
-
51
- interface Window {
52
- io: any;
53
- SocketClient: any;
54
- adapterName: undefined | string;
55
- socketUrl: undefined | string;
56
- oldAlert: any;
57
- changed: boolean;
58
- $iframeDialog: {
59
- close?: () => void;
60
- };
61
- }
62
- }
63
-
64
- // import './index.css';
65
- const cssStyle = `
66
- html {
67
- height: 100%;
68
- }
69
-
70
- body {
71
- margin: 0;
72
- padding: 0;
73
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
74
- -webkit-font-smoothing: antialiased;
75
- -moz-osx-font-smoothing: grayscale;
76
- width: 100%;
77
- height: 100%;
78
- overflow: hidden;
79
- }
80
-
81
- /* scrollbar */
82
- ::-webkit-scrollbar-track {
83
- background-color: #ccc;
84
- border-radius: 5px;
85
- }
86
-
87
- ::-webkit-scrollbar {
88
- width: 5px;
89
- height: 5px;
90
- background-color: #ccc;
91
- }
92
-
93
- ::-webkit-scrollbar-thumb {
94
- background-color: #575757;
95
- border-radius: 5px;
96
- }
97
-
98
- #root {
99
- height: 100%;
100
- }
101
-
102
- .App {
103
- height: 100%;
104
- }
105
-
106
- @keyframes glow {
107
- from {
108
- background-color: initial;
109
- }
110
- to {
111
- background-color: #58c458;
112
- }
113
- }
114
- `;
115
-
116
- class GenericApp<
117
- TProps extends GenericAppProps = GenericAppProps,
118
- TState extends GenericAppState = GenericAppState,
119
- > extends Router<TProps, TState> {
120
- protected socket: AdminConnection;
121
-
122
- protected readonly instance: number;
123
-
124
- protected readonly adapterName: string;
125
-
126
- protected readonly instanceId: string;
127
-
128
- protected readonly newReact: boolean;
129
-
130
- protected encryptedFields: string[];
131
-
132
- protected readonly sentryDSN: string | undefined;
133
-
134
- private alertDialogRendered: boolean;
135
-
136
- private _secret: string | undefined;
137
-
138
- protected _systemConfig: ioBroker.SystemConfigCommon | undefined;
139
-
140
- // it is not readonly
141
- private savedNative: Record<string, any>;
142
-
143
- protected common: ioBroker.InstanceCommon | null = null;
144
-
145
- private sentryStarted: boolean = false;
146
-
147
- private sentryInited: boolean = false;
148
-
149
- private resizeTimer: ReturnType<typeof setTimeout> | null = null;
150
-
151
- constructor(props: TProps, settings?: GenericAppSettings) {
152
- const ConnectionClass = (props.Connection ||
153
- settings?.Connection ||
154
- Connection) as unknown as typeof AdminConnection;
155
- // const ConnectionClass = props.Connection === 'admin' || settings.Connection = 'admin' ? AdminConnection : (props.Connection || settings.Connection || Connection);
156
-
157
- if (!window.document.getElementById('generic-app-iobroker-component')) {
158
- const style = window.document.createElement('style');
159
- style.setAttribute('id', 'generic-app-iobroker-component');
160
- style.innerHTML = cssStyle;
161
- window.document.head.appendChild(style);
162
- }
163
-
164
- // Remove `!Connection.isWeb() && window.adapterName !== 'material'` when iobroker.socket will support native ws
165
- if (!GenericApp.isWeb() && window.io && window.location.port === '3000') {
166
- try {
167
- const io = new window.SocketClient();
168
- delete window.io;
169
- window.io = io;
170
- } catch {
171
- // ignore
172
- }
173
- }
174
-
175
- super(props);
176
-
177
- printPrompt();
178
-
179
- const query = (window.location.search || '').replace(/^\?/, '').replace(/#.*$/, '');
180
- const args: Record<string, string | boolean> = {};
181
- query
182
- .trim()
183
- .split('&')
184
- .filter(t => t.trim())
185
- .forEach(b => {
186
- const parts = b.split('=');
187
- args[parts[0]] = parts.length === 2 ? parts[1] : true;
188
- if (args[parts[0]] === 'true') {
189
- args[parts[0]] = true;
190
- } else if (args[parts[0]] === 'false') {
191
- args[parts[0]] = false;
192
- }
193
- });
194
-
195
- // extract instance from URL
196
- this.instance =
197
- settings?.instance ??
198
- props.instance ??
199
- (args.instance !== undefined
200
- ? parseInt(args.instance as string, 10) || 0
201
- : parseInt(window.location.search.slice(1), 10) || 0);
202
- // extract adapter name from URL
203
- const tmp = window.location.pathname.split('/');
204
- this.adapterName =
205
- settings?.adapterName || props.adapterName || window.adapterName || tmp[tmp.length - 2] || 'iot';
206
- this.instanceId = `system.adapter.${this.adapterName}.${this.instance}`;
207
- this.newReact = args.newReact === true; // it is admin5
208
-
209
- const location = Router.getLocation();
210
- location.tab =
211
- location.tab ||
212
- ((window as any)._localStorage || window.localStorage).getItem(`${this.adapterName}-adapter`) ||
213
- '';
214
-
215
- const themeInstance = this.createTheme();
216
-
217
- this.state = {
218
- ...(this.state || {}), // keep the existing state
219
- selectedTab:
220
- ((window as any)._localStorage || window.localStorage).getItem(`${this.adapterName}-adapter`) || '',
221
- selectedTabNum: -1,
222
- native: {},
223
- errorText: '',
224
- changed: false,
225
- connected: false,
226
- loaded: false,
227
- isConfigurationError: '',
228
- expertMode: false,
229
- toast: '',
230
- theme: themeInstance,
231
- themeName: this.getThemeName(themeInstance),
232
- themeType: this.getThemeType(themeInstance),
233
- bottomButtons: (settings && settings.bottomButtons) === false ? false : props?.bottomButtons !== false,
234
- width: GenericApp.getWidth(),
235
- confirmClose: false,
236
- _alert: false,
237
- _alertType: 'info',
238
- _alertMessage: '',
239
- } satisfies GenericAppState as TState;
240
-
241
- // init translations
242
- const translations: Record<ioBroker.Languages, Record<string, string>> = {
243
- en: langEn,
244
- de: langDe,
245
- ru: langRu,
246
- pt: langPt,
247
- nl: langNl,
248
- fr: langFr,
249
- it: langIt,
250
- es: langEs,
251
- pl: langPl,
252
- uk: langUk,
253
- 'zh-cn': langZhCn,
254
- };
255
-
256
- // merge together
257
- if (settings?.translations) {
258
- Object.keys(settings.translations).forEach(lang => {
259
- if (settings.translations) {
260
- translations[lang as ioBroker.Languages] = Object.assign(
261
- translations[lang as ioBroker.Languages],
262
- settings.translations[lang as ioBroker.Languages] || {},
263
- );
264
- }
265
- });
266
- } else if (props.translations) {
267
- Object.keys(props.translations).forEach(lang => {
268
- if (props.translations) {
269
- translations[lang as ioBroker.Languages] = Object.assign(
270
- translations[lang as ioBroker.Languages],
271
- props.translations[lang as ioBroker.Languages] || {},
272
- );
273
- }
274
- });
275
- }
276
-
277
- I18n.setTranslations(translations);
278
-
279
- this.savedNative = {}; // to detect if the config changed
280
-
281
- this.encryptedFields = props.encryptedFields || settings?.encryptedFields || [];
282
-
283
- this.sentryDSN = (settings && settings.sentryDSN) || props.sentryDSN;
284
-
285
- if (window.socketUrl) {
286
- if (window.socketUrl.startsWith(':')) {
287
- window.socketUrl = `${window.location.protocol}//${window.location.hostname}${window.socketUrl}`;
288
- } else if (!window.socketUrl.startsWith('http://') && !window.socketUrl.startsWith('https://')) {
289
- window.socketUrl = `${window.location.protocol}//${window.socketUrl}`;
290
- }
291
- }
292
-
293
- this.alertDialogRendered = false;
294
-
295
- window.oldAlert = window.alert;
296
- window.alert = message => {
297
- if (!this.alertDialogRendered) {
298
- window.oldAlert(message);
299
- return;
300
- }
301
- if (message && message.toString().toLowerCase().includes('error')) {
302
- console.error(message);
303
- this.showAlert(message.toString(), 'error');
304
- } else {
305
- console.log(message);
306
- this.showAlert(message.toString(), 'info');
307
- }
308
- };
309
-
310
- // @ts-expect-error either make props in ConnectionProps required or the constructor needs to accept than as they are (means adapt socket-client)
311
- this.socket = new ConnectionClass({
312
- ...(props?.socket || settings?.socket),
313
- name: this.adapterName,
314
- doNotLoadAllObjects: settings?.doNotLoadAllObjects,
315
- onProgress: (progress: PROGRESS) => {
316
- if (progress === PROGRESS.CONNECTING) {
317
- this.setState({ connected: false });
318
- } else if (progress === PROGRESS.READY) {
319
- this.setState({ connected: true });
320
- } else {
321
- this.setState({ connected: true });
322
- }
323
- },
324
- onReady: (/* objects, scripts */) => {
325
- I18n.setLanguage(this.socket.systemLang);
326
-
327
- // subscribe because of language and expert mode
328
- this.socket
329
- .subscribeObject('system.config', this.onSystemConfigChanged)
330
- .then(() => this.getSystemConfig())
331
- .then(obj => {
332
- this._secret =
333
- (typeof obj !== 'undefined' && obj.native && obj.native.secret) || 'Zgfr56gFe87jJOM';
334
- this._systemConfig = obj?.common || ({} as ioBroker.SystemConfigCommon);
335
- return this.socket.getObject(this.instanceId);
336
- })
337
- .then(async obj => {
338
- let waitPromise;
339
- const instanceObj: ioBroker.InstanceObject | null | undefined = obj as
340
- | ioBroker.InstanceObject
341
- | null
342
- | undefined;
343
-
344
- const sentryPluginEnabled = (
345
- await this.socket.getState(`${this.instanceId}.plugins.sentry.enabled`)
346
- )?.val;
347
-
348
- const sentryEnabled =
349
- sentryPluginEnabled !== false &&
350
- this._systemConfig?.diag !== 'none' &&
351
- instanceObj?.common &&
352
- instanceObj.common.name &&
353
- instanceObj.common.version &&
354
- // @ts-expect-error will be extended in js-controller TODO: (BF: 2024.05.30) this is redundant to state `${this.instanceId}.plugins.sentry.enabled`, remove this in future when admin sets the state correctly
355
- !instanceObj.common.disableDataReporting &&
356
- window.location.host !== 'localhost:3000';
357
-
358
- // activate sentry plugin
359
- if (!this.sentryStarted && this.sentryDSN && sentryEnabled) {
360
- this.sentryStarted = true;
361
-
362
- Sentry.init({
363
- dsn: this.sentryDSN,
364
- release: `iobroker.${instanceObj.common.name}@${instanceObj.common.version}`,
365
- integrations: [Sentry.dedupeIntegration()],
366
- });
367
-
368
- console.log('Sentry initialized');
369
- }
370
-
371
- // read UUID and init sentry with it.
372
- // for backward compatibility it will be processed separately from the above logic: some adapters could still have this.sentryDSN as undefined
373
- if (!this.sentryInited && sentryEnabled) {
374
- this.sentryInited = true;
375
-
376
- waitPromise = this.socket.getObject('system.meta.uuid').then(uuidObj => {
377
- if (uuidObj && uuidObj.native && uuidObj.native.uuid) {
378
- const scope = Sentry.getCurrentScope();
379
- scope.setUser({ id: uuidObj.native.uuid });
380
- }
381
- });
382
- }
383
-
384
- waitPromise = waitPromise || Promise.resolve();
385
-
386
- void waitPromise.then(() => {
387
- if (instanceObj) {
388
- this.common = instanceObj?.common;
389
- this.onPrepareLoad(instanceObj.native, instanceObj.encryptedNative); // decode all secrets
390
- this.savedNative = JSON.parse(JSON.stringify(instanceObj.native));
391
- this.setState(
392
- { native: instanceObj.native, loaded: true, expertMode: this.getExpertMode() },
393
- () => this.onConnectionReady && this.onConnectionReady(),
394
- );
395
- } else {
396
- console.warn('Cannot load instance settings');
397
- this.setState(
398
- {
399
- native: {},
400
- loaded: true,
401
- expertMode: this.getExpertMode(),
402
- },
403
- () => this.onConnectionReady && this.onConnectionReady(),
404
- );
405
- }
406
- });
407
- })
408
- .catch(e => window.alert(`Cannot settings: ${e}`));
409
- },
410
- onError: (err: string) => {
411
- console.error(err);
412
- this.showError(err);
413
- },
414
- });
415
- }
416
-
417
- /**
418
- * Checks if this connection is running in a web adapter and not in an admin.
419
- *
420
- * @returns True if running in a web adapter or in a socketio adapter.
421
- */
422
- static isWeb(): boolean {
423
- return window.socketUrl !== undefined;
424
- }
425
-
426
- showAlert(message: string, type?: 'info' | 'warning' | 'error' | 'success'): void {
427
- if (type !== 'error' && type !== 'warning' && type !== 'info' && type !== 'success') {
428
- type = 'info';
429
- }
430
-
431
- this.setState({
432
- _alert: true,
433
- _alertType: type,
434
- _alertMessage: message,
435
- });
436
- }
437
-
438
- renderAlertSnackbar(): JSX.Element {
439
- this.alertDialogRendered = true;
440
-
441
- return (
442
- <Snackbar
443
- style={
444
- this.state._alertType === 'error'
445
- ? { backgroundColor: '#f44336' }
446
- : this.state._alertType === 'success'
447
- ? { backgroundColor: '#4caf50' }
448
- : undefined
449
- }
450
- open={this.state._alert}
451
- autoHideDuration={6000}
452
- onClose={(_e, reason) => reason !== 'clickaway' && this.setState({ _alert: false })}
453
- message={this.state._alertMessage}
454
- />
455
- );
456
- }
457
-
458
- onSystemConfigChanged = (id: string, obj: ioBroker.AnyObject | null | undefined): void => {
459
- if (obj && id === 'system.config') {
460
- if (this.socket.systemLang !== (obj as ioBroker.SystemConfigObject)?.common.language) {
461
- this.socket.systemLang = (obj as ioBroker.SystemConfigObject)?.common.language || 'en';
462
- I18n.setLanguage(this.socket.systemLang);
463
- }
464
-
465
- if (this._systemConfig?.expertMode !== !!(obj as ioBroker.SystemConfigObject)?.common?.expertMode) {
466
- this._systemConfig =
467
- (obj as ioBroker.SystemConfigObject)?.common || ({} as ioBroker.SystemConfigCommon);
468
- this.setState({ expertMode: this.getExpertMode() });
469
- } else {
470
- this._systemConfig =
471
- (obj as ioBroker.SystemConfigObject)?.common || ({} as ioBroker.SystemConfigCommon);
472
- }
473
- }
474
- };
475
-
476
- /**
477
- * Called immediately after a component is mounted. Setting state here will trigger re-rendering.
478
- */
479
- componentDidMount(): void {
480
- window.addEventListener('resize', this.onResize, true);
481
- window.addEventListener('message', this.onReceiveMessage, false);
482
- super.componentDidMount();
483
- }
484
-
485
- /**
486
- * Called immediately before a component is destroyed.
487
- */
488
- componentWillUnmount(): void {
489
- window.removeEventListener('resize', this.onResize, true);
490
- window.removeEventListener('message', this.onReceiveMessage, false);
491
- super.componentWillUnmount();
492
- }
493
-
494
- onReceiveMessage = (message: { data: string } | null): void => {
495
- if (message?.data) {
496
- if (message.data === 'updateTheme') {
497
- const newThemeName = Utils.getThemeName();
498
- Utils.setThemeName(Utils.getThemeName());
499
-
500
- const newTheme = this.createTheme(newThemeName);
501
-
502
- this.setState(
503
- {
504
- theme: newTheme,
505
- themeName: this.getThemeName(newTheme),
506
- themeType: this.getThemeType(newTheme),
507
- },
508
- () => {
509
- this.props.onThemeChange && this.props.onThemeChange(newThemeName);
510
- this.onThemeChanged && this.onThemeChanged(newThemeName);
511
- },
512
- );
513
- } else if (message.data === 'updateExpertMode') {
514
- this.onToggleExpertMode && this.onToggleExpertMode(this.getExpertMode());
515
- } else if (message.data !== 'chartReady') {
516
- // if not "echart ready" message
517
- console.debug(
518
- `Received unknown message: "${JSON.stringify(message.data)}". May be it will be processed later`,
519
- );
520
- }
521
- }
522
- };
523
-
524
- private onResize = (): void => {
525
- this.resizeTimer && clearTimeout(this.resizeTimer);
526
- this.resizeTimer = setTimeout(() => {
527
- this.resizeTimer = null;
528
- this.setState({ width: GenericApp.getWidth() });
529
- }, 200);
530
- };
531
-
532
- /**
533
- * Gets the width depending on the window inner width.
534
- */
535
- static getWidth(): Width {
536
- /**
537
- * innerWidth |xs sm md lg xl
538
- * |-------|-------|-------|-------|------>
539
- * width | xs | sm | md | lg | xl
540
- */
541
-
542
- const SIZES: Record<Width, number> = {
543
- xs: 0,
544
- sm: 600,
545
- md: 960,
546
- lg: 1280,
547
- xl: 1920,
548
- };
549
- const width = window.innerWidth;
550
- const keys = Object.keys(SIZES).reverse();
551
- const widthComputed = keys.find(key => width >= SIZES[key as Width]) as Width;
552
-
553
- return widthComputed || 'xs';
554
- }
555
-
556
- /**
557
- * Get a theme
558
- *
559
- * @param name Theme name
560
- */
561
- // eslint-disable-next-line class-methods-use-this
562
- createTheme(name?: ThemeName | null): IobTheme {
563
- return Theme(Utils.getThemeName(name));
564
- }
565
-
566
- /**
567
- * Get the theme name
568
- */
569
- // eslint-disable-next-line class-methods-use-this
570
- getThemeName(currentTheme: IobTheme): ThemeName {
571
- return currentTheme.name;
572
- }
573
-
574
- /**
575
- * Get the theme type
576
- */
577
- // eslint-disable-next-line class-methods-use-this
578
- getThemeType(currentTheme: IobTheme): ThemeType {
579
- return currentTheme.palette.mode;
580
- }
581
-
582
- // eslint-disable-next-line class-methods-use-this
583
- onThemeChanged(_newThemeName: string): void {}
584
-
585
- // eslint-disable-next-line class-methods-use-this
586
- onToggleExpertMode(_expertMode: boolean): void {}
587
-
588
- /**
589
- * Changes the current theme
590
- */
591
- toggleTheme(newThemeName?: ThemeName): void {
592
- const themeName = this.state.themeName;
593
-
594
- // dark => blue => colored => light => dark
595
- newThemeName =
596
- newThemeName ||
597
- (themeName === 'dark'
598
- ? 'light'
599
- : themeName === 'blue'
600
- ? 'light'
601
- : themeName === 'colored'
602
- ? 'light'
603
- : 'dark');
604
-
605
- if (newThemeName !== themeName) {
606
- Utils.setThemeName(newThemeName);
607
-
608
- const newTheme = this.createTheme(newThemeName);
609
-
610
- this.setState(
611
- {
612
- theme: newTheme,
613
- themeName: this.getThemeName(newTheme),
614
- themeType: this.getThemeType(newTheme),
615
- },
616
- () => {
617
- this.props.onThemeChange && this.props.onThemeChange(newThemeName || 'light');
618
- this.onThemeChanged && this.onThemeChanged(newThemeName || 'light');
619
- },
620
- );
621
- }
622
- }
623
-
624
- /**
625
- * Gets the system configuration.
626
- */
627
- getSystemConfig(): Promise<ioBroker.SystemConfigObject> {
628
- return this.socket.getSystemConfig();
629
- }
630
-
631
- /**
632
- * Get current expert mode
633
- */
634
- getExpertMode(): boolean {
635
- return window.sessionStorage.getItem('App.expertMode') === 'true' || !!this._systemConfig?.expertMode;
636
- }
637
-
638
- /**
639
- * Gets called when the socket.io connection is ready.
640
- * You can overload this function to execute own commands.
641
- */
642
- // eslint-disable-next-line class-methods-use-this
643
- onConnectionReady(): void {}
644
-
645
- /**
646
- * Encrypts a string.
647
- */
648
- encrypt(value: string): string {
649
- let result = '';
650
- if (this._secret) {
651
- for (let i = 0; i < value.length; i++) {
652
- result += String.fromCharCode(
653
- this._secret[i % this._secret.length].charCodeAt(0) ^ value.charCodeAt(i),
654
- );
655
- }
656
- }
657
- return result;
658
- }
659
-
660
- /**
661
- * Decrypts a string.
662
- */
663
- decrypt(value: string): string {
664
- let result = '';
665
- if (this._secret) {
666
- for (let i = 0; i < value.length; i++) {
667
- result += String.fromCharCode(
668
- this._secret[i % this._secret.length].charCodeAt(0) ^ value.charCodeAt(i),
669
- );
670
- }
671
- }
672
- return result;
673
- }
674
-
675
- /**
676
- * Gets called when the navigation hash changes.
677
- * You may override this if needed.
678
- */
679
- onHashChanged(): void {
680
- const location = Router.getLocation();
681
- if (location.tab !== this.state.selectedTab) {
682
- this.selectTab(location.tab);
683
- }
684
- }
685
-
686
- /**
687
- * Selects the given tab.
688
- */
689
- selectTab(tab: string, index?: number): void {
690
- ((window as any)._localStorage || window.localStorage).setItem(`${this.adapterName}-adapter`, tab);
691
- this.setState({ selectedTab: tab, selectedTabNum: index });
692
- }
693
-
694
- /**
695
- * Gets called before the settings are saved.
696
- * You may override this if needed.
697
- */
698
- onPrepareSave(settings: Record<string, any>): boolean {
699
- // here you can encode values
700
- this.encryptedFields &&
701
- this.encryptedFields.forEach(attr => {
702
- if (settings[attr]) {
703
- settings[attr] = this.encrypt(settings[attr]);
704
- }
705
- });
706
-
707
- return true;
708
- }
709
-
710
- /**
711
- * Gets called after the settings are loaded.
712
- * You may override this if needed.
713
- *
714
- * @param settings instance settings from native part
715
- * @param encryptedNative optional list of fields to be decrypted
716
- */
717
- onPrepareLoad(settings: Record<string, any>, encryptedNative?: string[]): void {
718
- // here you can encode values
719
- this.encryptedFields &&
720
- this.encryptedFields.forEach(attr => {
721
- if (settings[attr]) {
722
- settings[attr] = this.decrypt(settings[attr]);
723
- }
724
- });
725
- encryptedNative &&
726
- encryptedNative.forEach(attr => {
727
- this.encryptedFields = this.encryptedFields || [];
728
- !this.encryptedFields.includes(attr) && this.encryptedFields.push(attr);
729
- if (settings[attr]) {
730
- settings[attr] = this.decrypt(settings[attr]);
731
- }
732
- });
733
- }
734
-
735
- /**
736
- * Gets the extendable instances.
737
- */
738
- async getExtendableInstances(): Promise<ioBroker.InstanceObject[]> {
739
- try {
740
- const instances = await this.socket.getObjectViewSystem(
741
- 'instance',
742
- 'system.adapter.',
743
- 'system.adapter.\u9999',
744
- );
745
- return Object.values(instances).filter(instance => !!instance?.common?.webExtendable);
746
- } catch {
747
- return [];
748
- }
749
- }
750
-
751
- /**
752
- * Gets the IP addresses of the given host.
753
- */
754
- async getIpAddresses(host: string): Promise<{ name: string; address: string; family: 'ipv4' | 'ipv6' }[]> {
755
- const ips = await this.socket.getHostByIp(host || this.common?.host || '');
756
- // translate names
757
- const ip4 = ips.find(ip => ip.address === '0.0.0.0');
758
- if (ip4) {
759
- ip4.name = `[IPv4] 0.0.0.0 - ${I18n.t('ra_Listen on all IPs')}`;
760
- }
761
- const ip6 = ips.find(ip => ip.address === '::');
762
- if (ip6) {
763
- ip6.name = `[IPv4] :: - ${I18n.t('ra_Listen on all IPs')}`;
764
- }
765
- return ips;
766
- }
767
-
768
- /**
769
- * Saves the settings to the server.
770
- *
771
- * @param isClose True if the user is closing the dialog.
772
- */
773
- onSave(isClose?: boolean): void {
774
- let oldObj: ioBroker.InstanceObject;
775
- if (this.state.isConfigurationError) {
776
- this.setState({ errorText: this.state.isConfigurationError });
777
- return;
778
- }
779
-
780
- this.socket
781
- .getObject(this.instanceId)
782
- .then(_oldObj => {
783
- oldObj = (_oldObj || {}) as ioBroker.InstanceObject;
784
-
785
- for (const a in this.state.native) {
786
- if (Object.prototype.hasOwnProperty.call(this.state.native, a)) {
787
- if (this.state.native[a] === null) {
788
- oldObj.native[a] = null;
789
- } else if (this.state.native[a] !== undefined) {
790
- oldObj.native[a] = JSON.parse(JSON.stringify(this.state.native[a]));
791
- } else {
792
- delete oldObj.native[a];
793
- }
794
- }
795
- }
796
-
797
- if (this.state.common) {
798
- for (const b in this.state.common) {
799
- if (this.state.common[b] === null) {
800
- (oldObj as Record<string, any>).common[b] = null;
801
- } else if (this.state.common[b] !== undefined) {
802
- (oldObj as Record<string, any>).common[b] = JSON.parse(
803
- JSON.stringify(this.state.common[b]),
804
- );
805
- } else {
806
- delete (oldObj as Record<string, any>).common[b];
807
- }
808
- }
809
- }
810
-
811
- if (this.onPrepareSave(oldObj.native) !== false) {
812
- return this.socket.setObject(this.instanceId, oldObj);
813
- }
814
-
815
- return Promise.reject(new Error('Invalid configuration'));
816
- })
817
- .then(() => {
818
- this.savedNative = oldObj.native;
819
- globalThis.changed = false;
820
- try {
821
- window.parent.postMessage('nochange', '*');
822
- } catch {
823
- // ignore
824
- }
825
-
826
- this.setState({ changed: false });
827
- isClose && GenericApp.onClose();
828
- })
829
- .catch(e => console.error(`Cannot save configuration: ${e}`));
830
- }
831
-
832
- /**
833
- * Renders the toast.
834
- */
835
- renderToast(): JSX.Element | null {
836
- if (!this.state.toast) {
837
- return null;
838
- }
839
-
840
- return (
841
- <Snackbar
842
- anchorOrigin={{
843
- vertical: 'bottom',
844
- horizontal: 'left',
845
- }}
846
- open={!0}
847
- autoHideDuration={6000}
848
- onClose={() => this.setState({ toast: '' })}
849
- ContentProps={{ 'aria-describedby': 'message-id' }}
850
- message={<span id="message-id">{this.state.toast}</span>}
851
- action={[
852
- <IconButton
853
- key="close"
854
- aria-label="Close"
855
- color="inherit"
856
- className={this.props.classes?.close}
857
- onClick={() => this.setState({ toast: '' })}
858
- size="large"
859
- >
860
- <IconClose />
861
- </IconButton>,
862
- ]}
863
- />
864
- );
865
- }
866
-
867
- /**
868
- * Closes the dialog.
869
- */
870
- static onClose(): void {
871
- if (typeof window.parent !== 'undefined' && window.parent) {
872
- try {
873
- if (window.parent.$iframeDialog && typeof window.parent.$iframeDialog.close === 'function') {
874
- window.parent.$iframeDialog.close();
875
- } else {
876
- window.parent.postMessage('close', '*');
877
- }
878
- } catch {
879
- window.parent.postMessage('close', '*');
880
- }
881
- }
882
- }
883
-
884
- /**
885
- * Renders the error dialog.
886
- */
887
- renderError(): React.JSX.Element | null {
888
- if (!this.state.errorText) {
889
- return null;
890
- }
891
-
892
- return (
893
- <DialogError
894
- text={this.state.errorText}
895
- onClose={() => this.setState({ errorText: '' })}
896
- />
897
- );
898
- }
899
-
900
- /**
901
- * Checks if the configuration has changed.
902
- *
903
- * @param native the new state
904
- */
905
- getIsChanged(native: Record<string, any>): boolean {
906
- native = native || this.state.native;
907
- const isChanged = JSON.stringify(native) !== JSON.stringify(this.savedNative);
908
-
909
- globalThis.changed = isChanged;
910
-
911
- return isChanged;
912
- }
913
-
914
- /**
915
- * Gets called when loading the configuration.
916
- *
917
- * @param newNative The new configuration object.
918
- */
919
- onLoadConfig(newNative: Record<string, any>): void {
920
- if (JSON.stringify(newNative) !== JSON.stringify(this.state.native)) {
921
- this.setState({ native: newNative, changed: this.getIsChanged(newNative) });
922
- }
923
- }
924
-
925
- /**
926
- * Sets the configuration error.
927
- */
928
- setConfigurationError(errorText: string): void {
929
- if (this.state.isConfigurationError !== errorText) {
930
- this.setState({ isConfigurationError: errorText });
931
- }
932
- }
933
-
934
- /**
935
- * Renders the save and close buttons.
936
- */
937
- renderSaveCloseButtons(): React.JSX.Element | null {
938
- if (!this.state.confirmClose && !this.state.bottomButtons) {
939
- return null;
940
- }
941
-
942
- return (
943
- <>
944
- {this.state.bottomButtons ? (
945
- <SaveCloseButtons
946
- theme={this.state.theme}
947
- newReact={this.newReact}
948
- noTextOnButtons={
949
- this.state.width === 'xs' || this.state.width === 'sm' || this.state.width === 'md'
950
- }
951
- changed={this.state.changed}
952
- onSave={isClose => this.onSave(isClose)}
953
- onClose={() => {
954
- if (this.state.changed) {
955
- this.setState({ confirmClose: true });
956
- } else {
957
- GenericApp.onClose();
958
- }
959
- }}
960
- />
961
- ) : null}
962
- {this.state.confirmClose ? (
963
- <ConfirmDialog
964
- title={I18n.t('ra_Please confirm')}
965
- text={I18n.t('ra_Some data are not stored. Discard?')}
966
- ok={I18n.t('ra_Discard')}
967
- cancel={I18n.t('ra_Cancel')}
968
- onClose={isYes => this.setState({ confirmClose: false }, () => isYes && GenericApp.onClose())}
969
- />
970
- ) : null}
971
- </>
972
- );
973
- }
974
-
975
- private _updateNativeValue(obj: Record<string, any>, attrs: string | string[], value: any): boolean {
976
- if (typeof attrs !== 'object') {
977
- attrs = attrs.split('.');
978
- }
979
- const attr: string = attrs.shift() || '';
980
- if (!attrs.length) {
981
- if (value && typeof value === 'object') {
982
- if (JSON.stringify(obj[attr]) !== JSON.stringify(value)) {
983
- obj[attr] = value;
984
- return true;
985
- }
986
- return false;
987
- }
988
- if (obj[attr] !== value) {
989
- obj[attr] = value;
990
- return true;
991
- }
992
-
993
- return false;
994
- }
995
-
996
- obj[attr] = obj[attr] || {};
997
- if (typeof obj[attr] !== 'object') {
998
- throw new Error(`attribute ${attr} is no object, but ${typeof obj[attr]}`);
999
- }
1000
- return this._updateNativeValue(obj[attr], attrs, value);
1001
- }
1002
-
1003
- /**
1004
- * Update the native value
1005
- *
1006
- * @param attr The attribute name with dots as delimiter.
1007
- * @param value The new value.
1008
- * @param cb Callback which will be called upon completion.
1009
- */
1010
- updateNativeValue(attr: string, value: any, cb?: () => void): void {
1011
- const native = JSON.parse(JSON.stringify(this.state.native));
1012
- if (this._updateNativeValue(native, attr, value)) {
1013
- const changed = this.getIsChanged(native);
1014
-
1015
- if (changed !== this.state.changed) {
1016
- try {
1017
- window.parent.postMessage(changed ? 'change' : 'nochange', '*');
1018
- } catch {
1019
- // ignore
1020
- }
1021
- }
1022
-
1023
- this.setState({ native, changed }, cb);
1024
- }
1025
- }
1026
-
1027
- /**
1028
- * Set the error text to be shown.
1029
- */
1030
- showError(text: string | React.JSX.Element): void {
1031
- this.setState({ errorText: text });
1032
- }
1033
-
1034
- /**
1035
- * Sets the toast to be shown.
1036
- *
1037
- * @param toast Text to be shown.
1038
- */
1039
- showToast(toast: string | React.JSX.Element): void {
1040
- this.setState({ toast });
1041
- }
1042
-
1043
- /**
1044
- * Renders helper dialogs
1045
- */
1046
- renderHelperDialogs(): React.JSX.Element {
1047
- return (
1048
- <>
1049
- {this.renderError()}
1050
- {this.renderToast()}
1051
- {this.renderSaveCloseButtons()}
1052
- {this.renderAlertSnackbar()}
1053
- </>
1054
- );
1055
- }
1056
-
1057
- /**
1058
- * Renders this component.
1059
- */
1060
- render(): React.JSX.Element {
1061
- if (!this.state.loaded) {
1062
- return <Loader themeType={this.state.themeType} />;
1063
- }
1064
-
1065
- return (
1066
- <div className="App">
1067
- {this.renderError()}
1068
- {this.renderToast()}
1069
- {this.renderSaveCloseButtons()}
1070
- {this.renderAlertSnackbar()}
1071
- </div>
1072
- );
1073
- }
1074
- }
1075
-
1076
- export default GenericApp;