@ledvance/ui-biz-bundle 1.1.68 → 1.1.70

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.
@@ -1,642 +1,639 @@
1
- import React, { useCallback, useEffect, useMemo } from 'react'
2
- import { FlatList, Image, Linking, ScrollView, Switch, Text, TouchableOpacity, View } from 'react-native'
3
- import { useNavigation } from '@react-navigation/native'
4
- import { useDebounceFn, useReactive, useUpdateEffect } from 'ahooks'
5
- import {
6
- BiorhythmBean,
7
- BiorhythmGradientType,
8
- colorTemperatureValue,
9
- getDefBiorhythmUIState,
10
- Plan,
11
- } from './BiorhythmBean'
12
- import { Modal, Utils } from 'tuya-panel-kit'
13
- import { cloneDeep, sortBy } from 'lodash'
14
- import iconList from './iconListData'
15
- import pIdList from './pIdList'
16
- import {
17
- useDeviceId,
18
- useDeviceInfo,
19
- useSystemTimeFormate,
20
- } from '@ledvance/base/src/models/modules/NativePropsSlice'
21
- import I18n from '@ledvance/base/src/i18n'
22
- import res from '@ledvance/base/src/res'
23
- import { ui_biz_routerKey } from "../../navigation/Routers";
24
- import BiologicalRes from './res/BiologicalRes'
25
- import { cctToColor } from '@ledvance/base/src/utils/cctUtils'
26
- import { setDataSource } from '@ledvance/base/src/components/weekSelect'
27
- import { BiorhythmEditPageParams } from './BiorhythmEditPage'
28
- import { useBiorhythm, userOperation } from './BiorhythmActions'
29
- import { convertMinutesTo12HourFormat, showDialog as showCommonDialog } from '@ledvance/base/src/utils/common'
30
- import TimeCircular from './circular/TimeCircular'
31
- import Page from '@ledvance/base/src/components/Page'
32
- import Card from '@ledvance/base/src/components/Card'
33
- import Spacer from '@ledvance/base/src/components/Spacer'
34
- import DeleteButton from '@ledvance/base/src/components/DeleteButton'
35
- import { useParams } from '@ledvance/base/src/hooks/Hooks'
36
-
37
- const cx = Utils.RatioUtils.convertX
38
- const width = Utils.RatioUtils.width
39
-
40
- interface UIState extends BiorhythmBean {
41
- showGradientTypeSelectModal: boolean;
42
- flag: symbol;
43
- weekString: string,
44
- timeSchedule: any[]
45
- loading: boolean
46
- }
47
-
48
- export interface BiorhythmPageParams {
49
- biorhythmDpCode: string
50
- conflictDps: {
51
- randomTimeDpCode?: string
52
- fixedTimeDpCode?: string
53
- sleepDpCode?: string
54
- wakeUpDpCode?: string
55
- }
56
- isSupportTemperature: boolean
57
- isSupportBrightness: boolean
58
- isMixLight?: boolean
59
- }
60
-
61
- const BiorhythmPage = () => {
62
- const params = useParams<BiorhythmPageParams>()
63
- const navigation = useNavigation()
64
- const [biorhythm, setBiorhythm] = useBiorhythm(params.biorhythmDpCode)
65
- const deviceId = useDeviceId()
66
- const deviceInfo = useDeviceInfo()
67
- const { productId } = deviceInfo
68
- const is24Hour = useSystemTimeFormate()
69
- const devicesJudge = pIdList.some(val => val === productId)
70
- // const [sleepList, setSleepList] = useSleepPlan({ sleep_mode: params.sleepPlanDp })
71
- // const [wakeUpList, setWakeUpList] = useWakeUpPlan({ wakeup_mode: params.wakeUpPlanDp })
72
- // const sleepWakeUpStatus = [...sleepList, ...wakeUpList].some(ele => ele.enable)
73
- const state = useReactive<UIState>({
74
- ...biorhythm,
75
- showGradientTypeSelectModal: false,
76
- flag: Symbol(),
77
- weekString: '',
78
- timeSchedule: [],
79
- loading: false
80
- })
81
-
82
- const showGradientTypeSelectModal = useCallback((show: boolean) => {
83
- state.showGradientTypeSelectModal = show
84
- }, [])
85
-
86
- const onPlanEdited = useCallback((isAdd: boolean, newPlan: Plan) => {
87
- if (isAdd) {
88
- state.planList.push(newPlan)
89
- state.planList = sortBy(state.planList, p => p.time)
90
- } else {
91
- state.planList = state.planList.map(plan => (plan.index === newPlan.index ? newPlan : plan))
92
- }
93
- state.flag = Symbol()
94
- }, [])
95
-
96
- const onPlanDelete = useCallback((id: number) => {
97
- state.planList = state.planList.filter(plan => plan.index !== id)
98
- state.flag = Symbol()
99
- }, [])
100
-
101
- const requestSetBiorhythm = useCallback(async (pushFeature: boolean = true) => {
102
- state.loading = true
103
- const planList = state.planList?.map(item => { return { ...item, icon: `${item.icon}` } })
104
- .sort((a, b) => a.time - b.time);
105
- await userOperation(deviceId, false)
106
- const res = await setBiorhythm(cloneDeep({ ...state, planList }), pushFeature)
107
- state.loading = false
108
- if (res.success) {
109
- console.log('设置生物节律 OK')
110
- }
111
- }, [])
112
-
113
- useUpdateEffect(() => {
114
- if (state.planList?.length === 0) {
115
- const data = getDefBiorhythmUIState()
116
- data.planList = data.planList?.map(item => {
117
- return { ...item, icon: `${item.icon}` }
118
- })
119
- setBiorhythm(cloneDeep(data)).then()
120
- }
121
- }, [state.planList])
122
-
123
- const setTimer = value => {
124
- switch (value) {
125
- case 'Sunrise':
126
- return I18n.getLang('bio_ryhthm_default_field_text')
127
- case 'Wake Up':
128
- return I18n.getLang('bio_ryhthm_default_field_text2')
129
- case 'Sunlight':
130
- return I18n.getLang('bio_ryhthm_default_field_text3')
131
- case 'Comfortable':
132
- return I18n.getLang('bio_ryhthm_default_field_text4')
133
- case 'Night light':
134
- return I18n.getLang('bio_ryhthm_default_field_text5')
135
- default:
136
- return value
137
- }
138
- }
139
-
140
- const minimumEnable = useCallback((plan: Plan) => {
141
- let enable = false
142
- state.planList.filter(p => p.index !== plan.index).forEach(item => {
143
- const diff = Math.abs(plan.time - item.time)
144
- if (diff < 15) enable = true
145
- })
146
- return enable
147
- }, [state.planList])
148
-
149
- const nameRepeat = useCallback((plan: Plan) => {
150
- return !!state.planList.filter(p => p.index !== plan.index).find(p => p.name === plan.name)
151
- }, [state.planList])
152
-
153
- const showDeleteBtn = useMemo(() => {
154
- return state.planList?.length > 1
155
- }, [state.planList?.length])
156
-
157
- const { run } = useDebounceFn(requestSetBiorhythm, { wait: 300 })
158
-
159
- useUpdateEffect(() => {
160
- run()
161
- }, [state.flag])
162
-
163
- const replaceImg = (img) => {
164
- const item = iconList?.find(val => val.id === Number(img))
165
- switch (img) {
166
- case 'rhythm_icon1':
167
- case '31':
168
- return { icon: BiologicalRes.biorhythom_Icon1, iconId: 1 }
169
- case 'rhythm_icon2':
170
- case '33':
171
- return { icon: BiologicalRes.biorhythom_Icon5, iconId: 5 }
172
- case 'rhythm_icon3':
173
- case '35':
174
- return { icon: BiologicalRes.biorhythom_Icon2, iconId: 2 }
175
- case 'rhythm_icon4':
176
- case '32':
177
- return { icon: BiologicalRes.biorhythom_Icon9, iconId: 9 }
178
- case 'rhythm_icon12':
179
- case '39':
180
- return { icon: BiologicalRes.biorhythom_Icon3, iconId: 3 }
181
- default:
182
- return { icon: item?.icon, iconId: item?.id }
183
- }
184
- }
185
-
186
- useEffect(() =>{
187
- const weeks: string[] = setDataSource(
188
- biorhythm.repeatPeriod.map(item => {
189
- return item?.enabled ? 1 : 0
190
- })).filter(item => item.enabled)
191
- .map(item => item.title)
192
-
193
- if (weeks.length > 0) {
194
- if (weeks.length === 7) {
195
- state.weekString = I18n.getLang('motion_detection_time_schedule_notifications_field_weekdays_text4')
196
- } else {
197
- state.weekString = I18n.formatValue('timeschedule_add_schedule_text', weeks.join(', '))
198
- }
199
- } else {
200
- state.weekString = I18n.getLang('motion_detection_time_schedule_notifications_field_weekdays_text2')
201
- }
202
- }, [JSON.stringify(biorhythm.repeatPeriod)])
203
-
204
- useUpdateEffect(() => {
205
- console.log('Redux 生物节律数据更新', biorhythm)
206
- const planList = biorhythm.planList?.map(item => {
207
- return {
208
- ...item,
209
- icon: replaceImg(item?.iconId || item?.icon)?.icon,
210
- iconId: replaceImg(item?.iconId || item?.icon)?.iconId,
211
- }
212
- })
213
- state.enable = biorhythm.enable
214
- state.gradient = biorhythm.gradient
215
- state.repeatPeriod = biorhythm.repeatPeriod
216
- state.planList = planList
217
-
218
- }, [JSON.stringify(biorhythm)])
219
-
220
-
221
- const setImg = (id) => {
222
- const imgIcon = iconList?.find(val => val?.id === Number(id))?.icon
223
- return imgIcon || ''
224
- }
225
-
226
- const openLink = () => {
227
- const url = I18n.getLang('biorhythm_product_link') // 需要打开的链接
228
- Linking.openURL(url).catch((error) => console.error('无法打开链接:', error))
229
- }
230
-
231
- const sunHomeText = string => {
232
- const text = string.split('SUN@HOME')
233
- return text?.length === 1 && <Text style={{ fontSize: cx(14) }}>{text[0]}</Text> ||
234
- <Text style={{
235
- fontSize: cx(14),
236
- flexDirection: 'row',
237
- }}>
238
- <Text>{text[0]}</Text>
239
- <Text onPress={openLink}
240
- style={{
241
- fontFamily: 'helvetica_neue_lt_std_roman',
242
- color: '#ff6600',
243
- textDecorationLine: 'underline',
244
- flexWrap: 'wrap',
245
- }}>SUN@HOME</Text>
246
- <Text>{text[1]}</Text>
247
- </Text>
248
- }
249
-
250
- const randomIcon = () => {
251
- const iconIdList = state.planList?.map(item => {
252
- return item.iconId
253
- })
254
- const allIcon = iconList?.map(item => {
255
- return item.id
256
- })
257
- const availableChart = allIcon.filter((element) => !iconIdList.includes(element))
258
- const randomIndex = Math.floor(Math.random() * availableChart.length)
259
- return availableChart[randomIndex]
260
- }
261
-
262
- return (
263
- <>
264
- <Page
265
- backText={deviceInfo.name}
266
- onBackClick={navigation.goBack}
267
- headlineText={I18n.getLang('add_new_trigger_time_system_back_text')}
268
- headlineContent={<Switch
269
- value={state.enable}
270
- thumbColor={'#f60'}
271
- trackColor={{ false: '#00000026', true: '#ff660036' }}
272
- onValueChange={async enable => {
273
- state.loading = true
274
- state.enable = enable
275
- requestSetBiorhythm(false)
276
- }}
277
- />}
278
- loading={state.loading}
279
- >
280
- <ScrollView nestedScrollEnabled={true} style={{ position: 'relative' }}>
281
- <View style={{ marginHorizontal: cx(24) }}>
282
- {sunHomeText(I18n.getLang(devicesJudge ? 'bio_ryhthm_default_description_text' : 'bio_ryhthm_non_sun_home_products_description_text'))}
283
- </View>
284
- <View style={{ height: cx(10) }} />
285
- <View
286
- style={{
287
- flexDirection: 'row',
288
- justifyContent: 'space-between',
289
- marginHorizontal: cx(24),
290
- }}
291
- >
292
- {state.repeatPeriod.map(period => {
293
- return (
294
- <TouchableOpacity
295
- key={period.title}
296
- onPress={() => {
297
- const periodNum = state.repeatPeriod.filter(p => p.enabled)?.length
298
- if (periodNum === 1 && period.enabled) return
299
- period.enabled = !period.enabled
300
- requestSetBiorhythm(false)
301
- }}
302
- >
303
- <View
304
- style={{
305
- width: cx(40),
306
- height: cx(40),
307
- justifyContent: 'center',
308
- alignItems: 'center',
309
- borderRadius: cx(20),
310
- backgroundColor: period.enabled ? '#ffe0d4' : '#fff',
311
- borderWidth: cx(1),
312
- borderColor: '#f60',
313
- }}
314
- >
315
- <Text
316
- style={{
317
- color: '#FF6600',
318
- textAlign: 'center',
319
- }}
320
- >
321
- {period.title}
322
- </Text>
323
- </View>
324
- </TouchableOpacity>
325
- )
326
- })}
327
- </View>
328
- <View style={{ marginHorizontal: cx(24), marginTop: cx(20) }}>
329
- <Text>{state.weekString}</Text>
330
- </View>
331
- <View style={{ marginHorizontal: cx(24), marginTop: cx(16) }}>
332
- <Text>
333
- {I18n.getLang('bio_ryhthm_default_selectionfield_topic_text')}
334
- </Text>
335
- <TouchableOpacity
336
- onPress={() => {
337
- showGradientTypeSelectModal(true)
338
- }}
339
- >
340
- <View
341
- style={{
342
- flexDirection: 'row',
343
- borderRadius: cx(4),
344
- backgroundColor: '#f6f6f6',
345
- alignItems: 'center',
346
- flex: 1,
347
- height: cx(44),
348
- borderBottomWidth: cx(1),
349
- borderBottomColor: '#cbcbcb',
350
- }}
351
- >
352
- <Text style={{
353
- fontSize: cx(16),
354
- color: '#000',
355
- fontFamily: 'helvetica_neue_lt_std_roman',
356
- paddingLeft: cx(16),
357
- }}>
358
- {
359
- I18n.getLang(
360
- state.gradient === BiorhythmGradientType.DirectGradient
361
- ? 'add_new_dynamic_mood_color_changing_mode_value2'
362
- : 'add_new_dynamic_mood_color_changing_mode_value',
363
- )
364
- }
365
-
366
- </Text>
367
- </View>
368
- </TouchableOpacity>
369
- </View>
370
- <View style={{ height: cx(20) }} />
371
- <TimeCircular
372
- planEdit={true}
373
- planList={state.planList}
374
- onPanMoved={(id, time) => {
375
- state.planList = state.planList.map(plan => {
376
- return {
377
- ...plan,
378
- time: plan.index === id ? time : plan.time,
379
- }
380
- })
381
- state.flag = Symbol()
382
- }}
383
- replaceStatus={devicesJudge}
384
- gradient={state.gradient === BiorhythmGradientType.DirectGradient}
385
- isSupportTemperature={!params.isSupportTemperature} />
386
- <View
387
- style={{
388
- flexDirection: 'row',
389
- justifyContent: 'space-between',
390
- marginHorizontal: cx(24),
391
- marginBottom: cx(-8),
392
- marginTop: cx(26),
393
- }}>
394
- {state.planList.length === 8 && <View
395
- style={{ marginVertical: cx(10), flexDirection: 'row', alignItems: 'center', width: width - cx(48) }}>
396
- <Image style={{ width: cx(16), height: cx(16), tintColor: '#ff9500' }} source={res.ic_warning_amber} />
397
- <Text
398
- style={{
399
- flexWrap: 'wrap',
400
- fontSize: cx(12),
401
- }}>{I18n.getLang('add_new_trigger_time_warning_max_number_text')}</Text>
402
- </View>}
403
- {state.planList.length < 8 &&
404
- <>
405
- <Text
406
- style={{
407
- fontSize: cx(16),
408
- fontWeight: 'bold',
409
- color: 'rgb(0,0,0)',
410
- }}>{I18n.getLang('bio_ryhthm_default_subheadline_text')}</Text>
411
- <TouchableOpacity
412
- onPress={() => {
413
- const ids: number[] = state.planList.map(p => p.index)
414
- const newPlan: Plan = {
415
- index: Math.max(...ids) + 1,
416
- icon: res.rhythm_icon1,
417
- time: 0,
418
- name: '',
419
- colorTemperature: 0,
420
- brightness: 100,
421
- action: [
422
- {
423
- uri: 'model/attribute/set/LightCtrl/ColorTemperature',
424
- startValue: `${colorTemperatureValue(0)}`,
425
- },
426
- {
427
- uri: 'model/attribute/set/LightCtrl/Brightness',
428
- startValue: '100',
429
- },
430
- ],
431
- enable: true,
432
- iconId: randomIcon(),
433
- }
434
- const editPageParams: BiorhythmEditPageParams = {
435
- planData: newPlan,
436
- isAdd: true,
437
- onPlanEdited,
438
- onPlanDelete,
439
- minimumEnable,
440
- nameRepeat,
441
- iconIdList: state.planList?.map(item => {
442
- return item.iconId
443
- }),
444
- isMixRGBWLamp: !!params.isMixLight,
445
- isSupportTemperature: params.isSupportTemperature,
446
- isSupportBrightness: params.isSupportBrightness,
447
- showDeleteBtn
448
- }
449
- navigation.navigate(ui_biz_routerKey.bi_biz_biological_edit, editPageParams)
450
- }}>
451
- <Image source={BiologicalRes.biorhythom_add} style={{ height: cx(24), width: cx(24) }} />
452
- </TouchableOpacity>
453
- </>
454
- }
455
- </View>
456
- <FlatList
457
- data={state.planList}
458
- style={{
459
- flex: 1,
460
- marginTop: cx(12),
461
- }}
462
- nestedScrollEnabled={true}
463
- ListHeaderComponent={() => (<Spacer height={cx(16)} />)}
464
- ItemSeparatorComponent={() => (<Spacer height={cx(16)} />)}
465
- renderItem={({ item }) => {
466
- const type = typeof item?.icon === 'string'
467
- const bgColor = item?.brightness === 0 ? '#000' : !params.isSupportTemperature && cctToColor(1) || cctToColor(item.colorTemperature.toFixed(), item?.brightness)
468
- return (
469
- <Card
470
- style={{ marginHorizontal: cx(24) }}
471
- onPress={() => {
472
- const editPageParams: BiorhythmEditPageParams = {
473
- planData: {
474
- ...item,
475
- name: setTimer(item?.name)
476
- },
477
- isAdd: false,
478
- onPlanEdited,
479
- onPlanDelete,
480
- minimumEnable,
481
- nameRepeat,
482
- iconIdList: state.planList?.map(item => {
483
- return item.iconId
484
- }),
485
- isMixRGBWLamp: !!params.isMixLight,
486
- isSupportTemperature: params.isSupportTemperature,
487
- isSupportBrightness: params.isSupportBrightness,
488
- showDeleteBtn
489
- }
490
- navigation.navigate(ui_biz_routerKey.bi_biz_biological_edit, editPageParams)
491
- }}
492
- >
493
- <View
494
- style={{
495
- flex: 1,
496
- flexDirection: 'column',
497
- }}
498
- >
499
- <View style={{ flex: 1, flexDirection: 'row', justifyContent: 'space-between', marginTop: cx(16) }}>
500
- <View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
501
- <Image
502
- source={setImg(item?.iconId) || type && { uri: item?.icon } || item?.icon}
503
- style={{
504
- width: cx(24),
505
- height: cx(24),
506
- marginStart: cx(10),
507
- marginRight: cx(6),
508
- tintColor: '#474e5d',
509
- }}
510
- />
511
- <Text
512
- style={{
513
- fontSize: cx(16),
514
- color: '#000',
515
- fontFamily: 'helvetica_neue_lt_std_roman',
516
- }}
517
- >
518
- {convertMinutesTo12HourFormat(item.time, is24Hour)}
519
- </Text>
520
- </View>
521
- <Switch
522
- value={item.enable}
523
- thumbColor={'#f60'}
524
- trackColor={{ false: '#00000026', true: '#ff660036' }}
525
- onValueChange={e => {
526
- item.enable = e
527
- requestSetBiorhythm()
528
- }}
529
- style={{ marginRight: cx(10) }}
530
- />
531
- </View>
532
- <View style={{ flex: 1, flexDirection: 'row', justifyContent: 'space-between' }}>
533
- <Text
534
- style={{
535
- fontSize: cx(12),
536
- color: '#666',
537
- fontFamily: 'helvetica_neue_lt_std_roman',
538
- paddingLeft: cx(20),
539
- }}
540
- >
541
- {setTimer(item?.name)}
542
- </Text>
543
- </View>
544
- <View
545
- style={{
546
- width: cx(295),
547
- height: cx(24),
548
- backgroundColor: bgColor,
549
- marginLeft: cx(20),
550
- marginTop: cx(5),
551
- marginBottom: cx(24),
552
- borderRadius: cx(8),
553
- }} />
554
- </View>
555
- </Card>
556
- )
557
- }}
558
- keyExtractor={item => `${item.index}`}
559
- ListFooterComponent={() => {
560
- return (
561
- <View style={{ marginHorizontal: cx(24) }}>
562
- <Spacer />
563
- <DeleteButton
564
- text={I18n.getLang('bio_ryhthm_default_button_reset_text')}
565
- textStyle={{
566
- fontSize: cx(16),
567
- color: '#fff',
568
- fontFamily: 'helvetica_neue_lt_std_roman',
569
- }}
570
- onPress={() => {
571
- showCommonDialog({
572
- method: 'confirm',
573
- title: I18n.getLang('bio_ryhthm_reset_description_text'),
574
- onConfirm: (_, { close }) => {
575
- const defBiorhythmUIState = getDefBiorhythmUIState()
576
- state.enable = defBiorhythmUIState.enable
577
- state.gradient = defBiorhythmUIState.gradient
578
- state.repeatPeriod = defBiorhythmUIState.repeatPeriod
579
- state.planList = defBiorhythmUIState.planList
580
- requestSetBiorhythm()
581
- close()
582
- }
583
- })
584
- }}
585
- />
586
- <Spacer />
587
- </View>
588
- )
589
- }}
590
- />
591
- {!state.enable && (
592
- <View
593
- style={{
594
- backgroundColor: 'rgba(0,0,0,.5)',
595
- width: '100%',
596
- height: '100%',
597
- position: 'absolute',
598
- top: 0,
599
- zIndex: 999,
600
- }}
601
- />
602
- )}
603
- </ScrollView>
604
- </Page>
605
- <Modal.List
606
- type={'radio'}
607
- visible={state.showGradientTypeSelectModal}
608
- value={state.gradient}
609
- dataSource={[
610
- {
611
- title: I18n.getLang('add_new_dynamic_mood_color_changing_mode_value'),
612
- key: BiorhythmGradientType.EntireGradient.toString(),
613
- value: BiorhythmGradientType.EntireGradient,
614
- },
615
- {
616
- title: I18n.getLang('add_new_dynamic_mood_color_changing_mode_value2'),
617
- key: BiorhythmGradientType.DirectGradient.toString(),
618
- value: BiorhythmGradientType.DirectGradient,
619
- },
620
- ]}
621
- title={I18n.getLang('add_new_dynamic_mood_color_changing_mode_headline')}
622
- onMaskPress={() => {
623
- showGradientTypeSelectModal(false)
624
- }}
625
- onCancel={() => {
626
- showGradientTypeSelectModal(false)
627
- }}
628
- cancelText={I18n.getLang('auto_scan_system_cancel')}
629
- confirmText={I18n.getLang('auto_scan_system_wifi_confirm')}
630
- onConfirm={(item: BiorhythmGradientType) => {
631
- state.gradient = item
632
- requestSetBiorhythm()
633
- showGradientTypeSelectModal(false)
634
- }}
635
- />
636
- </>
637
- )
638
- }
639
-
640
- export default BiorhythmPage
641
-
642
-
1
+ import React, { useCallback, useEffect, useMemo } from 'react'
2
+ import { FlatList, Image, Linking, ScrollView, Switch, Text, TouchableOpacity, View } from 'react-native'
3
+ import { useNavigation } from '@react-navigation/native'
4
+ import { useDebounceFn, useReactive, useUpdateEffect } from 'ahooks'
5
+ import {
6
+ BiorhythmBean,
7
+ BiorhythmGradientType,
8
+ colorTemperatureValue,
9
+ getDefBiorhythmUIState,
10
+ Plan,
11
+ } from './BiorhythmBean'
12
+ import { Modal, Utils } from 'tuya-panel-kit'
13
+ import { cloneDeep, sortBy } from 'lodash'
14
+ import iconList from './iconListData'
15
+ import pIdList from './pIdList'
16
+ import {
17
+ useDeviceInfo,
18
+ useSystemTimeFormate,
19
+ } from '@ledvance/base/src/models/modules/NativePropsSlice'
20
+ import I18n from '@ledvance/base/src/i18n'
21
+ import res from '@ledvance/base/src/res'
22
+ import { ui_biz_routerKey } from "../../navigation/Routers";
23
+ import BiologicalRes from './res/BiologicalRes'
24
+ import { cctToColor } from '@ledvance/base/src/utils/cctUtils'
25
+ import { setDataSource } from '@ledvance/base/src/components/weekSelect'
26
+ import { BiorhythmEditPageParams } from './BiorhythmEditPage'
27
+ import { useBiorhythm } from './BiorhythmActions'
28
+ import { convertMinutesTo12HourFormat, showDialog as showCommonDialog } from '@ledvance/base/src/utils/common'
29
+ import TimeCircular from './circular/TimeCircular'
30
+ import Page from '@ledvance/base/src/components/Page'
31
+ import Card from '@ledvance/base/src/components/Card'
32
+ import Spacer from '@ledvance/base/src/components/Spacer'
33
+ import DeleteButton from '@ledvance/base/src/components/DeleteButton'
34
+ import { useParams } from '@ledvance/base/src/hooks/Hooks'
35
+
36
+ const cx = Utils.RatioUtils.convertX
37
+ const width = Utils.RatioUtils.width
38
+
39
+ interface UIState extends BiorhythmBean {
40
+ showGradientTypeSelectModal: boolean;
41
+ flag: symbol;
42
+ weekString: string,
43
+ timeSchedule: any[]
44
+ loading: boolean
45
+ }
46
+
47
+ export interface BiorhythmPageParams {
48
+ biorhythmDpCode: string
49
+ conflictDps: {
50
+ randomTimeDpCode?: string
51
+ fixedTimeDpCode?: string
52
+ sleepDpCode?: string
53
+ wakeUpDpCode?: string
54
+ }
55
+ isSupportTemperature: boolean
56
+ isSupportBrightness: boolean
57
+ isMixLight?: boolean
58
+ }
59
+
60
+ const BiorhythmPage = () => {
61
+ const params = useParams<BiorhythmPageParams>()
62
+ const navigation = useNavigation()
63
+ const [biorhythm, setBiorhythm] = useBiorhythm(params.biorhythmDpCode)
64
+ const deviceInfo = useDeviceInfo()
65
+ const { productId } = deviceInfo
66
+ const is24Hour = useSystemTimeFormate()
67
+ const devicesJudge = pIdList.some(val => val === productId)
68
+ // const [sleepList, setSleepList] = useSleepPlan({ sleep_mode: params.sleepPlanDp })
69
+ // const [wakeUpList, setWakeUpList] = useWakeUpPlan({ wakeup_mode: params.wakeUpPlanDp })
70
+ // const sleepWakeUpStatus = [...sleepList, ...wakeUpList].some(ele => ele.enable)
71
+ const state = useReactive<UIState>({
72
+ ...biorhythm,
73
+ showGradientTypeSelectModal: false,
74
+ flag: Symbol(),
75
+ weekString: '',
76
+ timeSchedule: [],
77
+ loading: false
78
+ })
79
+
80
+ const showGradientTypeSelectModal = useCallback((show: boolean) => {
81
+ state.showGradientTypeSelectModal = show
82
+ }, [])
83
+
84
+ const onPlanEdited = useCallback((isAdd: boolean, newPlan: Plan) => {
85
+ if (isAdd) {
86
+ state.planList.push(newPlan)
87
+ state.planList = sortBy(state.planList, p => p.time)
88
+ } else {
89
+ state.planList = state.planList.map(plan => (plan.index === newPlan.index ? newPlan : plan))
90
+ }
91
+ state.flag = Symbol()
92
+ }, [])
93
+
94
+ const onPlanDelete = useCallback((id: number) => {
95
+ state.planList = state.planList.filter(plan => plan.index !== id)
96
+ state.flag = Symbol()
97
+ }, [])
98
+
99
+ const requestSetBiorhythm = useCallback(async (pushFeature: boolean = true) => {
100
+ state.loading = true
101
+ const planList = state.planList?.map(item => { return { ...item, icon: `${item.icon}` } })
102
+ .sort((a, b) => a.time - b.time);
103
+ const res = await setBiorhythm(cloneDeep({ ...state, planList }), pushFeature)
104
+ state.loading = false
105
+ if (res.success) {
106
+ console.log('设置生物节律 OK')
107
+ }
108
+ }, [])
109
+
110
+ useUpdateEffect(() => {
111
+ if (state.planList?.length === 0) {
112
+ const data = getDefBiorhythmUIState()
113
+ data.planList = data.planList?.map(item => {
114
+ return { ...item, icon: `${item.icon}` }
115
+ })
116
+ setBiorhythm(cloneDeep(data)).then()
117
+ }
118
+ }, [state.planList])
119
+
120
+ const setTimer = value => {
121
+ switch (value) {
122
+ case 'Sunrise':
123
+ return I18n.getLang('bio_ryhthm_default_field_text')
124
+ case 'Wake Up':
125
+ return I18n.getLang('bio_ryhthm_default_field_text2')
126
+ case 'Sunlight':
127
+ return I18n.getLang('bio_ryhthm_default_field_text3')
128
+ case 'Comfortable':
129
+ return I18n.getLang('bio_ryhthm_default_field_text4')
130
+ case 'Night light':
131
+ return I18n.getLang('bio_ryhthm_default_field_text5')
132
+ default:
133
+ return value
134
+ }
135
+ }
136
+
137
+ const minimumEnable = useCallback((plan: Plan) => {
138
+ let enable = false
139
+ state.planList.filter(p => p.index !== plan.index).forEach(item => {
140
+ const diff = Math.abs(plan.time - item.time)
141
+ if (diff < 15) enable = true
142
+ })
143
+ return enable
144
+ }, [state.planList])
145
+
146
+ const nameRepeat = useCallback((plan: Plan) => {
147
+ return !!state.planList.filter(p => p.index !== plan.index).find(p => p.name === plan.name)
148
+ }, [state.planList])
149
+
150
+ const showDeleteBtn = useMemo(() => {
151
+ return state.planList?.length > 1
152
+ }, [state.planList?.length])
153
+
154
+ const { run } = useDebounceFn(requestSetBiorhythm, { wait: 300 })
155
+
156
+ useUpdateEffect(() => {
157
+ run()
158
+ }, [state.flag])
159
+
160
+ const replaceImg = (img) => {
161
+ const item = iconList?.find(val => val.id === Number(img))
162
+ switch (img) {
163
+ case 'rhythm_icon1':
164
+ case '31':
165
+ return { icon: BiologicalRes.biorhythom_Icon1, iconId: 1 }
166
+ case 'rhythm_icon2':
167
+ case '33':
168
+ return { icon: BiologicalRes.biorhythom_Icon5, iconId: 5 }
169
+ case 'rhythm_icon3':
170
+ case '35':
171
+ return { icon: BiologicalRes.biorhythom_Icon2, iconId: 2 }
172
+ case 'rhythm_icon4':
173
+ case '32':
174
+ return { icon: BiologicalRes.biorhythom_Icon9, iconId: 9 }
175
+ case 'rhythm_icon12':
176
+ case '39':
177
+ return { icon: BiologicalRes.biorhythom_Icon3, iconId: 3 }
178
+ default:
179
+ return { icon: item?.icon, iconId: item?.id }
180
+ }
181
+ }
182
+
183
+ useEffect(() =>{
184
+ const weeks: string[] = setDataSource(
185
+ biorhythm.repeatPeriod.map(item => {
186
+ return item?.enabled ? 1 : 0
187
+ })).filter(item => item.enabled)
188
+ .map(item => item.title)
189
+
190
+ if (weeks.length > 0) {
191
+ if (weeks.length === 7) {
192
+ state.weekString = I18n.getLang('motion_detection_time_schedule_notifications_field_weekdays_text4')
193
+ } else {
194
+ state.weekString = I18n.formatValue('timeschedule_add_schedule_text', weeks.join(', '))
195
+ }
196
+ } else {
197
+ state.weekString = I18n.getLang('motion_detection_time_schedule_notifications_field_weekdays_text2')
198
+ }
199
+ }, [JSON.stringify(biorhythm.repeatPeriod)])
200
+
201
+ useUpdateEffect(() => {
202
+ console.log('Redux 生物节律数据更新', biorhythm)
203
+ const planList = biorhythm.planList?.map(item => {
204
+ return {
205
+ ...item,
206
+ icon: replaceImg(item?.iconId || item?.icon)?.icon,
207
+ iconId: replaceImg(item?.iconId || item?.icon)?.iconId,
208
+ }
209
+ })
210
+ state.enable = biorhythm.enable
211
+ state.gradient = biorhythm.gradient
212
+ state.repeatPeriod = biorhythm.repeatPeriod
213
+ state.planList = planList
214
+
215
+ }, [JSON.stringify(biorhythm)])
216
+
217
+
218
+ const setImg = (id) => {
219
+ const imgIcon = iconList?.find(val => val?.id === Number(id))?.icon
220
+ return imgIcon || ''
221
+ }
222
+
223
+ const openLink = () => {
224
+ const url = I18n.getLang('biorhythm_product_link') // 需要打开的链接
225
+ Linking.openURL(url).catch((error) => console.error('无法打开链接:', error))
226
+ }
227
+
228
+ const sunHomeText = string => {
229
+ const text = string.split('SUN@HOME')
230
+ return text?.length === 1 && <Text style={{ fontSize: cx(14) }}>{text[0]}</Text> ||
231
+ <Text style={{
232
+ fontSize: cx(14),
233
+ flexDirection: 'row',
234
+ }}>
235
+ <Text>{text[0]}</Text>
236
+ <Text onPress={openLink}
237
+ style={{
238
+ fontFamily: 'helvetica_neue_lt_std_roman',
239
+ color: '#ff6600',
240
+ textDecorationLine: 'underline',
241
+ flexWrap: 'wrap',
242
+ }}>SUN@HOME</Text>
243
+ <Text>{text[1]}</Text>
244
+ </Text>
245
+ }
246
+
247
+ const randomIcon = () => {
248
+ const iconIdList = state.planList?.map(item => {
249
+ return item.iconId
250
+ })
251
+ const allIcon = iconList?.map(item => {
252
+ return item.id
253
+ })
254
+ const availableChart = allIcon.filter((element) => !iconIdList.includes(element))
255
+ const randomIndex = Math.floor(Math.random() * availableChart.length)
256
+ return availableChart[randomIndex]
257
+ }
258
+
259
+ return (
260
+ <>
261
+ <Page
262
+ backText={deviceInfo.name}
263
+ onBackClick={navigation.goBack}
264
+ headlineText={I18n.getLang('add_new_trigger_time_system_back_text')}
265
+ headlineContent={<Switch
266
+ value={state.enable}
267
+ thumbColor={'#f60'}
268
+ trackColor={{ false: '#00000026', true: '#ff660036' }}
269
+ onValueChange={async enable => {
270
+ state.loading = true
271
+ state.enable = enable
272
+ requestSetBiorhythm(false)
273
+ }}
274
+ />}
275
+ loading={state.loading}
276
+ >
277
+ <ScrollView nestedScrollEnabled={true} style={{ position: 'relative' }}>
278
+ <View style={{ marginHorizontal: cx(24) }}>
279
+ {sunHomeText(I18n.getLang(devicesJudge ? 'bio_ryhthm_default_description_text' : 'bio_ryhthm_non_sun_home_products_description_text'))}
280
+ </View>
281
+ <View style={{ height: cx(10) }} />
282
+ <View
283
+ style={{
284
+ flexDirection: 'row',
285
+ justifyContent: 'space-between',
286
+ marginHorizontal: cx(24),
287
+ }}
288
+ >
289
+ {state.repeatPeriod.map(period => {
290
+ return (
291
+ <TouchableOpacity
292
+ key={period.title}
293
+ onPress={() => {
294
+ const periodNum = state.repeatPeriod.filter(p => p.enabled)?.length
295
+ if (periodNum === 1 && period.enabled) return
296
+ period.enabled = !period.enabled
297
+ requestSetBiorhythm(false)
298
+ }}
299
+ >
300
+ <View
301
+ style={{
302
+ width: cx(40),
303
+ height: cx(40),
304
+ justifyContent: 'center',
305
+ alignItems: 'center',
306
+ borderRadius: cx(20),
307
+ backgroundColor: period.enabled ? '#ffe0d4' : '#fff',
308
+ borderWidth: cx(1),
309
+ borderColor: '#f60',
310
+ }}
311
+ >
312
+ <Text
313
+ style={{
314
+ color: '#FF6600',
315
+ textAlign: 'center',
316
+ }}
317
+ >
318
+ {period.title}
319
+ </Text>
320
+ </View>
321
+ </TouchableOpacity>
322
+ )
323
+ })}
324
+ </View>
325
+ <View style={{ marginHorizontal: cx(24), marginTop: cx(20) }}>
326
+ <Text>{state.weekString}</Text>
327
+ </View>
328
+ <View style={{ marginHorizontal: cx(24), marginTop: cx(16) }}>
329
+ <Text>
330
+ {I18n.getLang('bio_ryhthm_default_selectionfield_topic_text')}
331
+ </Text>
332
+ <TouchableOpacity
333
+ onPress={() => {
334
+ showGradientTypeSelectModal(true)
335
+ }}
336
+ >
337
+ <View
338
+ style={{
339
+ flexDirection: 'row',
340
+ borderRadius: cx(4),
341
+ backgroundColor: '#f6f6f6',
342
+ alignItems: 'center',
343
+ flex: 1,
344
+ height: cx(44),
345
+ borderBottomWidth: cx(1),
346
+ borderBottomColor: '#cbcbcb',
347
+ }}
348
+ >
349
+ <Text style={{
350
+ fontSize: cx(16),
351
+ color: '#000',
352
+ fontFamily: 'helvetica_neue_lt_std_roman',
353
+ paddingLeft: cx(16),
354
+ }}>
355
+ {
356
+ I18n.getLang(
357
+ state.gradient === BiorhythmGradientType.DirectGradient
358
+ ? 'add_new_dynamic_mood_color_changing_mode_value2'
359
+ : 'add_new_dynamic_mood_color_changing_mode_value',
360
+ )
361
+ }
362
+
363
+ </Text>
364
+ </View>
365
+ </TouchableOpacity>
366
+ </View>
367
+ <View style={{ height: cx(20) }} />
368
+ <TimeCircular
369
+ planEdit={true}
370
+ planList={state.planList}
371
+ onPanMoved={(id, time) => {
372
+ state.planList = state.planList.map(plan => {
373
+ return {
374
+ ...plan,
375
+ time: plan.index === id ? time : plan.time,
376
+ }
377
+ })
378
+ state.flag = Symbol()
379
+ }}
380
+ replaceStatus={devicesJudge}
381
+ gradient={state.gradient === BiorhythmGradientType.DirectGradient}
382
+ isSupportTemperature={!params.isSupportTemperature} />
383
+ <View
384
+ style={{
385
+ flexDirection: 'row',
386
+ justifyContent: 'space-between',
387
+ marginHorizontal: cx(24),
388
+ marginBottom: cx(-8),
389
+ marginTop: cx(26),
390
+ }}>
391
+ {state.planList.length === 8 && <View
392
+ style={{ marginVertical: cx(10), flexDirection: 'row', alignItems: 'center', width: width - cx(48) }}>
393
+ <Image style={{ width: cx(16), height: cx(16), tintColor: '#ff9500' }} source={res.ic_warning_amber} />
394
+ <Text
395
+ style={{
396
+ flexWrap: 'wrap',
397
+ fontSize: cx(12),
398
+ }}>{I18n.getLang('add_new_trigger_time_warning_max_number_text')}</Text>
399
+ </View>}
400
+ {state.planList.length < 8 &&
401
+ <>
402
+ <Text
403
+ style={{
404
+ fontSize: cx(16),
405
+ fontWeight: 'bold',
406
+ color: 'rgb(0,0,0)',
407
+ }}>{I18n.getLang('bio_ryhthm_default_subheadline_text')}</Text>
408
+ <TouchableOpacity
409
+ onPress={() => {
410
+ const ids: number[] = state.planList.map(p => p.index)
411
+ const newPlan: Plan = {
412
+ index: Math.max(...ids) + 1,
413
+ icon: res.rhythm_icon1,
414
+ time: 0,
415
+ name: '',
416
+ colorTemperature: 0,
417
+ brightness: 100,
418
+ action: [
419
+ {
420
+ uri: 'model/attribute/set/LightCtrl/ColorTemperature',
421
+ startValue: `${colorTemperatureValue(0)}`,
422
+ },
423
+ {
424
+ uri: 'model/attribute/set/LightCtrl/Brightness',
425
+ startValue: '100',
426
+ },
427
+ ],
428
+ enable: true,
429
+ iconId: randomIcon(),
430
+ }
431
+ const editPageParams: BiorhythmEditPageParams = {
432
+ planData: newPlan,
433
+ isAdd: true,
434
+ onPlanEdited,
435
+ onPlanDelete,
436
+ minimumEnable,
437
+ nameRepeat,
438
+ iconIdList: state.planList?.map(item => {
439
+ return item.iconId
440
+ }),
441
+ isMixRGBWLamp: !!params.isMixLight,
442
+ isSupportTemperature: params.isSupportTemperature,
443
+ isSupportBrightness: params.isSupportBrightness,
444
+ showDeleteBtn
445
+ }
446
+ navigation.navigate(ui_biz_routerKey.bi_biz_biological_edit, editPageParams)
447
+ }}>
448
+ <Image source={BiologicalRes.biorhythom_add} style={{ height: cx(24), width: cx(24) }} />
449
+ </TouchableOpacity>
450
+ </>
451
+ }
452
+ </View>
453
+ <FlatList
454
+ data={state.planList}
455
+ style={{
456
+ flex: 1,
457
+ marginTop: cx(12),
458
+ }}
459
+ nestedScrollEnabled={true}
460
+ ListHeaderComponent={() => (<Spacer height={cx(16)} />)}
461
+ ItemSeparatorComponent={() => (<Spacer height={cx(16)} />)}
462
+ renderItem={({ item }) => {
463
+ const type = typeof item?.icon === 'string'
464
+ const bgColor = item?.brightness === 0 ? '#000' : !params.isSupportTemperature && cctToColor(1) || cctToColor(item.colorTemperature.toFixed(), item?.brightness)
465
+ return (
466
+ <Card
467
+ style={{ marginHorizontal: cx(24) }}
468
+ onPress={() => {
469
+ const editPageParams: BiorhythmEditPageParams = {
470
+ planData: {
471
+ ...item,
472
+ name: setTimer(item?.name)
473
+ },
474
+ isAdd: false,
475
+ onPlanEdited,
476
+ onPlanDelete,
477
+ minimumEnable,
478
+ nameRepeat,
479
+ iconIdList: state.planList?.map(item => {
480
+ return item.iconId
481
+ }),
482
+ isMixRGBWLamp: !!params.isMixLight,
483
+ isSupportTemperature: params.isSupportTemperature,
484
+ isSupportBrightness: params.isSupportBrightness,
485
+ showDeleteBtn
486
+ }
487
+ navigation.navigate(ui_biz_routerKey.bi_biz_biological_edit, editPageParams)
488
+ }}
489
+ >
490
+ <View
491
+ style={{
492
+ flex: 1,
493
+ flexDirection: 'column',
494
+ }}
495
+ >
496
+ <View style={{ flex: 1, flexDirection: 'row', justifyContent: 'space-between', marginTop: cx(16) }}>
497
+ <View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
498
+ <Image
499
+ source={setImg(item?.iconId) || type && { uri: item?.icon } || item?.icon}
500
+ style={{
501
+ width: cx(24),
502
+ height: cx(24),
503
+ marginStart: cx(10),
504
+ marginRight: cx(6),
505
+ tintColor: '#474e5d',
506
+ }}
507
+ />
508
+ <Text
509
+ style={{
510
+ fontSize: cx(16),
511
+ color: '#000',
512
+ fontFamily: 'helvetica_neue_lt_std_roman',
513
+ }}
514
+ >
515
+ {convertMinutesTo12HourFormat(item.time, is24Hour)}
516
+ </Text>
517
+ </View>
518
+ <Switch
519
+ value={item.enable}
520
+ thumbColor={'#f60'}
521
+ trackColor={{ false: '#00000026', true: '#ff660036' }}
522
+ onValueChange={e => {
523
+ item.enable = e
524
+ requestSetBiorhythm()
525
+ }}
526
+ style={{ marginRight: cx(10) }}
527
+ />
528
+ </View>
529
+ <View style={{ flex: 1, flexDirection: 'row', justifyContent: 'space-between' }}>
530
+ <Text
531
+ style={{
532
+ fontSize: cx(12),
533
+ color: '#666',
534
+ fontFamily: 'helvetica_neue_lt_std_roman',
535
+ paddingLeft: cx(20),
536
+ }}
537
+ >
538
+ {setTimer(item?.name)}
539
+ </Text>
540
+ </View>
541
+ <View
542
+ style={{
543
+ width: cx(295),
544
+ height: cx(24),
545
+ backgroundColor: bgColor,
546
+ marginLeft: cx(20),
547
+ marginTop: cx(5),
548
+ marginBottom: cx(24),
549
+ borderRadius: cx(8),
550
+ }} />
551
+ </View>
552
+ </Card>
553
+ )
554
+ }}
555
+ keyExtractor={item => `${item.index}`}
556
+ ListFooterComponent={() => {
557
+ return (
558
+ <View style={{ marginHorizontal: cx(24) }}>
559
+ <Spacer />
560
+ <DeleteButton
561
+ text={I18n.getLang('bio_ryhthm_default_button_reset_text')}
562
+ textStyle={{
563
+ fontSize: cx(16),
564
+ color: '#fff',
565
+ fontFamily: 'helvetica_neue_lt_std_roman',
566
+ }}
567
+ onPress={() => {
568
+ showCommonDialog({
569
+ method: 'confirm',
570
+ title: I18n.getLang('bio_ryhthm_reset_description_text'),
571
+ onConfirm: (_, { close }) => {
572
+ const defBiorhythmUIState = getDefBiorhythmUIState()
573
+ state.enable = defBiorhythmUIState.enable
574
+ state.gradient = defBiorhythmUIState.gradient
575
+ state.repeatPeriod = defBiorhythmUIState.repeatPeriod
576
+ state.planList = defBiorhythmUIState.planList
577
+ requestSetBiorhythm()
578
+ close()
579
+ }
580
+ })
581
+ }}
582
+ />
583
+ <Spacer />
584
+ </View>
585
+ )
586
+ }}
587
+ />
588
+ {!state.enable && (
589
+ <View
590
+ style={{
591
+ backgroundColor: 'rgba(0,0,0,.5)',
592
+ width: '100%',
593
+ height: '100%',
594
+ position: 'absolute',
595
+ top: 0,
596
+ zIndex: 999,
597
+ }}
598
+ />
599
+ )}
600
+ </ScrollView>
601
+ </Page>
602
+ <Modal.List
603
+ type={'radio'}
604
+ visible={state.showGradientTypeSelectModal}
605
+ value={state.gradient}
606
+ dataSource={[
607
+ {
608
+ title: I18n.getLang('add_new_dynamic_mood_color_changing_mode_value'),
609
+ key: BiorhythmGradientType.EntireGradient.toString(),
610
+ value: BiorhythmGradientType.EntireGradient,
611
+ },
612
+ {
613
+ title: I18n.getLang('add_new_dynamic_mood_color_changing_mode_value2'),
614
+ key: BiorhythmGradientType.DirectGradient.toString(),
615
+ value: BiorhythmGradientType.DirectGradient,
616
+ },
617
+ ]}
618
+ title={I18n.getLang('add_new_dynamic_mood_color_changing_mode_headline')}
619
+ onMaskPress={() => {
620
+ showGradientTypeSelectModal(false)
621
+ }}
622
+ onCancel={() => {
623
+ showGradientTypeSelectModal(false)
624
+ }}
625
+ cancelText={I18n.getLang('auto_scan_system_cancel')}
626
+ confirmText={I18n.getLang('auto_scan_system_wifi_confirm')}
627
+ onConfirm={(item: BiorhythmGradientType) => {
628
+ state.gradient = item
629
+ requestSetBiorhythm()
630
+ showGradientTypeSelectModal(false)
631
+ }}
632
+ />
633
+ </>
634
+ )
635
+ }
636
+
637
+ export default BiorhythmPage
638
+
639
+