@ledvance/ui-biz-bundle 1.1.70 → 1.1.71

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,786 +1,789 @@
1
- import React, { useCallback, useEffect, useMemo } from 'react';
2
- import {
3
- FlatList,
4
- Image,
5
- ScrollView,
6
- StyleSheet,
7
- Text,
8
- TouchableOpacity,
9
- View,
10
- } from 'react-native';
11
- import { Utils } from 'tuya-panel-kit';
12
- import { useReactive } from 'ahooks';
13
- import { cloneDeep, find, isEqual } from 'lodash';
14
- import Page from '@ledvance/base/src/components/Page';
15
- import Strings from '@ledvance/base/src/i18n';
16
- import { StaticMoodEditorPageParams, StaticMoodEditorPageState } from './StaticMoodEditorPage';
17
- import { useNavigation, useRoute } from '@react-navigation/native';
18
- import { hsv2Hex, mapFloatToRange } from '@ledvance/base/src/utils';
19
- import { cctToColor } from '@ledvance/base/src/utils/cctUtils';
20
- import res from '@ledvance/base/src/res';
21
- import TextField from '@ledvance/base/src/components/TextField';
22
- import Card from '@ledvance/base/src/components/Card';
23
- import Spacer from '@ledvance/base/src/components/Spacer';
24
- import LdvSlider from '@ledvance/base/src/components/ldvSlider';
25
- import TextButton from '@ledvance/base/src/components/TextButton';
26
- import {
27
- MoodNodeTransitionMode,
28
- stripLightMoodMode,
29
- lightMoodMode,
30
- StripLightMoodMode,
31
- } from './Interface';
32
- import TextFieldStyleButton from '@ledvance/base/src/components/TextFieldStyleButton';
33
- import {ui_biz_routerKey} from '../../navigation/Routers'
34
- import { SelectPageParams } from '@ledvance/ui-biz-bundle/src/modules/select/SelectPage';
35
- import I18n from '@ledvance/base/src/i18n';
36
- import Segmented from '@ledvance/base/src/components/Segmented';
37
- import { MoodNodeInfo } from './Interface';
38
- import ColorAdjustView from '@ledvance/base/src/components/ColorAdjustView';
39
- import LdvSwitch from '@ledvance/base/src/components/ldvSwitch';
40
- import ColorTempAdjustView from '@ledvance/base/src/components/ColorTempAdjustView';
41
- import { showDialog } from '@ledvance/base/src/utils/common';
42
-
43
- const cx = Utils.RatioUtils.convertX;
44
- interface MixDynamicMoodEditorPageState extends StaticMoodEditorPageState {
45
- mainBucketSelected: boolean;
46
- secondaryBucketSelected: boolean;
47
- mainNodeIdx: number;
48
- secondaryIdx: number;
49
- sceneMode: StripLightMoodMode;
50
- }
51
- const MixDynamicMoodEditorPage = () => {
52
- const navigation = useNavigation();
53
- const routeParams = useRoute().params as StaticMoodEditorPageParams;
54
- const params = cloneDeep(routeParams);
55
- const moduleParams = params.moduleParams;
56
- const state = useReactive<MixDynamicMoodEditorPageState>({
57
- headline: '',
58
- mood: params.currentMood,
59
- mainNode: params.currentMood.mainLamp.nodes[params.currentMood.mainLamp.nodes.length - 1],
60
- secondaryNode:
61
- params.currentMood.secondaryLamp.nodes[params.currentMood.secondaryLamp.nodes.length - 1],
62
- mainNodeIdx: params.currentMood.mainLamp.nodes.length - 1,
63
- secondaryIdx: params.currentMood.secondaryLamp.nodes.length - 1,
64
- mainBucketSelected: false,
65
- secondaryBucketSelected: false,
66
- loading: false,
67
- sceneMode: moduleParams.isCeilingLight ? stripLightMoodMode : lightMoodMode,
68
- });
69
- useEffect(() => {
70
- state.headline = Strings.getLang(
71
- params.mode === 'add'
72
- ? 'add_new_dynamic_mood_headline_text'
73
- : 'edit_static_mood_headline_text'
74
- );
75
- }, [params.mode]);
76
-
77
- const getColorBlockColor = useCallback((node: MoodNodeInfo) => {
78
- const s = Math.round(mapFloatToRange(node.s / 100, 30, 100));
79
- if (node.isColorNode) {
80
- return hsv2Hex(node.h, s, 100);
81
- } else {
82
- return cctToColor(node.colorTemp.toFixed());
83
- }
84
- }, []);
85
-
86
- const getNodeColor = useCallback((node: MoodNodeInfo) => {
87
- if (node.isColorNode) {
88
- const s = Math.round(mapFloatToRange(node.s / 100, 30, 100));
89
- return hsv2Hex(node.h, s, 100);
90
- }
91
- return cctToColor(node.colorTemp.toFixed());
92
- }, []);
93
-
94
- const createSelectModeData = useCallback(
95
- (mode: number, moodMode: StripLightMoodMode) => {
96
- return Object.values(moodMode).map(scene => {
97
- return {
98
- text: scene.title,
99
- selected: scene.mode === mode,
100
- value: scene.mode,
101
- };
102
- });
103
- },
104
- []
105
- );
106
-
107
- const createSelectOtherData = useCallback((otherData, expand) => {
108
- return otherData.map(other => {
109
- return {
110
- text: other.label,
111
- selected: other.value === expand,
112
- value: other.value,
113
- };
114
- });
115
- }, []);
116
-
117
- const getSelectOther = useCallback((otherData, expand) => {
118
- const currentOther = otherData.find(other => other.value === expand);
119
- return currentOther.label;
120
- }, []);
121
-
122
- const nameRepeat = useMemo(() => {
123
- return params.nameRepeat(state.mood)
124
- }, [state.mood.name]);
125
-
126
- const checkMoodChanged = useMemo(() =>{
127
- return isEqual(state.mood, params.currentMood)
128
- }, [JSON.stringify(state.mood), params.currentMood])
129
-
130
- const canSaveMoodData = useMemo(() =>{
131
- return state.mood.name.length > 0 && state.mood.name.length < 33 && !nameRepeat && (params.mode === 'add' || !checkMoodChanged)
132
- }, [nameRepeat, state.mood.name, checkMoodChanged, params.mode])
133
-
134
- return (
135
- <Page
136
- backText={Strings.getLang('mesh_device_detail_mode')}
137
- showBackDialog={!checkMoodChanged}
138
- backDialogTitle={Strings.getLang(
139
- params.mode === 'add'
140
- ? 'string_light_pp_dialog_sm_add_headline_c'
141
- : 'manage_user_unsaved_changes_dialog_headline'
142
- )}
143
- backDialogContent={Strings.getLang(
144
- params.mode === 'add'
145
- ? 'strip_light_static_mood_add_step_2_dialog_text'
146
- : 'strip_light_static_mood_editor_step_2_dialog_text'
147
- )}
148
- headlineText={state.headline}
149
- rightButtonIcon={canSaveMoodData ? res.ic_check : res.ic_uncheck}
150
- rightButtonIconClick={async () => {
151
- if (state.loading || !canSaveMoodData) return;
152
- state.loading = true;
153
- const newMood = cloneDeep(state.mood)
154
- if(moduleParams.isMixLight){
155
- if(moduleParams.isSupportBrightness){
156
- newMood.mainLamp.type = 1
157
- }
158
- if(moduleParams.isSupportBrightness && moduleParams.isSupportTemperature){
159
- newMood.mainLamp.type = 2
160
- }
161
- if(moduleParams.isSupportColor){
162
- newMood.secondaryLamp.type = 3
163
- }
164
- }
165
- const res = await params.modDeleteMood(params.mode, newMood);
166
- if (res.success) {
167
- navigation.navigate(ui_biz_routerKey.ui_biz_mood);
168
- }
169
- state.loading = false;
170
- }}
171
- loading={state.loading}
172
- >
173
- <ScrollView style={{ flex: 1 }} nestedScrollEnabled={true}>
174
- <View style={styles.root}>
175
- <TextField
176
- style={styles.name}
177
- value={state.mood.name}
178
- placeholder={Strings.getLang('edit_static_mood_inputfield_topic_text')}
179
- onChangeText={text => {
180
- state.mood.name = text;
181
- }}
182
- maxLength={33}
183
- showError={state.mood.name.length > 32 || nameRepeat}
184
- tipColor={nameRepeat ? '#f00' : undefined}
185
- tipIcon={nameRepeat ? res.ic_text_field_input_error : undefined}
186
- errorText={Strings.getLang(
187
- nameRepeat ? 'string_light_pp_field_sm_add_error1' : 'add_new_dynamic_mood_alert_text'
188
- )}
189
- />
190
- <Card style={styles.adjustCard}>
191
- <LdvSwitch
192
- title={I18n.getLang('light_sources_tile_main_lighting_headline')}
193
- color="#fff"
194
- colorAlpha={1}
195
- enable={!!state.mood.mainLamp.enable}
196
- setEnable={v => {
197
- if (v && !state.mood.mainLamp.nodes.length) {
198
- state.mood.mainLamp.nodes.push(
199
- {
200
- h: 0,
201
- s: 0,
202
- v: 0,
203
- brightness: 100,
204
- colorTemp: 0,
205
- isColorNode: false,
206
- },
207
- {
208
- h: 0,
209
- s: 0,
210
- v: 0,
211
- brightness: 100,
212
- colorTemp: 0,
213
- isColorNode: false,
214
- }
215
- );
216
- state.mood.mainLamp.mode = MoodNodeTransitionMode.Gradient;
217
- state.mood.mainLamp.speed = 75;
218
- state.mainNodeIdx = 1;
219
- state.mainNode = state.mood.mainLamp.nodes[1];
220
- }
221
-
222
- state.mood.mainLamp.enable = v;
223
- }}
224
- showSwitch={!!moduleParams.isMixLight}
225
- />
226
- {(!moduleParams.isMixLight || state.mood.mainLamp.enable) && (
227
- <>
228
- <TextFieldStyleButton
229
- style={styles.transitionMode}
230
- text={lightMoodMode[state.mood.mainLamp.mode]?.title}
231
- placeholder={I18n.getLang('add_new_dynamic_mood_color_changing_mode_headline')}
232
- onPress={() => {
233
- const paramsSelect: SelectPageParams<number> = {
234
- title: I18n.getLang('add_new_dynamic_mood_color_changing_mode_headline'),
235
- data: createSelectModeData(state.mood.mainLamp.mode, lightMoodMode),
236
- onSelect: selectPageData => {
237
- state.mood.mainLamp.mode = selectPageData.value;
238
- },
239
- };
240
- navigation.navigate(ui_biz_routerKey.ui_biz_select_page, paramsSelect);
241
- }}
242
- />
243
- <Spacer height={cx(10)} />
244
- <LdvSlider
245
- title={I18n.getLang('add_new_dynamic_mood_lights_field_speed_topic_text')}
246
- value={state.mood.mainLamp.speed}
247
- onValueChange={() => {}}
248
- onSlidingComplete={value => {
249
- state.mood.mainLamp.speed = value;
250
- }}
251
- />
252
- <Spacer height={cx(16)} />
253
- <View style={styles.nodesAdjust}>
254
- <View style={styles.adjustButtons}>
255
- <TouchableOpacity
256
- onPress={() => {
257
- state.mainBucketSelected = true;
258
- }}
259
- >
260
- <Image
261
- style={[
262
- styles.adjustButton,
263
- { tintColor: state.mainBucketSelected ? '#f60' : '#666' },
264
- ]}
265
- source={res.ic_paint_bucket}
266
- />
267
- </TouchableOpacity>
268
- <TouchableOpacity
269
- onPress={() => {
270
- state.mainBucketSelected = false;
271
- }}
272
- >
273
- <Image
274
- style={[
275
- styles.adjustButton,
276
- { tintColor: state.mainBucketSelected ? '#666' : '#f60' },
277
- ]}
278
- source={res.ic_colorize}
279
- />
280
- </TouchableOpacity>
281
- </View>
282
- <FlatList
283
- data={state.mood.mainLamp.nodes}
284
- style={styles.nodeList}
285
- renderItem={({ item, index }) => {
286
- return (
287
- <View style={styles.nodeItem}>
288
- <TouchableOpacity
289
- style={[
290
- styles.nodeBlock,
291
- {
292
- backgroundColor: getNodeColor(item),
293
- },
294
- ]}
295
- onPress={() => {
296
- state.mainNodeIdx = index;
297
- state.mainNode = state.mood.mainLamp.nodes[index];
298
- }}
299
- />
300
- <TouchableOpacity
301
- style={styles.nodeDeleteBtn}
302
- disabled={state.mood.mainLamp.nodes.length < 3}
303
- onPress={() => {
304
- state.mood.mainLamp.nodes.splice(index, 1);
305
- state.mainNodeIdx = state.mood.mainLamp.nodes.length - 1;
306
- }}
307
- >
308
- <Image
309
- style={[
310
- styles.nodeDeleteIcon,
311
- {
312
- tintColor: state.mood.mainLamp.nodes.length < 3 ? '#ccc' : '#666',
313
- },
314
- ]}
315
- source={res.ic_mood_del}
316
- />
317
- </TouchableOpacity>
318
- </View>
319
- );
320
- }}
321
- keyExtractor={(_, index) => `${index}`}
322
- ItemSeparatorComponent={() => <Spacer height={cx(12)} />}
323
- ListFooterComponent={() => {
324
- if (state.mood.mainLamp.nodes.length >= 8) {
325
- return <></>;
326
- }
327
- return (
328
- <View>
329
- <Spacer height={cx(12)} />
330
- <TouchableOpacity
331
- style={styles.nodeAddBtn}
332
- onPress={() => {
333
- const node = {
334
- ...state.mood.mainLamp.nodes[state.mainNodeIdx],
335
- };
336
- state.mood.mainLamp.nodes.push(node);
337
- state.mainNodeIdx = state.mood.mainLamp.nodes.length - 1;
338
- }}
339
- >
340
- <Image
341
- style={{
342
- width: cx(18),
343
- height: cx(18),
344
- tintColor: '#000',
345
- }}
346
- source={{ uri: res.add }}
347
- />
348
- </TouchableOpacity>
349
- </View>
350
- );
351
- }}
352
- />
353
- </View>
354
- <Spacer />
355
- <ColorTempAdjustView
356
- isSupportBrightness={moduleParams.isSupportBrightness}
357
- isSupportTemperature={moduleParams.isSupportTemperature}
358
- colorTemp={state.mood.mainLamp.nodes[state.mainNodeIdx].colorTemp}
359
- brightness={state.mood.mainLamp.nodes[state.mainNodeIdx].brightness}
360
- onBrightnessChangeComplete={bright => {
361
- state.mood.mainLamp.nodes.forEach((node, idx) => {
362
- if (state.mainBucketSelected) {
363
- node.brightness = bright;
364
- }
365
- if (state.mainNodeIdx === idx) {
366
- node.brightness = bright;
367
- }
368
- });
369
- }}
370
- onCCTChangeComplete={cct => {
371
- state.mood.mainLamp.nodes.forEach((node, idx) => {
372
- if (state.mainBucketSelected) {
373
- node.colorTemp = cct;
374
- }
375
- if (state.mainNodeIdx === idx) {
376
- node.colorTemp = cct;
377
- }
378
- });
379
- }}
380
- />
381
- <Spacer height={cx(10)} />
382
- </>
383
- )}
384
- </Card>
385
- <Card style={styles.adjustCard}>
386
- <LdvSwitch
387
- title={I18n.getLang('light_sources_tile_sec_lighting_headline')}
388
- color={'#fff'}
389
- colorAlpha={1}
390
- enable={!!state.mood.secondaryLamp.enable}
391
- setEnable={v => {
392
- state.mood.secondaryLamp.enable = v;
393
- if (v && !state.mood.secondaryLamp.nodes.length) {
394
- state.mood.secondaryLamp.nodes.push(
395
- {
396
- h: 0,
397
- s: 100,
398
- v: 100,
399
- brightness: 0,
400
- colorTemp: 0,
401
- isColorNode: true,
402
- },
403
- {
404
- h: 0,
405
- s: 100,
406
- v: 100,
407
- brightness: 0,
408
- colorTemp: 0,
409
- isColorNode: true,
410
- }
411
- );
412
- state.mood.secondaryLamp.mode = MoodNodeTransitionMode.Gradient;
413
- state.mood.secondaryLamp.speed = 75;
414
- state.secondaryIdx = 1;
415
- state.secondaryNode = state.mood.secondaryLamp.nodes[1];
416
- }
417
- }}
418
- showSwitch={!!moduleParams.isMixLight}
419
- />
420
- {(!moduleParams.isMixLight || state.mood.secondaryLamp.enable) && (
421
- <>
422
- <TextFieldStyleButton
423
- style={styles.transitionMode}
424
- text={state.sceneMode[state.mood.secondaryLamp.mode]?.title}
425
- placeholder={I18n.getLang('add_new_dynamic_mood_color_changing_mode_headline')}
426
- onPress={() => {
427
- const paramsSelect: SelectPageParams<number> = {
428
- title: I18n.getLang('add_new_dynamic_mood_color_changing_mode_headline'),
429
- data: createSelectModeData(
430
- state.mood.secondaryLamp.mode,
431
- state.sceneMode
432
- ),
433
- onSelect: selectPageData => {
434
- state.mood.secondaryLamp.mode = selectPageData.value;
435
- },
436
- };
437
- navigation.navigate(ui_biz_routerKey.ui_biz_select_page, paramsSelect);
438
- }}
439
- />
440
- <Spacer height={cx(10)} />
441
- <LdvSlider
442
- title={I18n.getLang('add_new_dynamic_mood_lights_field_speed_topic_text')}
443
- value={state.mood.secondaryLamp.speed}
444
- onValueChange={() => {}}
445
- onSlidingComplete={value => {
446
- state.mood.secondaryLamp.speed = value;
447
- }}
448
- />
449
- <Spacer height={cx(16)} />
450
- {state.sceneMode[state.mood.secondaryLamp.mode]?.turnOn && (
451
- <View style={styles.transitionMode}>
452
- <Segmented
453
- value={state.mood.secondaryLamp.direction}
454
- options={[
455
- {
456
- label: I18n.getLang('add_new_dynamic_mood_strip_lights_switch_tab_text1'),
457
- value: 0,
458
- },
459
- {
460
- label: I18n.getLang('add_new_dynamic_mood_strip_lights_switch_tab_text2'),
461
- value: 1,
462
- },
463
- ]}
464
- onChange={v => (state.mood.secondaryLamp.direction = Number(v))}
465
- />
466
- <Spacer />
467
- </View>
468
- )}
469
- {state.sceneMode[state.mood.secondaryLamp.mode]?.paragraph && (
470
- <View style={styles.transitionMode}>
471
- <Segmented
472
- value={state.mood.secondaryLamp.segmented}
473
- options={[
474
- {
475
- label: I18n.getLang('add_new_dynamic_mood_strip_lights_switch_tab_text3'),
476
- value: 0,
477
- },
478
- {
479
- label: I18n.getLang('add_new_dynamic_mood_strip_lights_switch_tab_text4'),
480
- value: 1,
481
- },
482
- ]}
483
- onChange={v => (state.mood.secondaryLamp.segmented = Number(v))}
484
- />
485
- <Spacer />
486
- </View>
487
- )}
488
- {state.sceneMode[state.mood.secondaryLamp.mode]?.other && (
489
- <>
490
- <TextFieldStyleButton
491
- style={styles.transitionMode}
492
- text={getSelectOther(
493
- state.sceneMode[state.mood.secondaryLamp.mode]?.other,
494
- state.mood.secondaryLamp.expand
495
- )}
496
- placeholder={I18n.getLang(
497
- 'add_new_dynamic_mood_strip_lights_selectionfield2_topic_text'
498
- )}
499
- onPress={() => {
500
- const paramsSelect: SelectPageParams<number> = {
501
- title: I18n.getLang(
502
- 'add_new_dynamic_mood_strip_lights_selectionfield2_topic_text'
503
- ),
504
- data: createSelectOtherData(
505
- state.sceneMode[state.mood.secondaryLamp.mode]?.other,
506
- state.mood.secondaryLamp.expand
507
- ),
508
- onSelect: selectPageData => {
509
- state.mood.secondaryLamp.expand = selectPageData.value;
510
- },
511
- };
512
- navigation.navigate(ui_biz_routerKey.ui_biz_select_page, paramsSelect);
513
- }}
514
- />
515
- <Spacer />
516
- </>
517
- )}
518
- <View style={styles.nodesAdjust}>
519
- <View style={styles.adjustButtons}>
520
- <TouchableOpacity
521
- onPress={() => {
522
- state.secondaryBucketSelected = true;
523
- }}
524
- >
525
- <Image
526
- style={[
527
- styles.adjustButton,
528
- { tintColor: state.secondaryBucketSelected ? '#f60' : '#666' },
529
- ]}
530
- source={res.ic_paint_bucket}
531
- />
532
- </TouchableOpacity>
533
- <TouchableOpacity
534
- onPress={() => {
535
- state.secondaryBucketSelected = false;
536
- }}
537
- >
538
- <Image
539
- style={[
540
- styles.adjustButton,
541
- { tintColor: state.secondaryBucketSelected ? '#666' : '#f60' },
542
- ]}
543
- source={res.ic_colorize}
544
- />
545
- </TouchableOpacity>
546
- </View>
547
- <FlatList
548
- data={state.mood.secondaryLamp.nodes}
549
- style={styles.nodeList}
550
- renderItem={({ item, index }) => {
551
- return (
552
- <View style={styles.nodeItem}>
553
- <TouchableOpacity
554
- style={[
555
- styles.nodeBlock,
556
- {
557
- backgroundColor: getNodeColor(item),
558
- },
559
- ]}
560
- onPress={() => {
561
- state.secondaryIdx = index;
562
- }}
563
- />
564
- <TouchableOpacity
565
- style={styles.nodeDeleteBtn}
566
- disabled={state.mood.secondaryLamp.nodes.length < 3}
567
- onPress={() => {
568
- state.mood.secondaryLamp.nodes.splice(index, 1);
569
- state.secondaryIdx = state.mood.secondaryLamp.nodes.length - 1;
570
- }}
571
- >
572
- <Image
573
- style={[
574
- styles.nodeDeleteIcon,
575
- {
576
- tintColor:
577
- state.mood.secondaryLamp.nodes.length < 3 ? '#ccc' : '#666',
578
- },
579
- ]}
580
- source={res.ic_mood_del}
581
- />
582
- </TouchableOpacity>
583
- </View>
584
- );
585
- }}
586
- keyExtractor={(_, index) => `${index}`}
587
- ItemSeparatorComponent={() => <Spacer height={cx(12)} />}
588
- ListFooterComponent={() => {
589
- if (state.mood.secondaryLamp.nodes.length >= 8) {
590
- return <></>;
591
- }
592
- return (
593
- <View>
594
- <Spacer height={cx(12)} />
595
- <TouchableOpacity
596
- style={styles.nodeAddBtn}
597
- onPress={() => {
598
- const node = {
599
- ...state.mood.secondaryLamp.nodes[state.secondaryIdx],
600
- };
601
- state.mood.secondaryLamp.nodes.push(node);
602
- state.secondaryIdx = state.mood.secondaryLamp.nodes.length - 1;
603
- }}
604
- >
605
- <Image
606
- style={{
607
- width: cx(18),
608
- height: cx(18),
609
- tintColor: '#000',
610
- }}
611
- source={{ uri: res.add }}
612
- />
613
- </TouchableOpacity>
614
- </View>
615
- );
616
- }}
617
- />
618
- </View>
619
- <Spacer />
620
- <View style={styles.lightLine}>
621
- <Text style={styles.light}>
622
- {I18n.getLang('add_new_dynamic_mood_lights_field_headline2_text')}
623
- </Text>
624
- <View
625
- style={[
626
- styles.preview,
627
- { backgroundColor: getColorBlockColor(state.secondaryNode) },
628
- ]}
629
- />
630
- </View>
631
- <Spacer />
632
- <ColorAdjustView
633
- h={state.secondaryNode.h}
634
- s={state.secondaryNode.s}
635
- v={state.secondaryNode.v}
636
- reserveSV={true}
637
- onHSVChange={() => {}}
638
- onHSVChangeComplete={(h, s, v) => {
639
- state.mood.secondaryLamp.nodes.forEach((node, idx) => {
640
- if (state.secondaryBucketSelected) {
641
- node.h = h;
642
- node.s = s;
643
- node.v = v;
644
- }
645
- if (state.secondaryIdx === idx) {
646
- node.h = h;
647
- node.s = s;
648
- node.v = v;
649
- }
650
- });
651
- }}
652
- />
653
- <Spacer height={cx(10)} />
654
- </>
655
- )}
656
- </Card>
657
-
658
- {params.mode === 'edit' && (
659
- <View style={{ marginTop: cx(20), marginHorizontal: cx(24) }}>
660
- <TextButton
661
- style={styles.deleteBtn}
662
- textStyle={styles.deleteBtnText}
663
- text={Strings.getLang('edit_static_mood_button_delete_text')}
664
- onPress={() => {
665
- showDialog({
666
- method: 'confirm',
667
- title: I18n.getLang('string_light_pp_dialog_sm_ed_headline_d'),
668
- subTitle: I18n.getLang(`strip_light_static_mood_edit_dialog_text`),
669
- onConfirm: async (_, {close})=>{
670
- close();
671
- state.loading = true;
672
- const res = await params.modDeleteMood('del', state.mood);
673
- state.loading = false;
674
- if (res.success) {
675
- navigation.navigate(ui_biz_routerKey.ui_biz_mood);
676
- }
677
- }
678
- })
679
- }}
680
- />
681
- </View>
682
- )}
683
- <Spacer />
684
- </View>
685
- </ScrollView>
686
- </Page>
687
- );
688
- };
689
- const styles = StyleSheet.create({
690
- root: {
691
- flex: 1,
692
- flexDirection: 'column',
693
- },
694
- name: {
695
- marginHorizontal: cx(24),
696
- },
697
- adjustCard: {
698
- marginVertical: cx(12),
699
- marginHorizontal: cx(24),
700
- },
701
- fanAdjustCard: {
702
- marginHorizontal: cx(24),
703
- },
704
- lightLine: {
705
- flexDirection: 'row',
706
- marginHorizontal: cx(16),
707
- },
708
- light: {
709
- color: '#000',
710
- fontSize: cx(18),
711
- fontFamily: 'helvetica_neue_lt_std_bd',
712
- },
713
- transitionMode: {
714
- marginHorizontal: cx(16),
715
- },
716
- preview: {
717
- width: cx(20),
718
- height: cx(20),
719
- marginStart: cx(12),
720
- borderRadius: cx(4),
721
- },
722
- nodesAdjust: {
723
- flexDirection: 'row',
724
- alignItems: 'center',
725
- },
726
- adjustButtons: {
727
- width: cx(44),
728
- marginStart: cx(16),
729
- },
730
- adjustButton: {
731
- width: cx(44),
732
- height: cx(44),
733
- },
734
- nodeList: {
735
- flex: 1,
736
- marginHorizontal: cx(16),
737
- },
738
- nodeItem: {
739
- flexDirection: 'row',
740
- alignItems: 'center',
741
- },
742
- nodeBlock: {
743
- flex: 1,
744
- height: cx(40),
745
- borderRadius: cx(8),
746
- },
747
- nodeDeleteBtn: {
748
- width: cx(24),
749
- height: cx(30),
750
- justifyContent: 'center',
751
- alignItems: 'center',
752
- },
753
- nodeDeleteIcon: {
754
- width: cx(16),
755
- height: cx(16),
756
- },
757
- nodeAddBtn: {
758
- height: cx(40),
759
- justifyContent: 'center',
760
- alignItems: 'center',
761
- marginEnd: cx(26),
762
- borderRadius: cx(8),
763
- borderWidth: cx(1),
764
- borderStyle: 'dashed',
765
- borderColor: '#666',
766
- backgroundColor: '#f6f6f6',
767
- },
768
- deleteBtn: {
769
- width: '100%',
770
- height: cx(50),
771
- backgroundColor: '#666',
772
- borderRadius: cx(8),
773
- },
774
- deleteBtnText: {
775
- color: '#fff',
776
- fontSize: cx(16),
777
- fontFamily: 'helvetica_neue_lt_std_bd',
778
- },
779
- });
780
- export default MixDynamicMoodEditorPage;
781
- export function getTransitionModeString(transitionMode: MoodNodeTransitionMode): string {
782
- if (transitionMode === MoodNodeTransitionMode.Jump) {
783
- return Strings.getLang('other_lights_modes_jump_text');
784
- }
785
- return Strings.getLang('other_lights_modes_gradient_text');
786
- }
1
+ import React, { useCallback, useEffect, useMemo } from 'react';
2
+ import {
3
+ FlatList,
4
+ Image,
5
+ ScrollView,
6
+ StyleSheet,
7
+ Text,
8
+ TouchableOpacity,
9
+ View,
10
+ } from 'react-native';
11
+ import { Utils } from 'tuya-panel-kit';
12
+ import { useReactive } from 'ahooks';
13
+ import { cloneDeep, isEqual } from 'lodash';
14
+ import Page from '@ledvance/base/src/components/Page';
15
+ import Strings from '@ledvance/base/src/i18n';
16
+ import { StaticMoodEditorPageParams, StaticMoodEditorPageState } from './StaticMoodEditorPage';
17
+ import { useNavigation, useRoute } from '@react-navigation/native';
18
+ import { hsv2Hex, mapFloatToRange } from '@ledvance/base/src/utils';
19
+ import { cctToColor } from '@ledvance/base/src/utils/cctUtils';
20
+ import res from '@ledvance/base/src/res';
21
+ import TextField from '@ledvance/base/src/components/TextField';
22
+ import Card from '@ledvance/base/src/components/Card';
23
+ import Spacer from '@ledvance/base/src/components/Spacer';
24
+ import LdvSlider from '@ledvance/base/src/components/ldvSlider';
25
+ import TextButton from '@ledvance/base/src/components/TextButton';
26
+ import {
27
+ MoodNodeTransitionMode,
28
+ stripLightMoodMode,
29
+ lightMoodMode,
30
+ StripLightMoodMode,
31
+ CeilingLightSceneMode
32
+ } from './Interface';
33
+ import TextFieldStyleButton from '@ledvance/base/src/components/TextFieldStyleButton';
34
+ import {ui_biz_routerKey} from '../../navigation/Routers'
35
+ import { SelectPageParams } from '@ledvance/ui-biz-bundle/src/modules/select/SelectPage';
36
+ import I18n from '@ledvance/base/src/i18n';
37
+ import Segmented from '@ledvance/base/src/components/Segmented';
38
+ import { MoodNodeInfo } from './Interface';
39
+ import ColorAdjustView from '@ledvance/base/src/components/ColorAdjustView';
40
+ import LdvSwitch from '@ledvance/base/src/components/ldvSwitch';
41
+ import ColorTempAdjustView from '@ledvance/base/src/components/ColorTempAdjustView';
42
+ import { showDialog } from '@ledvance/base/src/utils/common';
43
+
44
+ const cx = Utils.RatioUtils.convertX;
45
+ interface MixDynamicMoodEditorPageState extends StaticMoodEditorPageState {
46
+ mainBucketSelected: boolean;
47
+ secondaryBucketSelected: boolean;
48
+ mainNodeIdx: number;
49
+ secondaryIdx: number;
50
+ sceneMode: StripLightMoodMode;
51
+ mainSceneMode: any
52
+ }
53
+ const MixDynamicMoodEditorPage = () => {
54
+ const navigation = useNavigation();
55
+ const routeParams = useRoute().params as StaticMoodEditorPageParams;
56
+ const params = cloneDeep(routeParams);
57
+ const moduleParams = params.moduleParams;
58
+ const state = useReactive<MixDynamicMoodEditorPageState>({
59
+ headline: '',
60
+ mood: params.currentMood,
61
+ mainNode: params.currentMood.mainLamp.nodes[params.currentMood.mainLamp.nodes.length - 1],
62
+ secondaryNode:
63
+ params.currentMood.secondaryLamp.nodes[params.currentMood.secondaryLamp.nodes.length - 1],
64
+ mainNodeIdx: params.currentMood.mainLamp.nodes.length - 1,
65
+ secondaryIdx: params.currentMood.secondaryLamp.nodes.length - 1,
66
+ mainBucketSelected: false,
67
+ secondaryBucketSelected: false,
68
+ loading: false,
69
+ sceneMode: moduleParams.isCeilingLight ? stripLightMoodMode : lightMoodMode,
70
+ mainSceneMode: moduleParams.isCeilingLight ? CeilingLightSceneMode : lightMoodMode
71
+ });
72
+ useEffect(() => {
73
+ state.headline = Strings.getLang(
74
+ params.mode === 'add'
75
+ ? 'add_new_dynamic_mood_headline_text'
76
+ : 'edit_static_mood_headline_text'
77
+ );
78
+ }, [params.mode]);
79
+
80
+ const getColorBlockColor = useCallback((node: MoodNodeInfo) => {
81
+ const s = Math.round(mapFloatToRange(node.s / 100, 30, 100));
82
+ if (node.isColorNode) {
83
+ return hsv2Hex(node.h, s, 100);
84
+ } else {
85
+ return cctToColor(node.colorTemp.toFixed());
86
+ }
87
+ }, []);
88
+
89
+ const getNodeColor = useCallback((node: MoodNodeInfo) => {
90
+ if (node.isColorNode) {
91
+ const s = Math.round(mapFloatToRange(node.s / 100, 30, 100));
92
+ return hsv2Hex(node.h, s, 100);
93
+ }
94
+ return cctToColor(node.colorTemp.toFixed());
95
+ }, []);
96
+
97
+ const createSelectModeData = useCallback(
98
+ (mode: number, moodMode: StripLightMoodMode) => {
99
+ return Object.values(moodMode).map(scene => {
100
+ return {
101
+ text: scene.title,
102
+ selected: scene.mode === mode,
103
+ value: scene.mode,
104
+ };
105
+ });
106
+ },
107
+ []
108
+ );
109
+
110
+ const createSelectOtherData = useCallback((otherData, expand) => {
111
+ return otherData.map(other => {
112
+ return {
113
+ text: other.label,
114
+ selected: other.value === expand,
115
+ value: other.value,
116
+ };
117
+ });
118
+ }, []);
119
+
120
+ const getSelectOther = useCallback((otherData, expand) => {
121
+ const currentOther = otherData.find(other => other.value === expand);
122
+ return currentOther.label;
123
+ }, []);
124
+
125
+ const nameRepeat = useMemo(() => {
126
+ return params.nameRepeat(state.mood)
127
+ }, [state.mood.name]);
128
+
129
+ const checkMoodChanged = useMemo(() =>{
130
+ return isEqual(state.mood, params.currentMood)
131
+ }, [JSON.stringify(state.mood), params.currentMood])
132
+
133
+ const canSaveMoodData = useMemo(() =>{
134
+ return state.mood.name.length > 0 && state.mood.name.length < 33 && !nameRepeat && (params.mode === 'add' || !checkMoodChanged)
135
+ }, [nameRepeat, state.mood.name, checkMoodChanged, params.mode])
136
+
137
+ return (
138
+ <Page
139
+ backText={Strings.getLang('mesh_device_detail_mode')}
140
+ showBackDialog={!checkMoodChanged}
141
+ backDialogTitle={Strings.getLang(
142
+ params.mode === 'add'
143
+ ? 'string_light_pp_dialog_sm_add_headline_c'
144
+ : 'manage_user_unsaved_changes_dialog_headline'
145
+ )}
146
+ backDialogContent={Strings.getLang(
147
+ params.mode === 'add'
148
+ ? 'strip_light_static_mood_add_step_2_dialog_text'
149
+ : 'strip_light_static_mood_editor_step_2_dialog_text'
150
+ )}
151
+ headlineText={state.headline}
152
+ rightButtonIcon={canSaveMoodData ? res.ic_check : res.ic_uncheck}
153
+ rightButtonIconClick={async () => {
154
+ if (state.loading || !canSaveMoodData) return;
155
+ state.loading = true;
156
+ const newMood = cloneDeep(state.mood)
157
+ if(moduleParams.isMixLight){
158
+ if(moduleParams.isSupportBrightness){
159
+ newMood.mainLamp.type = 1
160
+ }
161
+ if(moduleParams.isSupportBrightness && moduleParams.isSupportTemperature){
162
+ newMood.mainLamp.type = 2
163
+ }
164
+ if(moduleParams.isSupportColor){
165
+ newMood.secondaryLamp.type = 3
166
+ }
167
+ }
168
+ const res = await params.modDeleteMood(params.mode, newMood);
169
+ if (res.success) {
170
+ navigation.navigate(ui_biz_routerKey.ui_biz_mood);
171
+ }
172
+ state.loading = false;
173
+ }}
174
+ loading={state.loading}
175
+ >
176
+ <ScrollView style={{ flex: 1 }} nestedScrollEnabled={true}>
177
+ <View style={styles.root}>
178
+ <TextField
179
+ style={styles.name}
180
+ value={state.mood.name}
181
+ placeholder={Strings.getLang('edit_static_mood_inputfield_topic_text')}
182
+ onChangeText={text => {
183
+ state.mood.name = text;
184
+ }}
185
+ maxLength={33}
186
+ showError={state.mood.name.length > 32 || nameRepeat}
187
+ tipColor={nameRepeat ? '#f00' : undefined}
188
+ tipIcon={nameRepeat ? res.ic_text_field_input_error : undefined}
189
+ errorText={Strings.getLang(
190
+ nameRepeat ? 'string_light_pp_field_sm_add_error1' : 'add_new_dynamic_mood_alert_text'
191
+ )}
192
+ />
193
+ <Card style={styles.adjustCard}>
194
+ <LdvSwitch
195
+ title={I18n.getLang('light_sources_tile_main_lighting_headline')}
196
+ color="#fff"
197
+ colorAlpha={1}
198
+ enable={!!state.mood.mainLamp.enable}
199
+ setEnable={v => {
200
+ if (v && !state.mood.mainLamp.nodes.length) {
201
+ state.mood.mainLamp.nodes.push(
202
+ {
203
+ h: 0,
204
+ s: 0,
205
+ v: 0,
206
+ brightness: 100,
207
+ colorTemp: 0,
208
+ isColorNode: false,
209
+ },
210
+ {
211
+ h: 0,
212
+ s: 0,
213
+ v: 0,
214
+ brightness: 100,
215
+ colorTemp: 0,
216
+ isColorNode: false,
217
+ }
218
+ );
219
+ state.mood.mainLamp.mode = MoodNodeTransitionMode.Gradient;
220
+ state.mood.mainLamp.speed = 75;
221
+ state.mainNodeIdx = 1;
222
+ state.mainNode = state.mood.mainLamp.nodes[1];
223
+ }
224
+
225
+ state.mood.mainLamp.enable = v;
226
+ }}
227
+ showSwitch={!!moduleParams.isMixLight}
228
+ />
229
+ {state.mood.mainLamp.enable && (
230
+ <>
231
+ <TextFieldStyleButton
232
+ style={styles.transitionMode}
233
+ text={state.mainSceneMode[state.mood.mainLamp.mode]?.title}
234
+ placeholder={I18n.getLang('add_new_dynamic_mood_color_changing_mode_headline')}
235
+ onPress={() => {
236
+ const paramsSelect: SelectPageParams<number> = {
237
+ title: I18n.getLang('add_new_dynamic_mood_color_changing_mode_headline'),
238
+ data: createSelectModeData(state.mood.mainLamp.mode, state.mainSceneMode),
239
+ onSelect: selectPageData => {
240
+ state.mood.mainLamp.mode = selectPageData.value;
241
+ },
242
+ };
243
+ navigation.navigate(ui_biz_routerKey.ui_biz_select_page, paramsSelect);
244
+ }}
245
+ />
246
+ <Spacer height={cx(10)} />
247
+ <LdvSlider
248
+ title={I18n.getLang('add_new_dynamic_mood_lights_field_speed_topic_text')}
249
+ value={state.mood.mainLamp.speed}
250
+ onValueChange={() => {}}
251
+ onSlidingComplete={value => {
252
+ state.mood.mainLamp.speed = value;
253
+ }}
254
+ />
255
+ <Spacer height={cx(16)} />
256
+ <View style={styles.nodesAdjust}>
257
+ <View style={styles.adjustButtons}>
258
+ <TouchableOpacity
259
+ onPress={() => {
260
+ state.mainBucketSelected = true;
261
+ }}
262
+ >
263
+ <Image
264
+ style={[
265
+ styles.adjustButton,
266
+ { tintColor: state.mainBucketSelected ? '#f60' : '#666' },
267
+ ]}
268
+ source={res.ic_paint_bucket}
269
+ />
270
+ </TouchableOpacity>
271
+ <TouchableOpacity
272
+ onPress={() => {
273
+ state.mainBucketSelected = false;
274
+ }}
275
+ >
276
+ <Image
277
+ style={[
278
+ styles.adjustButton,
279
+ { tintColor: state.mainBucketSelected ? '#666' : '#f60' },
280
+ ]}
281
+ source={res.ic_colorize}
282
+ />
283
+ </TouchableOpacity>
284
+ </View>
285
+ <FlatList
286
+ data={state.mood.mainLamp.nodes}
287
+ style={styles.nodeList}
288
+ renderItem={({ item, index }) => {
289
+ return (
290
+ <View style={styles.nodeItem}>
291
+ <TouchableOpacity
292
+ style={[
293
+ styles.nodeBlock,
294
+ {
295
+ backgroundColor: getNodeColor(item),
296
+ },
297
+ ]}
298
+ onPress={() => {
299
+ state.mainNodeIdx = index;
300
+ state.mainNode = state.mood.mainLamp.nodes[index];
301
+ }}
302
+ />
303
+ <TouchableOpacity
304
+ style={styles.nodeDeleteBtn}
305
+ disabled={state.mood.mainLamp.nodes.length < 3}
306
+ onPress={() => {
307
+ state.mood.mainLamp.nodes.splice(index, 1);
308
+ state.mainNodeIdx = state.mood.mainLamp.nodes.length - 1;
309
+ }}
310
+ >
311
+ <Image
312
+ style={[
313
+ styles.nodeDeleteIcon,
314
+ {
315
+ tintColor: state.mood.mainLamp.nodes.length < 3 ? '#ccc' : '#666',
316
+ },
317
+ ]}
318
+ source={res.ic_mood_del}
319
+ />
320
+ </TouchableOpacity>
321
+ </View>
322
+ );
323
+ }}
324
+ keyExtractor={(_, index) => `${index}`}
325
+ ItemSeparatorComponent={() => <Spacer height={cx(12)} />}
326
+ ListFooterComponent={() => {
327
+ if (state.mood.mainLamp.nodes.length >= 8) {
328
+ return <></>;
329
+ }
330
+ return (
331
+ <View>
332
+ <Spacer height={cx(12)} />
333
+ <TouchableOpacity
334
+ style={styles.nodeAddBtn}
335
+ onPress={() => {
336
+ const node = {
337
+ ...state.mood.mainLamp.nodes[state.mainNodeIdx],
338
+ };
339
+ state.mood.mainLamp.nodes.push(node);
340
+ state.mainNodeIdx = state.mood.mainLamp.nodes.length - 1;
341
+ }}
342
+ >
343
+ <Image
344
+ style={{
345
+ width: cx(18),
346
+ height: cx(18),
347
+ tintColor: '#000',
348
+ }}
349
+ source={{ uri: res.add }}
350
+ />
351
+ </TouchableOpacity>
352
+ </View>
353
+ );
354
+ }}
355
+ />
356
+ </View>
357
+ <Spacer />
358
+ <ColorTempAdjustView
359
+ isSupportBrightness={moduleParams.isSupportBrightness}
360
+ isSupportTemperature={moduleParams.isSupportTemperature}
361
+ colorTemp={state.mood.mainLamp.nodes[state.mainNodeIdx].colorTemp}
362
+ brightness={state.mood.mainLamp.nodes[state.mainNodeIdx].brightness}
363
+ onBrightnessChangeComplete={bright => {
364
+ state.mood.mainLamp.nodes.forEach((node, idx) => {
365
+ if (state.mainBucketSelected) {
366
+ node.brightness = bright;
367
+ }
368
+ if (state.mainNodeIdx === idx) {
369
+ node.brightness = bright;
370
+ }
371
+ });
372
+ }}
373
+ onCCTChangeComplete={cct => {
374
+ state.mood.mainLamp.nodes.forEach((node, idx) => {
375
+ if (state.mainBucketSelected) {
376
+ node.colorTemp = cct;
377
+ }
378
+ if (state.mainNodeIdx === idx) {
379
+ node.colorTemp = cct;
380
+ }
381
+ });
382
+ }}
383
+ />
384
+ <Spacer height={cx(10)} />
385
+ </>
386
+ )}
387
+ </Card>
388
+ <Card style={styles.adjustCard}>
389
+ <LdvSwitch
390
+ title={I18n.getLang('light_sources_tile_sec_lighting_headline')}
391
+ color={'#fff'}
392
+ colorAlpha={1}
393
+ enable={!!state.mood.secondaryLamp.enable}
394
+ setEnable={v => {
395
+ state.mood.secondaryLamp.enable = v;
396
+ if (v && !state.mood.secondaryLamp.nodes.length) {
397
+ state.mood.secondaryLamp.nodes.push(
398
+ {
399
+ h: 0,
400
+ s: 100,
401
+ v: 100,
402
+ brightness: 0,
403
+ colorTemp: 0,
404
+ isColorNode: true,
405
+ },
406
+ {
407
+ h: 0,
408
+ s: 100,
409
+ v: 100,
410
+ brightness: 0,
411
+ colorTemp: 0,
412
+ isColorNode: true,
413
+ }
414
+ );
415
+ state.mood.secondaryLamp.mode = MoodNodeTransitionMode.Gradient;
416
+ state.mood.secondaryLamp.speed = 75;
417
+ state.secondaryIdx = 1;
418
+ state.secondaryNode = state.mood.secondaryLamp.nodes[1];
419
+ }
420
+ }}
421
+ showSwitch={!!moduleParams.isMixLight}
422
+ />
423
+ {state.mood.secondaryLamp.enable && (
424
+ <>
425
+ <TextFieldStyleButton
426
+ style={styles.transitionMode}
427
+ text={state.sceneMode[state.mood.secondaryLamp.mode]?.title}
428
+ placeholder={I18n.getLang('add_new_dynamic_mood_color_changing_mode_headline')}
429
+ onPress={() => {
430
+ const paramsSelect: SelectPageParams<number> = {
431
+ title: I18n.getLang('add_new_dynamic_mood_color_changing_mode_headline'),
432
+ data: createSelectModeData(
433
+ state.mood.secondaryLamp.mode,
434
+ state.sceneMode
435
+ ),
436
+ onSelect: selectPageData => {
437
+ state.mood.secondaryLamp.mode = selectPageData.value;
438
+ },
439
+ };
440
+ navigation.navigate(ui_biz_routerKey.ui_biz_select_page, paramsSelect);
441
+ }}
442
+ />
443
+ <Spacer height={cx(10)} />
444
+ <LdvSlider
445
+ title={I18n.getLang('add_new_dynamic_mood_lights_field_speed_topic_text')}
446
+ value={state.mood.secondaryLamp.speed}
447
+ onValueChange={() => {}}
448
+ onSlidingComplete={value => {
449
+ state.mood.secondaryLamp.speed = value;
450
+ }}
451
+ />
452
+ <Spacer height={cx(16)} />
453
+ {state.sceneMode[state.mood.secondaryLamp.mode]?.turnOn && (
454
+ <View style={styles.transitionMode}>
455
+ <Segmented
456
+ value={state.mood.secondaryLamp.direction}
457
+ options={[
458
+ {
459
+ label: I18n.getLang('add_new_dynamic_mood_strip_lights_switch_tab_text1'),
460
+ value: 0,
461
+ },
462
+ {
463
+ label: I18n.getLang('add_new_dynamic_mood_strip_lights_switch_tab_text2'),
464
+ value: 1,
465
+ },
466
+ ]}
467
+ onChange={v => (state.mood.secondaryLamp.direction = Number(v))}
468
+ />
469
+ <Spacer />
470
+ </View>
471
+ )}
472
+ {state.sceneMode[state.mood.secondaryLamp.mode]?.paragraph && (
473
+ <View style={styles.transitionMode}>
474
+ <Segmented
475
+ value={state.mood.secondaryLamp.segmented}
476
+ options={[
477
+ {
478
+ label: I18n.getLang('add_new_dynamic_mood_strip_lights_switch_tab_text3'),
479
+ value: 0,
480
+ },
481
+ {
482
+ label: I18n.getLang('add_new_dynamic_mood_strip_lights_switch_tab_text4'),
483
+ value: 1,
484
+ },
485
+ ]}
486
+ onChange={v => (state.mood.secondaryLamp.segmented = Number(v))}
487
+ />
488
+ <Spacer />
489
+ </View>
490
+ )}
491
+ {state.sceneMode[state.mood.secondaryLamp.mode]?.other && (
492
+ <>
493
+ <TextFieldStyleButton
494
+ style={styles.transitionMode}
495
+ text={getSelectOther(
496
+ state.sceneMode[state.mood.secondaryLamp.mode]?.other,
497
+ state.mood.secondaryLamp.expand
498
+ )}
499
+ placeholder={I18n.getLang(
500
+ 'add_new_dynamic_mood_strip_lights_selectionfield2_topic_text'
501
+ )}
502
+ onPress={() => {
503
+ const paramsSelect: SelectPageParams<number> = {
504
+ title: I18n.getLang(
505
+ 'add_new_dynamic_mood_strip_lights_selectionfield2_topic_text'
506
+ ),
507
+ data: createSelectOtherData(
508
+ state.sceneMode[state.mood.secondaryLamp.mode]?.other,
509
+ state.mood.secondaryLamp.expand
510
+ ),
511
+ onSelect: selectPageData => {
512
+ state.mood.secondaryLamp.expand = selectPageData.value;
513
+ },
514
+ };
515
+ navigation.navigate(ui_biz_routerKey.ui_biz_select_page, paramsSelect);
516
+ }}
517
+ />
518
+ <Spacer />
519
+ </>
520
+ )}
521
+ <View style={styles.nodesAdjust}>
522
+ <View style={styles.adjustButtons}>
523
+ <TouchableOpacity
524
+ onPress={() => {
525
+ state.secondaryBucketSelected = true;
526
+ }}
527
+ >
528
+ <Image
529
+ style={[
530
+ styles.adjustButton,
531
+ { tintColor: state.secondaryBucketSelected ? '#f60' : '#666' },
532
+ ]}
533
+ source={res.ic_paint_bucket}
534
+ />
535
+ </TouchableOpacity>
536
+ <TouchableOpacity
537
+ onPress={() => {
538
+ state.secondaryBucketSelected = false;
539
+ }}
540
+ >
541
+ <Image
542
+ style={[
543
+ styles.adjustButton,
544
+ { tintColor: state.secondaryBucketSelected ? '#666' : '#f60' },
545
+ ]}
546
+ source={res.ic_colorize}
547
+ />
548
+ </TouchableOpacity>
549
+ </View>
550
+ <FlatList
551
+ data={state.mood.secondaryLamp.nodes}
552
+ style={styles.nodeList}
553
+ renderItem={({ item, index }) => {
554
+ return (
555
+ <View style={styles.nodeItem}>
556
+ <TouchableOpacity
557
+ style={[
558
+ styles.nodeBlock,
559
+ {
560
+ backgroundColor: getNodeColor(item),
561
+ },
562
+ ]}
563
+ onPress={() => {
564
+ state.secondaryIdx = index;
565
+ }}
566
+ />
567
+ <TouchableOpacity
568
+ style={styles.nodeDeleteBtn}
569
+ disabled={state.mood.secondaryLamp.nodes.length < 3}
570
+ onPress={() => {
571
+ state.mood.secondaryLamp.nodes.splice(index, 1);
572
+ state.secondaryIdx = state.mood.secondaryLamp.nodes.length - 1;
573
+ }}
574
+ >
575
+ <Image
576
+ style={[
577
+ styles.nodeDeleteIcon,
578
+ {
579
+ tintColor:
580
+ state.mood.secondaryLamp.nodes.length < 3 ? '#ccc' : '#666',
581
+ },
582
+ ]}
583
+ source={res.ic_mood_del}
584
+ />
585
+ </TouchableOpacity>
586
+ </View>
587
+ );
588
+ }}
589
+ keyExtractor={(_, index) => `${index}`}
590
+ ItemSeparatorComponent={() => <Spacer height={cx(12)} />}
591
+ ListFooterComponent={() => {
592
+ if (state.mood.secondaryLamp.nodes.length >= 8) {
593
+ return <></>;
594
+ }
595
+ return (
596
+ <View>
597
+ <Spacer height={cx(12)} />
598
+ <TouchableOpacity
599
+ style={styles.nodeAddBtn}
600
+ onPress={() => {
601
+ const node = {
602
+ ...state.mood.secondaryLamp.nodes[state.secondaryIdx],
603
+ };
604
+ state.mood.secondaryLamp.nodes.push(node);
605
+ state.secondaryIdx = state.mood.secondaryLamp.nodes.length - 1;
606
+ }}
607
+ >
608
+ <Image
609
+ style={{
610
+ width: cx(18),
611
+ height: cx(18),
612
+ tintColor: '#000',
613
+ }}
614
+ source={{ uri: res.add }}
615
+ />
616
+ </TouchableOpacity>
617
+ </View>
618
+ );
619
+ }}
620
+ />
621
+ </View>
622
+ <Spacer />
623
+ <View style={styles.lightLine}>
624
+ <Text style={styles.light}>
625
+ {I18n.getLang('add_new_dynamic_mood_lights_field_headline2_text')}
626
+ </Text>
627
+ <View
628
+ style={[
629
+ styles.preview,
630
+ { backgroundColor: getColorBlockColor(state.secondaryNode) },
631
+ ]}
632
+ />
633
+ </View>
634
+ <Spacer />
635
+ <ColorAdjustView
636
+ h={state.secondaryNode.h}
637
+ s={state.secondaryNode.s}
638
+ v={state.secondaryNode.v}
639
+ reserveSV={true}
640
+ onHSVChange={() => {}}
641
+ onHSVChangeComplete={(h, s, v) => {
642
+ state.mood.secondaryLamp.nodes.forEach((node, idx) => {
643
+ if (state.secondaryBucketSelected) {
644
+ node.h = h;
645
+ node.s = s;
646
+ node.v = v;
647
+ }
648
+ if (state.secondaryIdx === idx) {
649
+ node.h = h;
650
+ node.s = s;
651
+ node.v = v;
652
+ }
653
+ });
654
+ }}
655
+ />
656
+ <Spacer height={cx(10)} />
657
+ </>
658
+ )}
659
+ </Card>
660
+
661
+ {params.mode === 'edit' && (
662
+ <View style={{ marginTop: cx(20), marginHorizontal: cx(24) }}>
663
+ <TextButton
664
+ style={styles.deleteBtn}
665
+ textStyle={styles.deleteBtnText}
666
+ text={Strings.getLang('edit_static_mood_button_delete_text')}
667
+ onPress={() => {
668
+ showDialog({
669
+ method: 'confirm',
670
+ title: I18n.getLang('string_light_pp_dialog_sm_ed_headline_d'),
671
+ subTitle: I18n.getLang(`strip_light_static_mood_edit_dialog_text`),
672
+ onConfirm: async (_, {close})=>{
673
+ close();
674
+ state.loading = true;
675
+ const res = await params.modDeleteMood('del', state.mood);
676
+ state.loading = false;
677
+ if (res.success) {
678
+ navigation.navigate(ui_biz_routerKey.ui_biz_mood);
679
+ }
680
+ }
681
+ })
682
+ }}
683
+ />
684
+ </View>
685
+ )}
686
+ <Spacer />
687
+ </View>
688
+ </ScrollView>
689
+ </Page>
690
+ );
691
+ };
692
+ const styles = StyleSheet.create({
693
+ root: {
694
+ flex: 1,
695
+ flexDirection: 'column',
696
+ },
697
+ name: {
698
+ marginHorizontal: cx(24),
699
+ },
700
+ adjustCard: {
701
+ marginVertical: cx(12),
702
+ marginHorizontal: cx(24),
703
+ },
704
+ fanAdjustCard: {
705
+ marginHorizontal: cx(24),
706
+ },
707
+ lightLine: {
708
+ flexDirection: 'row',
709
+ marginHorizontal: cx(16),
710
+ },
711
+ light: {
712
+ color: '#000',
713
+ fontSize: cx(18),
714
+ fontFamily: 'helvetica_neue_lt_std_bd',
715
+ },
716
+ transitionMode: {
717
+ marginHorizontal: cx(16),
718
+ },
719
+ preview: {
720
+ width: cx(20),
721
+ height: cx(20),
722
+ marginStart: cx(12),
723
+ borderRadius: cx(4),
724
+ },
725
+ nodesAdjust: {
726
+ flexDirection: 'row',
727
+ alignItems: 'center',
728
+ },
729
+ adjustButtons: {
730
+ width: cx(44),
731
+ marginStart: cx(16),
732
+ },
733
+ adjustButton: {
734
+ width: cx(44),
735
+ height: cx(44),
736
+ },
737
+ nodeList: {
738
+ flex: 1,
739
+ marginHorizontal: cx(16),
740
+ },
741
+ nodeItem: {
742
+ flexDirection: 'row',
743
+ alignItems: 'center',
744
+ },
745
+ nodeBlock: {
746
+ flex: 1,
747
+ height: cx(40),
748
+ borderRadius: cx(8),
749
+ },
750
+ nodeDeleteBtn: {
751
+ width: cx(24),
752
+ height: cx(30),
753
+ justifyContent: 'center',
754
+ alignItems: 'center',
755
+ },
756
+ nodeDeleteIcon: {
757
+ width: cx(16),
758
+ height: cx(16),
759
+ },
760
+ nodeAddBtn: {
761
+ height: cx(40),
762
+ justifyContent: 'center',
763
+ alignItems: 'center',
764
+ marginEnd: cx(26),
765
+ borderRadius: cx(8),
766
+ borderWidth: cx(1),
767
+ borderStyle: 'dashed',
768
+ borderColor: '#666',
769
+ backgroundColor: '#f6f6f6',
770
+ },
771
+ deleteBtn: {
772
+ width: '100%',
773
+ height: cx(50),
774
+ backgroundColor: '#666',
775
+ borderRadius: cx(8),
776
+ },
777
+ deleteBtnText: {
778
+ color: '#fff',
779
+ fontSize: cx(16),
780
+ fontFamily: 'helvetica_neue_lt_std_bd',
781
+ },
782
+ });
783
+ export default MixDynamicMoodEditorPage;
784
+ export function getTransitionModeString(transitionMode: MoodNodeTransitionMode): string {
785
+ if (transitionMode === MoodNodeTransitionMode.Jump) {
786
+ return Strings.getLang('other_lights_modes_jump_text');
787
+ }
788
+ return Strings.getLang('other_lights_modes_gradient_text');
789
+ }