@ledvance/ui-biz-bundle 1.1.105 → 1.1.107
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.
- package/package.json +1 -1
- package/src/hooks/DeviceDpStateHooks.ts +156 -0
- package/src/modules/flags/FlagActions.ts +4 -1
- package/src/modules/flags/FlagPage.tsx +12 -42
- package/src/modules/music/MusicPage.tsx +13 -7
- package/src/modules/timer/TimerPage.tsx +4 -1
- package/src/modules/timer/TimerPageAction.ts +0 -1
- package/src/navigation/Routers.ts +3 -1
- package/src/newModules/biorhythm/BiorhythmBean.ts +1 -1
- package/src/newModules/biorhythm/BiorhythmPage.tsx +27 -4
- package/src/newModules/diyScene/DefaultScenes.ts +438 -0
- package/src/newModules/diyScene/DiySceneActions.ts +232 -0
- package/src/newModules/diyScene/DiySceneEditorPage.tsx +359 -0
- package/src/newModules/diyScene/DiyScenePage.tsx +297 -0
- package/src/newModules/diyScene/Router.ts +25 -0
- package/src/newModules/energyConsumption/EnergyConsumptionActions.ts +15 -2
- package/src/newModules/energyConsumption/EnergyConsumptionChart.tsx +31 -10
- package/src/newModules/energyConsumption/component/DateSwitch.tsx +111 -0
- package/src/newModules/energyConsumption/component/DateTypeItem.tsx +1 -0
- package/src/newModules/energyConsumption/component/NewBarChart.tsx +16 -3
- package/src/newModules/fixedTime/FixedTimeActions.ts +3 -3
- package/src/newModules/fixedTime/FixedTimePage.tsx +58 -6
- package/src/newModules/mood/MoodActions.ts +4 -1
- package/src/newModules/mood/MoodItem.tsx +2 -1
- package/src/newModules/mood/MoodPage.tsx +4 -3
- package/src/newModules/randomTime/RandomTimeActions.ts +3 -3
- package/src/newModules/randomTime/RandomTimePage.tsx +60 -7
- package/src/newModules/sleepWakeUp/SleepWakeUpActions.ts +21 -11
- package/src/newModules/sleepWakeUp/SleepWakeUpPage.tsx +108 -13
- package/src/newModules/switchGradient/SwitchGradientPage.tsx +7 -4
- package/src/newModules/swithInching/SwithInching.tsx +1 -0
- package/src/newModules/timeSchedule/Interface.ts +19 -7
- package/src/newModules/timeSchedule/TimeScheduleActions.ts +6 -0
- package/src/newModules/timeSchedule/TimeScheduleDetailPage.tsx +134 -57
- package/src/newModules/timeSchedule/TimeSchedulePage.tsx +23 -6
- package/src/newModules/timeSchedule/components/ManuaSettings.tsx +42 -9
- package/src/newModules/timeSchedule/components/ScheduleCard.tsx +5 -1
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import React, {useEffect, useMemo} from 'react';
|
|
2
|
+
import {Image, ScrollView, StyleSheet, TouchableOpacity, View} from 'react-native';
|
|
3
|
+
import {cloneDeep, isEqual} from 'lodash';
|
|
4
|
+
import {useReactive} from 'ahooks';
|
|
5
|
+
import Page from '@ledvance/base/src/components/Page';
|
|
6
|
+
import I18n from '@ledvance/base/src/i18n';
|
|
7
|
+
import {useNavigation} from '@react-navigation/native';
|
|
8
|
+
import TextField from '@ledvance/base/src/components/TextField';
|
|
9
|
+
import {Utils} from 'tuya-panel-kit';
|
|
10
|
+
import Card from '@ledvance/base/src/components/Card';
|
|
11
|
+
import Spacer from '@ledvance/base/src/components/Spacer';
|
|
12
|
+
import res from '@ledvance/base/src/res';
|
|
13
|
+
import TextButton from '@ledvance/base/src/components/TextButton';
|
|
14
|
+
import {useParams} from '@ledvance/base/src/hooks/Hooks';
|
|
15
|
+
import LdvSwitch from '@ledvance/base/src/components/ldvSwitch';
|
|
16
|
+
import {showDialog} from '@ledvance/base/src/utils/common';
|
|
17
|
+
import ColorAdjustView from '@ledvance/base/src/components/ColorAdjustView';
|
|
18
|
+
import ThemeType from '@ledvance/base/src/config/themeType'
|
|
19
|
+
import DiySceneNodeView from "@ledvance/base/src/components/DiySceneNodeView";
|
|
20
|
+
import { useLoveScenes} from "./DiySceneActions";
|
|
21
|
+
import LdvSlider from "@ledvance/base/src/components/ldvSlider";
|
|
22
|
+
import {Result} from "@ledvance/base/src/models/modules/Result";
|
|
23
|
+
import {ui_biz_routerKey} from "../../navigation/Routers";
|
|
24
|
+
import {DiySceneInfo} from "@ledvance/base/src/utils/interface";
|
|
25
|
+
|
|
26
|
+
const cx = Utils.RatioUtils.convertX;
|
|
27
|
+
const { withTheme } = Utils.ThemeUtils
|
|
28
|
+
|
|
29
|
+
export interface DiySceneEditorPageParams {
|
|
30
|
+
mode: 'add' | 'edit'
|
|
31
|
+
currentScene: DiySceneInfo
|
|
32
|
+
nameRepeat: (mood: DiySceneInfo) => boolean
|
|
33
|
+
sceneInfoOperation: (mode: 'add' | 'edit' | 'del', currentScene?: DiySceneInfo) => Promise<Result<any>>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface DiySceneEditorPageState {
|
|
37
|
+
headline: string;
|
|
38
|
+
h: number
|
|
39
|
+
s: number
|
|
40
|
+
v: number
|
|
41
|
+
sceneInfo: DiySceneInfo;
|
|
42
|
+
loading: boolean;
|
|
43
|
+
currentNode: number,
|
|
44
|
+
position: 'Top' | 'Bottom'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const DiySceneEditorPage = (props: { theme?: ThemeType }) => {
|
|
48
|
+
const navigation = useNavigation();
|
|
49
|
+
const routeParams = useParams<DiySceneEditorPageParams>();
|
|
50
|
+
const params = cloneDeep(routeParams);
|
|
51
|
+
const [loveScenes, setLoveScenes] = useLoveScenes()
|
|
52
|
+
const state = useReactive<DiySceneEditorPageState>({
|
|
53
|
+
headline: '',
|
|
54
|
+
h: 359,
|
|
55
|
+
s: 100,
|
|
56
|
+
v: 100,
|
|
57
|
+
sceneInfo: params.currentScene,
|
|
58
|
+
loading: false,
|
|
59
|
+
currentNode: 0,
|
|
60
|
+
position: 'Top'
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
state.headline = I18n.getLang(
|
|
65
|
+
params.mode === 'add' ? 'string_light_pp_sm_headline_add' : 'edit_static_mood_headline_text'
|
|
66
|
+
);
|
|
67
|
+
}, [params.mode]);
|
|
68
|
+
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (state.sceneInfo.nodes.length > 0) {
|
|
71
|
+
const color = state.sceneInfo.nodes[0].top;
|
|
72
|
+
state.h = color.h
|
|
73
|
+
state.s = Math.round(color.s / 10)
|
|
74
|
+
state.v = Math.round((state.sceneInfo.v || 1000) / 10)
|
|
75
|
+
}
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
const onRightClick = async () => {
|
|
79
|
+
if (state.loading || !canSaveMoodData) return;
|
|
80
|
+
state.loading = true;
|
|
81
|
+
const newScene: DiySceneInfo = {
|
|
82
|
+
...state.sceneInfo
|
|
83
|
+
};
|
|
84
|
+
const res = await params.sceneInfoOperation(params.mode, newScene);
|
|
85
|
+
state.loading = false;
|
|
86
|
+
if (res.success) {
|
|
87
|
+
navigation.navigate(ui_biz_routerKey.ui_biz_diy_scene_page);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const nameRepeat = useMemo(() => {
|
|
92
|
+
return params.nameRepeat(state.sceneInfo)
|
|
93
|
+
}, [state.sceneInfo.name]);
|
|
94
|
+
|
|
95
|
+
const isLove = useMemo(() => {
|
|
96
|
+
return loveScenes.some(it => it === state.sceneInfo.id)
|
|
97
|
+
}, [loveScenes]);
|
|
98
|
+
|
|
99
|
+
const checkSceneChanged = useMemo(() => {
|
|
100
|
+
return isEqual(state.sceneInfo, params.currentScene)
|
|
101
|
+
}, [JSON.stringify(state.sceneInfo), {}])
|
|
102
|
+
|
|
103
|
+
const canSaveMoodData = useMemo(() => {
|
|
104
|
+
return state.sceneInfo.name.length > 0 && state.sceneInfo.name.length < 33 && !nameRepeat && (params.mode === 'add' || !checkSceneChanged)
|
|
105
|
+
}, [nameRepeat, state.sceneInfo.name, checkSceneChanged, params.currentScene])
|
|
106
|
+
|
|
107
|
+
const toggleLoveScene = (id: number) => {
|
|
108
|
+
const newLoveScenes = loveScenes
|
|
109
|
+
const index = newLoveScenes.indexOf(id)
|
|
110
|
+
if (index !== -1) {
|
|
111
|
+
if (newLoveScenes.length <= 2) {
|
|
112
|
+
showLoveNumAlert()
|
|
113
|
+
} else {
|
|
114
|
+
newLoveScenes.splice(index, 1)
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
if (newLoveScenes.length >= 14) {
|
|
118
|
+
showLoveNumAlert()
|
|
119
|
+
} else {
|
|
120
|
+
newLoveScenes.push(id)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
setLoveScenes(newLoveScenes).then()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const showLoveNumAlert = () => {
|
|
127
|
+
showDialog({
|
|
128
|
+
method: 'alert',
|
|
129
|
+
title: I18n.getLang('love_mood_alert_title'),
|
|
130
|
+
subTitle: I18n.getLang('love_mood_alert_content'),
|
|
131
|
+
onConfirm: async (_, { close }) => {
|
|
132
|
+
close()
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
const styles = StyleSheet.create({
|
|
139
|
+
root: {
|
|
140
|
+
flex: 1,
|
|
141
|
+
flexDirection: 'column',
|
|
142
|
+
},
|
|
143
|
+
name: {
|
|
144
|
+
marginHorizontal: cx(24),
|
|
145
|
+
},
|
|
146
|
+
nodeCard: {
|
|
147
|
+
marginHorizontal: cx(24),
|
|
148
|
+
},
|
|
149
|
+
nodeOperation: {
|
|
150
|
+
flexDirection: 'row',
|
|
151
|
+
justifyContent: 'flex-end',
|
|
152
|
+
marginTop: cx(12),
|
|
153
|
+
paddingHorizontal: cx(12),
|
|
154
|
+
},
|
|
155
|
+
nodeList: {
|
|
156
|
+
display: 'flex',
|
|
157
|
+
flexDirection: 'row',
|
|
158
|
+
justifyContent: 'flex-start',
|
|
159
|
+
flexWrap: 'wrap',
|
|
160
|
+
},
|
|
161
|
+
adjustCard: {
|
|
162
|
+
marginTop: cx(12),
|
|
163
|
+
marginHorizontal: cx(24),
|
|
164
|
+
},
|
|
165
|
+
fanAdjustCard: {
|
|
166
|
+
marginHorizontal: cx(24),
|
|
167
|
+
},
|
|
168
|
+
preview: {
|
|
169
|
+
width: cx(20),
|
|
170
|
+
height: cx(20),
|
|
171
|
+
marginStart: cx(12),
|
|
172
|
+
borderRadius: cx(4),
|
|
173
|
+
},
|
|
174
|
+
deleteBtn: {
|
|
175
|
+
width: '100%',
|
|
176
|
+
height: cx(50),
|
|
177
|
+
backgroundColor: props.theme?.button.delete,
|
|
178
|
+
borderRadius: cx(8),
|
|
179
|
+
},
|
|
180
|
+
deleteBtnText: {
|
|
181
|
+
color: props.theme?.button.fontColor,
|
|
182
|
+
fontSize: cx(16),
|
|
183
|
+
fontFamily: 'helvetica_neue_lt_std_bd',
|
|
184
|
+
},
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<Page
|
|
189
|
+
backText={I18n.getLang('mesh_device_detail_mode')}
|
|
190
|
+
showBackDialog={!checkSceneChanged}
|
|
191
|
+
backDialogTitle={I18n.getLang(
|
|
192
|
+
params.mode === 'add'
|
|
193
|
+
? 'string_light_pp_dialog_sm_add_headline_c'
|
|
194
|
+
: 'manage_user_unsaved_changes_dialog_headline'
|
|
195
|
+
)}
|
|
196
|
+
backDialogContent={I18n.getLang(
|
|
197
|
+
params.mode === 'add'
|
|
198
|
+
? 'strip_light_static_mood_add_step_2_dialog_text'
|
|
199
|
+
: 'strip_light_static_mood_editor_step_2_dialog_text'
|
|
200
|
+
)}
|
|
201
|
+
headlineText={state.headline}
|
|
202
|
+
headlineIconContent={
|
|
203
|
+
state.sceneInfo.type !== 'DIY' && <TouchableOpacity onPress={() => {
|
|
204
|
+
toggleLoveScene(state.sceneInfo.id)
|
|
205
|
+
}}>
|
|
206
|
+
{isLove ? <Image source={res.like} style={{width: cx(24), height: cx(24)}}/>
|
|
207
|
+
: <Image source={res.un_like}
|
|
208
|
+
style={{width: cx(24), height: cx(24), tintColor: props.theme?.global.fontColor}}/>}
|
|
209
|
+
</TouchableOpacity>
|
|
210
|
+
}
|
|
211
|
+
rightButtonIcon={canSaveMoodData ? res.ic_check : res.ic_uncheck}
|
|
212
|
+
rightButtonIconClick={onRightClick}
|
|
213
|
+
loading={state.loading}
|
|
214
|
+
>
|
|
215
|
+
<ScrollView style={{ flex: 1 }} nestedScrollEnabled={true}>
|
|
216
|
+
<View style={styles.root}>
|
|
217
|
+
<TextField
|
|
218
|
+
style={styles.name}
|
|
219
|
+
value={state.sceneInfo.name}
|
|
220
|
+
placeholder={I18n.getLang('edit_static_mood_inputfield_topic_text')}
|
|
221
|
+
onChangeText={text => {
|
|
222
|
+
state.sceneInfo.name = text;
|
|
223
|
+
}}
|
|
224
|
+
maxLength={33}
|
|
225
|
+
showError={state.sceneInfo.name.length > 32 || nameRepeat}
|
|
226
|
+
tipColor={nameRepeat ? props.theme?.global.error : undefined}
|
|
227
|
+
tipIcon={nameRepeat ? res.ic_text_field_input_error : undefined}
|
|
228
|
+
errorText={I18n.getLang(
|
|
229
|
+
nameRepeat ? 'string_light_pp_field_sm_add_error1' : 'add_new_dynamic_mood_alert_text'
|
|
230
|
+
)}
|
|
231
|
+
/>
|
|
232
|
+
{
|
|
233
|
+
state.sceneInfo.type === 'DIY' && <Card style={styles.nodeCard}>
|
|
234
|
+
<View style={styles.nodeOperation}>
|
|
235
|
+
{state.sceneInfo.nodes.length < 8 && <TouchableOpacity onPress={() => {
|
|
236
|
+
console.log('onPress add node')
|
|
237
|
+
const color = {h: state.h, s: state.s * 10, v: 1000}
|
|
238
|
+
state.sceneInfo.nodes.push({top: color, bottom: color});
|
|
239
|
+
}}>
|
|
240
|
+
<Image source={res.ic_plus} style={{tintColor: props.theme?.icon.primary}} />
|
|
241
|
+
</TouchableOpacity>}
|
|
242
|
+
{ state.sceneInfo.nodes.length > 2 && <TouchableOpacity onPress={() => {
|
|
243
|
+
console.log('onPress delete node')
|
|
244
|
+
state.sceneInfo.nodes.splice(state.currentNode, 1)
|
|
245
|
+
state.currentNode = state.currentNode === state.sceneInfo.nodes.length ? 0 : state.currentNode
|
|
246
|
+
console.log('nextNode', state.currentNode)
|
|
247
|
+
}}>
|
|
248
|
+
<Image source={res.ic_mood_del} style={{marginLeft: cx(5),tintColor: props.theme?.icon.primary}} />
|
|
249
|
+
</TouchableOpacity>}
|
|
250
|
+
</View>
|
|
251
|
+
<View style={styles.nodeList}>
|
|
252
|
+
{state.sceneInfo.nodes.map((node, index) => (
|
|
253
|
+
<DiySceneNodeView
|
|
254
|
+
key={index}
|
|
255
|
+
isCurrent={state.currentNode === index}
|
|
256
|
+
position={state.position}
|
|
257
|
+
topColor={node.top}
|
|
258
|
+
bottomColor={node.bottom}
|
|
259
|
+
onChangePosition={(position: 'Top' | 'Bottom') => {
|
|
260
|
+
state.currentNode = index
|
|
261
|
+
state.position = position
|
|
262
|
+
const color = state.sceneInfo.nodes[index];
|
|
263
|
+
state.h = position === 'Top' && color.top.h || color.bottom.h
|
|
264
|
+
state.s = Math.round((position === 'Top' && color.top.s || color.bottom.s) / 10)
|
|
265
|
+
}}
|
|
266
|
+
/>
|
|
267
|
+
))}
|
|
268
|
+
</View>
|
|
269
|
+
</Card>
|
|
270
|
+
}
|
|
271
|
+
<Card style={styles.adjustCard}>
|
|
272
|
+
<LdvSwitch
|
|
273
|
+
title={I18n.getLang('light_sources_tile_tw_lighting_headline')}
|
|
274
|
+
color={''}
|
|
275
|
+
colorAlpha={1}
|
|
276
|
+
enable={false}
|
|
277
|
+
setEnable={() => {}}
|
|
278
|
+
showSwitch={false}
|
|
279
|
+
/>
|
|
280
|
+
{
|
|
281
|
+
state.sceneInfo.type === 'DIY' && <ColorAdjustView
|
|
282
|
+
reserveSV={true}
|
|
283
|
+
h={state.h}
|
|
284
|
+
s={state.s}
|
|
285
|
+
v={state.v}
|
|
286
|
+
onHSVChange={() => {}}
|
|
287
|
+
onHSVChangeComplete={(h, s, v) => {
|
|
288
|
+
state.h = h
|
|
289
|
+
state.s = s
|
|
290
|
+
state.v = v
|
|
291
|
+
state.sceneInfo.v = v * 10
|
|
292
|
+
const node = state.sceneInfo.nodes && state.sceneInfo.nodes[state.currentNode]
|
|
293
|
+
if (node) {
|
|
294
|
+
const color = { h: h, s: s * 10, v: 1000 }
|
|
295
|
+
state.position === 'Top' && (node.top = color) || (node.bottom = color)
|
|
296
|
+
}
|
|
297
|
+
}} />
|
|
298
|
+
}
|
|
299
|
+
{
|
|
300
|
+
state.sceneInfo.type !== 'DIY' && <LdvSlider
|
|
301
|
+
title={I18n.getLang('light_sources_tile_rgb_lighting_brightness')}
|
|
302
|
+
min={1}
|
|
303
|
+
value={Math.round((state.sceneInfo.v || 1000) / 10)}
|
|
304
|
+
onValueChange={(v: number) => {state.sceneInfo.v = v * 10}}
|
|
305
|
+
onSlidingComplete={(v: number) => {state.sceneInfo.v = v * 10}}/>
|
|
306
|
+
}
|
|
307
|
+
<Spacer />
|
|
308
|
+
</Card>
|
|
309
|
+
|
|
310
|
+
{
|
|
311
|
+
state.sceneInfo.type !== 'Static' && <Card style={styles.adjustCard}>
|
|
312
|
+
<LdvSlider
|
|
313
|
+
title={I18n.getLang('add_new_dynamic_mood_ceiling_fan_field_text')}
|
|
314
|
+
value={state.sceneInfo.speed || 1}
|
|
315
|
+
min={1}
|
|
316
|
+
max={100}
|
|
317
|
+
onValueChange={(v: number) => {
|
|
318
|
+
state.sceneInfo.speed = v
|
|
319
|
+
}}
|
|
320
|
+
onSlidingComplete={(v: number) => {
|
|
321
|
+
state.sceneInfo.speed = v
|
|
322
|
+
}}
|
|
323
|
+
subTitleStr={`${(state.sceneInfo.speed || 1)}/100`}/>
|
|
324
|
+
</Card>
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
{(params.mode === 'edit' && state.sceneInfo.type === 'DIY') && (
|
|
328
|
+
<View style={{ marginTop: cx(20), marginHorizontal: cx(24) }}>
|
|
329
|
+
<TextButton
|
|
330
|
+
style={styles.deleteBtn}
|
|
331
|
+
textStyle={styles.deleteBtnText}
|
|
332
|
+
text={I18n.getLang('edit_static_mood_button_delete_text')}
|
|
333
|
+
onPress={() => {
|
|
334
|
+
showDialog({
|
|
335
|
+
method: 'confirm',
|
|
336
|
+
title: I18n.getLang('string_light_pp_dialog_sm_ed_headline_d'),
|
|
337
|
+
subTitle: I18n.getLang(`strip_light_static_mood_edit_dialog_text`),
|
|
338
|
+
onConfirm: async (_, { close }) => {
|
|
339
|
+
close();
|
|
340
|
+
state.loading = true;
|
|
341
|
+
const res = await params.sceneInfoOperation('del', state.sceneInfo);
|
|
342
|
+
state.loading = false;
|
|
343
|
+
if (res.success) {
|
|
344
|
+
navigation.navigate(ui_biz_routerKey.ui_biz_diy_scene_page);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
})
|
|
348
|
+
}}
|
|
349
|
+
/>
|
|
350
|
+
</View>
|
|
351
|
+
)}
|
|
352
|
+
<Spacer />
|
|
353
|
+
</View>
|
|
354
|
+
</ScrollView>
|
|
355
|
+
</Page>
|
|
356
|
+
);
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
export default withTheme(DiySceneEditorPage)
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import React, {useCallback, useEffect, useMemo} from 'react';
|
|
2
|
+
import Page from '@ledvance/base/src/components/Page';
|
|
3
|
+
import {Utils} from 'tuya-panel-kit';
|
|
4
|
+
import {useDeviceId, useDeviceInfo, useMoods,} from '@ledvance/base/src/models/modules/NativePropsSlice';
|
|
5
|
+
import {useReactive} from 'ahooks';
|
|
6
|
+
import I18n from '@ledvance/base/src/i18n';
|
|
7
|
+
import res from '@ledvance/base/src/res';
|
|
8
|
+
import {FlatList, Image, Platform, StyleSheet, TouchableOpacity, View} from 'react-native';
|
|
9
|
+
import Tag from '@ledvance/base/src/components/Tag';
|
|
10
|
+
import Spacer from '@ledvance/base/src/components/Spacer';
|
|
11
|
+
import InfoText from '@ledvance/base/src/components/InfoText';
|
|
12
|
+
import {useNavigation} from '@react-navigation/core';
|
|
13
|
+
import {showDialog} from '@ledvance/base/src/utils/common';
|
|
14
|
+
import ThemeType from '@ledvance/base/src/config/themeType'
|
|
15
|
+
import DiySceneItem from "@ledvance/base/src/components/DiySceneItem";
|
|
16
|
+
import {
|
|
17
|
+
getMixSceneList,
|
|
18
|
+
saveMixSceneList,
|
|
19
|
+
useDiySceneId,
|
|
20
|
+
useSceneData,
|
|
21
|
+
useSceneId,
|
|
22
|
+
useSceneStatus,
|
|
23
|
+
useSwitchLed,
|
|
24
|
+
useWorkMode
|
|
25
|
+
} from "./DiySceneActions";
|
|
26
|
+
import {cloneDeep, difference, head, range} from "lodash";
|
|
27
|
+
import {DiySceneInfo, SceneStatusType, WorkMode} from "@ledvance/base/src/utils/interface";
|
|
28
|
+
import {ui_biz_routerKey} from "../../navigation/Routers";
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
const cx = Utils.RatioUtils.convertX;
|
|
32
|
+
const { withTheme } = Utils.ThemeUtils
|
|
33
|
+
|
|
34
|
+
const MAX_MOOD_COUNT = 255;
|
|
35
|
+
|
|
36
|
+
const DiyScenePage = (props: { theme?: ThemeType }) => {
|
|
37
|
+
const deviceInfo = useDeviceInfo();
|
|
38
|
+
const devId = useDeviceId();
|
|
39
|
+
const [switchLed] = useSwitchLed()
|
|
40
|
+
const [workMode, setWorkMode] = useWorkMode()
|
|
41
|
+
const [sceneStatusId, setSceneStatus] = useSceneStatus()
|
|
42
|
+
const [sceneId] = useSceneId()
|
|
43
|
+
const [diySceneId] = useDiySceneId()
|
|
44
|
+
const [setSceneData] = useSceneData()
|
|
45
|
+
const [scenes, setScenes] = useMoods()
|
|
46
|
+
const navigation = useNavigation();
|
|
47
|
+
const DEFAULT_SCENE: DiySceneInfo = {
|
|
48
|
+
id: -1,
|
|
49
|
+
type: 'DIY',
|
|
50
|
+
name: '',
|
|
51
|
+
speed: 50,
|
|
52
|
+
v: 1000,
|
|
53
|
+
nodes: [{top: {h: 359, s: 1000, v: 1000}, bottom: {h: 120, s: 1000, v: 1000}},
|
|
54
|
+
{top: {h: 359, s: 1000, v: 1000}, bottom: {h: 120, s: 1000, v: 1000}}]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const state = useReactive({
|
|
58
|
+
currentMood: undefined,
|
|
59
|
+
staticTagChecked: true,
|
|
60
|
+
dynamicTagChecked: true,
|
|
61
|
+
diyTagChecked: true,
|
|
62
|
+
showAddMoodPopover: false,
|
|
63
|
+
originScenes: cloneDeep(scenes) as DiySceneInfo[],
|
|
64
|
+
filterMoods: [] as DiySceneInfo[],
|
|
65
|
+
loading: false,
|
|
66
|
+
timerId: undefined as any,
|
|
67
|
+
flag: Symbol(),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const filterScenes = useMemo(() => {
|
|
71
|
+
return state.originScenes.filter(item => {
|
|
72
|
+
return [state.staticTagChecked, state.dynamicTagChecked, state.diyTagChecked].every(it => !it)
|
|
73
|
+
|| (state.staticTagChecked && item.type === 'Static')
|
|
74
|
+
|| (state.dynamicTagChecked && item.type === 'Dynamic')
|
|
75
|
+
|| (state.diyTagChecked && item.type === 'DIY')
|
|
76
|
+
})
|
|
77
|
+
}, [JSON.stringify(state.originScenes), state.staticTagChecked, state.dynamicTagChecked, state.diyTagChecked])
|
|
78
|
+
|
|
79
|
+
const newSceneId = useMemo(() => {
|
|
80
|
+
const existsIds = state.originScenes.filter(it => it.type === 'DIY').map(it => it.id)
|
|
81
|
+
const idRange = range(1, 256);
|
|
82
|
+
const newId: number = head(difference(idRange, existsIds)) || 0;
|
|
83
|
+
return newId
|
|
84
|
+
}, [state.originScenes])
|
|
85
|
+
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (sceneId) {
|
|
88
|
+
setSceneStatus(SceneStatusType.Scene, sceneId).then()
|
|
89
|
+
}
|
|
90
|
+
}, [sceneId])
|
|
91
|
+
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (diySceneId) {
|
|
94
|
+
setSceneStatus(SceneStatusType.DIY, diySceneId).then()
|
|
95
|
+
}
|
|
96
|
+
}, [diySceneId])
|
|
97
|
+
|
|
98
|
+
const getRemoteMixSceneInfo = async (isRefresh?: boolean) => {
|
|
99
|
+
state.loading = true
|
|
100
|
+
const res = await getMixSceneList(devId, isRefresh)
|
|
101
|
+
state.loading = false
|
|
102
|
+
if (res.success && Array.isArray(res.data)) {
|
|
103
|
+
setScenes(res.data)
|
|
104
|
+
state.originScenes = cloneDeep(res.data);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const navigationRoute = (mode: 'add' | 'edit' | 'del', currentScene?: DiySceneInfo) => {
|
|
109
|
+
navigation.navigate(ui_biz_routerKey.ui_biz_diy_scene_edit_page, {
|
|
110
|
+
mode,
|
|
111
|
+
currentScene,
|
|
112
|
+
nameRepeat,
|
|
113
|
+
sceneInfoOperation,
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const getItemEnable = useCallback(
|
|
118
|
+
(sceneInfo: DiySceneInfo) => {
|
|
119
|
+
if (sceneStatusId !== -1) {
|
|
120
|
+
return sceneStatusId === sceneInfo.id && workMode === WorkMode.Scene && switchLed
|
|
121
|
+
}
|
|
122
|
+
return false
|
|
123
|
+
},
|
|
124
|
+
[workMode, switchLed, sceneStatusId]
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const nameRepeat = useCallback((scene: DiySceneInfo) => {
|
|
128
|
+
return !!state.originScenes.filter(m => m.id !== scene.id).find(m => m.name === scene.name)
|
|
129
|
+
}, [state.originScenes])
|
|
130
|
+
|
|
131
|
+
const sceneInfoOperation = async (mode: 'add' | 'edit' | 'del' | 'set', currentScene: DiySceneInfo) => {
|
|
132
|
+
const checkedScene: DiySceneInfo = {
|
|
133
|
+
...currentScene
|
|
134
|
+
};
|
|
135
|
+
if (mode === 'set') {
|
|
136
|
+
await setSceneStatus(checkedScene.type === 'DIY' ? SceneStatusType.DIY : SceneStatusType.Scene, checkedScene.id)
|
|
137
|
+
return setSceneData(checkedScene);
|
|
138
|
+
}
|
|
139
|
+
let newSceneList: DiySceneInfo[]
|
|
140
|
+
if (mode === 'add') {
|
|
141
|
+
checkedScene.id = newSceneId
|
|
142
|
+
newSceneList = [checkedScene, ...state.originScenes]
|
|
143
|
+
} else if (mode === 'del') {
|
|
144
|
+
newSceneList = state.originScenes.filter(item => item.id !== checkedScene.id)
|
|
145
|
+
} else {
|
|
146
|
+
newSceneList = state.originScenes.map(item => {
|
|
147
|
+
if (item.id === checkedScene.id) {
|
|
148
|
+
return checkedScene
|
|
149
|
+
}
|
|
150
|
+
return item
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
const scene = mode === 'del' ? (newSceneList.length === 0 ? undefined : newSceneList[0]) : checkedScene
|
|
154
|
+
const res = await saveMixSceneList(devId, newSceneList)
|
|
155
|
+
if (res.success) {
|
|
156
|
+
state.originScenes = cloneDeep(newSceneList);
|
|
157
|
+
setScenes(cloneDeep(newSceneList))
|
|
158
|
+
if (scene) {
|
|
159
|
+
if (mode === 'del') {
|
|
160
|
+
if (currentScene.id !== sceneStatusId || !switchLed) {
|
|
161
|
+
return {
|
|
162
|
+
success: true,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
setSceneData(checkedScene).then(() => {
|
|
167
|
+
setSceneStatus(checkedScene.type === 'DIY' ? SceneStatusType.DIY : SceneStatusType.Scene, checkedScene.id).then()
|
|
168
|
+
})
|
|
169
|
+
} else {
|
|
170
|
+
if (workMode === WorkMode.Scene) {
|
|
171
|
+
setWorkMode(WorkMode.Colour).then()
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
success: true,
|
|
176
|
+
};
|
|
177
|
+
} else {
|
|
178
|
+
return {
|
|
179
|
+
success: false,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const styles = StyleSheet.create({
|
|
185
|
+
tagLine: {
|
|
186
|
+
flexDirection: 'row',
|
|
187
|
+
marginHorizontal: cx(24),
|
|
188
|
+
},
|
|
189
|
+
infoLine: {
|
|
190
|
+
marginHorizontal: cx(24),
|
|
191
|
+
},
|
|
192
|
+
addMoodPopover: {
|
|
193
|
+
position: 'absolute',
|
|
194
|
+
right: cx(60),
|
|
195
|
+
top: Platform.OS === 'android' ? cx(90) : cx(130),
|
|
196
|
+
maxWidth: cx(200),
|
|
197
|
+
backgroundColor: props.theme?.card.background,
|
|
198
|
+
},
|
|
199
|
+
popoverItem: {
|
|
200
|
+
padding: cx(5),
|
|
201
|
+
alignItems: 'flex-start',
|
|
202
|
+
alignSelf: 'flex-start'
|
|
203
|
+
},
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<>
|
|
208
|
+
<Page
|
|
209
|
+
backText={deviceInfo.name}
|
|
210
|
+
headlineText={I18n.getLang('mood_overview_headline_text')}
|
|
211
|
+
headlineIcon={state.originScenes.length < MAX_MOOD_COUNT ? res.add : undefined}
|
|
212
|
+
onHeadlineIconClick={() => {
|
|
213
|
+
navigationRoute('add', cloneDeep(DEFAULT_SCENE))
|
|
214
|
+
}}
|
|
215
|
+
loading={state.loading}
|
|
216
|
+
>
|
|
217
|
+
<View style={styles.tagLine}>
|
|
218
|
+
<Tag
|
|
219
|
+
checked={state.staticTagChecked}
|
|
220
|
+
text={I18n.getLang('mood_overview_filter_name_text1')}
|
|
221
|
+
onCheckedChange={checked => {
|
|
222
|
+
state.staticTagChecked = checked;
|
|
223
|
+
}}
|
|
224
|
+
/>
|
|
225
|
+
<Spacer width={cx(8)} height={0} />
|
|
226
|
+
<Tag
|
|
227
|
+
checked={state.dynamicTagChecked}
|
|
228
|
+
text={I18n.getLang('mood_overview_filter_name_text2')}
|
|
229
|
+
onCheckedChange={checked => {
|
|
230
|
+
state.dynamicTagChecked = checked;
|
|
231
|
+
}}
|
|
232
|
+
/>
|
|
233
|
+
<Spacer width={cx(8)} height={0} />
|
|
234
|
+
<Tag
|
|
235
|
+
checked={state.diyTagChecked}
|
|
236
|
+
text={I18n.getLang('mood_overview_field_chip_diy')}
|
|
237
|
+
onCheckedChange={checked => {
|
|
238
|
+
state.diyTagChecked = checked;
|
|
239
|
+
}}
|
|
240
|
+
/>
|
|
241
|
+
</View>
|
|
242
|
+
<TouchableOpacity style={{ alignItems: 'flex-end',paddingRight: cx(24) }}
|
|
243
|
+
onPress={() => {
|
|
244
|
+
showDialog({
|
|
245
|
+
method: 'confirm',
|
|
246
|
+
title: I18n.getLang('mood_resetbutton'),
|
|
247
|
+
subTitle: I18n.getLang('reset_mooddescription'),
|
|
248
|
+
onConfirm: async (_, { close }) => {
|
|
249
|
+
close()
|
|
250
|
+
await getRemoteMixSceneInfo(true)
|
|
251
|
+
}
|
|
252
|
+
})
|
|
253
|
+
}}
|
|
254
|
+
>
|
|
255
|
+
<Image source={res.ic_refresh} style={{ width: cx(24), height: cx(24), tintColor: props.theme?.global.fontColor }} />
|
|
256
|
+
</TouchableOpacity>
|
|
257
|
+
<Spacer height={cx(10)} />
|
|
258
|
+
{state.originScenes.length >= MAX_MOOD_COUNT && (
|
|
259
|
+
<View style={styles.infoLine}>
|
|
260
|
+
<Spacer height={cx(10)} />
|
|
261
|
+
<InfoText
|
|
262
|
+
icon={res.ic_warning_amber}
|
|
263
|
+
text={I18n.getLang('mood_overview_warning_max_number_text')}
|
|
264
|
+
contentColor={props.theme?.global.warning}
|
|
265
|
+
/>
|
|
266
|
+
<Spacer height={cx(6)} />
|
|
267
|
+
</View>
|
|
268
|
+
)}
|
|
269
|
+
<FlatList
|
|
270
|
+
data={filterScenes}
|
|
271
|
+
renderItem={({ item }) => {
|
|
272
|
+
return (
|
|
273
|
+
<DiySceneItem
|
|
274
|
+
enable={getItemEnable(item)}
|
|
275
|
+
scene={item}
|
|
276
|
+
onPress={() => {
|
|
277
|
+
navigationRoute('edit', item)
|
|
278
|
+
}}
|
|
279
|
+
onSwitch={async _ => {
|
|
280
|
+
state.loading = true;
|
|
281
|
+
await sceneInfoOperation('set', item);
|
|
282
|
+
state.loading = false;
|
|
283
|
+
}}
|
|
284
|
+
/>
|
|
285
|
+
);
|
|
286
|
+
}}
|
|
287
|
+
ListHeaderComponent={() => <Spacer height={cx(10)} />}
|
|
288
|
+
ItemSeparatorComponent={() => <Spacer />}
|
|
289
|
+
ListFooterComponent={() => <Spacer />}
|
|
290
|
+
keyExtractor={item => `${item.name}`}
|
|
291
|
+
/>
|
|
292
|
+
</Page>
|
|
293
|
+
</>
|
|
294
|
+
);
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
export default withTheme(DiyScenePage)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {NavigationRoute} from "tuya-panel-kit";
|
|
2
|
+
import {ui_biz_routerKey} from "../../navigation/Routers";
|
|
3
|
+
import DiyScenePage from "./DiyScenePage";
|
|
4
|
+
import DiySceneEditorPage from "./DiySceneEditorPage";
|
|
5
|
+
|
|
6
|
+
const DiyScenePageRouters: NavigationRoute[] = [
|
|
7
|
+
{
|
|
8
|
+
name: ui_biz_routerKey.ui_biz_diy_scene_page,
|
|
9
|
+
component: DiyScenePage,
|
|
10
|
+
options: {
|
|
11
|
+
hideTopbar: true,
|
|
12
|
+
showOfflineView: false,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: ui_biz_routerKey.ui_biz_diy_scene_edit_page,
|
|
17
|
+
component: DiySceneEditorPage,
|
|
18
|
+
options: {
|
|
19
|
+
hideTopbar: true,
|
|
20
|
+
showOfflineView: false,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
export default DiyScenePageRouters
|