@ledvance/ui-biz-bundle 1.1.35 → 1.1.37

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.
@@ -0,0 +1,133 @@
1
+ import React, { useMemo } from 'react'
2
+ import Card from '@ledvance/base/src/components/Card'
3
+ import { SwitchButton, Utils } from 'tuya-panel-kit'
4
+ import { Image, StyleSheet, Text, View, ViewProps, ViewStyle } from 'react-native'
5
+ import { hsv2Hex, mapFloatToRange } from '@ledvance/base/src/utils'
6
+ import { cctToColor } from '@ledvance/base/src/utils/cctUtils'
7
+ import Spacer from '@ledvance/base/src/components/Spacer'
8
+ import I18n from '@ledvance/base/src/i18n'
9
+ import MoodColorsLine from '@ledvance/base/src/components/MoodColorsLine'
10
+ import res from '@ledvance/base/src/res'
11
+ import { MixSceneInfo, MixMainLampInfo } from './MixSceneBeans'
12
+
13
+ const cx = Utils.RatioUtils.convertX
14
+
15
+ interface MixMoodItemProps extends ViewProps {
16
+ enable: boolean
17
+ mixMood: MixSceneInfo
18
+ onPress?: () => void
19
+ onSwitch: (enable: boolean) => void
20
+ style?: ViewStyle
21
+ }
22
+
23
+
24
+ const MixMoodItem = (props: MixMoodItemProps) => {
25
+ const { mixMood } = props
26
+ const isStaticMood = useMemo(() => {
27
+ return mixMood.secondlyLamp.nodes?.length === 0
28
+ }, [mixMood])
29
+
30
+ return (
31
+ <Card style={[styles.card, props.style]} onPress={props.onPress}>
32
+ <View>
33
+ <Spacer height={cx(16)} />
34
+ <View style={styles.headline}>
35
+ <Text style={styles.headText}>{mixMood.name}</Text>
36
+ <SwitchButton value={props.enable} onValueChange={props.onSwitch} thumbStyle={{ elevation: 0 }} />
37
+ </View>
38
+ <Spacer />
39
+ <MixMoodColorsLine mixSubLight={mixMood.mainLamp} colorLight={false} />
40
+ {!isStaticMood && <>
41
+ <Spacer height={cx(7)} />
42
+ <MixMoodColorsLine mixSubLight={mixMood.secondlyLamp} colorLight={true} />
43
+ </>}
44
+ <Spacer height={cx(12)} />
45
+ <View style={styles.moodTypeItem}>
46
+ <View style={styles.moodTypeLabel}>
47
+ <Text style={styles.moodTypeLabelText}>
48
+ {I18n.getLang(isStaticMood ? 'mood_overview_field_chip_text' : 'mood_overview_field_chip_2')}
49
+ </Text>
50
+ </View>
51
+ </View>
52
+ <Spacer height={cx(16)} />
53
+ </View>
54
+ </Card>
55
+ )
56
+ }
57
+
58
+ export default MixMoodItem
59
+
60
+ export function MixMoodColorsLine(props: { mixSubLight: MixMainLampInfo, colorLight: boolean }) {
61
+ const { mixSubLight, colorLight } = props
62
+ const lightColors = mixSubLight.nodes?.map(n => {
63
+ return colorLight ? hsv2Hex(n.h, Math.round(n.s), Math.round(mapFloatToRange(n.v / 100, 50, 100)))
64
+ : cctToColor(n.colorTemp.toFixed())
65
+ })
66
+ return (
67
+ <View style={styles.gradientItem}>
68
+ <Spacer height={0} width={cx(16)} />
69
+ <MoodColorsLine
70
+ width={cx(264)}
71
+ type={'separate'}
72
+ colors={lightColors} />
73
+ <Spacer height={0} width={cx(7)} />
74
+ <View style={styles.gradientItemIconView}>
75
+ <Image style={styles.gradientItemIcon} source={res.light_on} />
76
+ </View>
77
+ <Spacer height={0} width={cx(16)} />
78
+ </View>
79
+ )
80
+ }
81
+
82
+ const styles = StyleSheet.create({
83
+ card: {
84
+ marginHorizontal: cx(24),
85
+ },
86
+ headline: {
87
+ flexDirection: 'row',
88
+ marginHorizontal: cx(16),
89
+ },
90
+ headText: {
91
+ flex: 1,
92
+ color: '#000',
93
+ fontSize: cx(16),
94
+ fontFamily: 'helvetica_neue_lt_std_bd',
95
+ lineHeight: cx(20),
96
+ },
97
+ gradientItem: {
98
+ flexDirection: 'row',
99
+ },
100
+ gradientItemIconView: {
101
+ width: cx(24),
102
+ height: cx(24),
103
+ justifyContent: 'center',
104
+ alignItems: 'center',
105
+ backgroundColor: '#aaa',
106
+ borderRadius: cx(8),
107
+ },
108
+ gradientItemIcon: {
109
+ width: cx(16),
110
+ height: cx(16),
111
+ tintColor: '#fff',
112
+ },
113
+ gradient: {
114
+ borderRadius: cx(8),
115
+ },
116
+ moodTypeItem: {
117
+ flexDirection: 'row',
118
+ },
119
+ moodTypeLabel: {
120
+ marginStart: cx(16),
121
+ paddingHorizontal: cx(12.5),
122
+ backgroundColor: '#E6E7E8',
123
+ borderRadius: cx(8),
124
+ },
125
+ moodTypeLabelText: {
126
+ height: cx(16),
127
+ color: '#000000DD',
128
+ fontSize: cx(10),
129
+ textAlignVertical: 'center',
130
+ fontFamily: 'helvetica_neue_lt_std_roman',
131
+ lineHeight: cx(16),
132
+ },
133
+ })
@@ -0,0 +1,318 @@
1
+ import React, { useCallback, useEffect } from 'react'
2
+ import { FlatList, StyleSheet, View, Platform } from 'react-native'
3
+ import { Utils } from 'tuya-panel-kit'
4
+ import Page from '@ledvance/base/src/components/Page'
5
+ import { useScene, useFantasyScene } from '../../scene/SceneAction'
6
+ import { getRemoteMixScene, saveMixMood } from './MixMoodActions'
7
+ import { useWorkMode } from '../../../hooks/DeviceDpStateHooks'
8
+ import { useDeviceInfo, useFlagMode } from '@ledvance/base/src/models/modules/NativePropsSlice'
9
+ import { useReactive, useUpdateEffect } from 'ahooks'
10
+ import Strings from '@ledvance/base/src/i18n'
11
+ import res from '@ledvance/base/src/res'
12
+ import Tag from '@ledvance/base/src/components/Tag'
13
+ import Spacer from '@ledvance/base/src/components/Spacer'
14
+ import InfoText from '@ledvance/base/src/components/InfoText'
15
+ import CustomListDialog from '@ledvance/base/src/components/CustomListDialog'
16
+ import { MixSceneInfo, MixMainLampInfo } from './MixSceneBeans'
17
+ import { SceneInfo } from '../../scene/SceneInfo'
18
+ import MixMoodItem from './MixMoodItem'
19
+ import { useNavigation, useRoute } from '@react-navigation/core'
20
+ import { saveFlagMode } from '@ledvance/ui-biz-bundle/src/modules/flags/FlagActions'
21
+ import { ui_biz_routerKey } from '../../../navigation/Routers'
22
+ import { WORK_MODE } from '@tuya/tuya-panel-lamp-sdk/lib/utils'
23
+ import { cloneDeep } from 'lodash'
24
+
25
+ const { convertX: cx } = Utils.RatioUtils
26
+
27
+ interface MixMoodPageUIState {
28
+ staticTagChecked: boolean
29
+ dynamicTagChecked: boolean
30
+ showAddMoodPopover: boolean
31
+ currentScene?: MixSceneInfo
32
+ scenes: MixSceneInfo[]
33
+ filteredMoods: MixSceneInfo[]
34
+ loading: boolean
35
+ flag: Symbol
36
+ }
37
+
38
+ export interface MixMoodPageProps {
39
+ switchLedDpCode: string // whiteSwitch
40
+ sceneDpCode: string
41
+ workModeDpCode: string
42
+ rgbcSwitchLed: string
43
+ fantasySceneDpCode: string
44
+ rgbcWorkModeDpCode: string
45
+ isCeilingLight: boolean
46
+ isSupportColor: boolean
47
+ isSupportTemperature: boolean
48
+ isSupportBrightness: boolean
49
+ }
50
+
51
+ const MAX_MOOD_COUNT = 256
52
+
53
+ const MixMoodPage = () => {
54
+ const routeParams = useRoute().params as MixMoodPageProps
55
+ const params = {
56
+ ...routeParams,
57
+ }
58
+ const [whiteSceneId, setScene] = useScene(params.sceneDpCode)
59
+ const [fantasyId, setFantasyScene] = useFantasyScene(params.fantasySceneDpCode, params.rgbcWorkModeDpCode, params.rgbcSwitchLed)
60
+ const [workMode, setWorkMode] = useWorkMode(params.workModeDpCode)
61
+ const [rgbcWorkMode, setRgbcWorkMode] = useWorkMode(params.rgbcWorkModeDpCode || '')
62
+ const [flagMode, setFlagMode] = useFlagMode()
63
+ const deviceInfo = useDeviceInfo()
64
+ const navigation = useNavigation()
65
+
66
+ const state = useReactive<MixMoodPageUIState>({
67
+ currentScene: undefined,
68
+ staticTagChecked: true,
69
+ dynamicTagChecked: true,
70
+ showAddMoodPopover: false,
71
+ scenes: [],
72
+ flag: Symbol(),
73
+ filteredMoods: [],
74
+ loading: false,
75
+ })
76
+
77
+ const getSceneList = useCallback(async (mainId: number, secondlyId: number) => {
78
+ setTimeout(async () => {
79
+ const res = await getRemoteMixScene(deviceInfo.devId)
80
+ if (res.success) {
81
+ state.scenes = res.data || []
82
+ state.currentScene = state.scenes.find(scene => {
83
+ const { mainLamp, secondlyLamp } = scene
84
+ return (mainLamp.id === mainId) && (secondlyLamp.id === secondlyId)
85
+ })
86
+ }
87
+ }, 400)
88
+ }, [])
89
+
90
+ const onAddMoodDialogItemClick = useCallback((isStatic: boolean, _: number) => {
91
+ navigation.navigate(ui_biz_routerKey.ui_biz_mix_mood_add, {
92
+ isStatic,
93
+ mixMoods: cloneDeep(state.scenes),
94
+ moduleParams: params,
95
+ modDeleteFlag,
96
+ })
97
+ state.showAddMoodPopover = false
98
+ }, [state.scenes])
99
+
100
+ useEffect(() => {
101
+ state.filteredMoods = state.scenes.filter(item => {
102
+ return (state.staticTagChecked && state.dynamicTagChecked) ||
103
+ (!state.staticTagChecked && !state.dynamicTagChecked) ||
104
+ (state.staticTagChecked && item.mainLamp.nodes.length < 2) ||
105
+ (state.dynamicTagChecked && item.secondlyLamp.nodes.length > 1)
106
+ })
107
+ }, [state.staticTagChecked, state.dynamicTagChecked, state.scenes])
108
+
109
+ useUpdateEffect(() => {
110
+ state.currentScene = state.scenes.find(scene => {
111
+ const { mainLamp, secondlyLamp } = scene
112
+ return (mainLamp.id === whiteSceneId) && (secondlyLamp.id === fantasyId)
113
+ })
114
+ }, [whiteSceneId, fantasyId])
115
+
116
+ useEffect(() => {
117
+ getSceneList(whiteSceneId, fantasyId).then()
118
+ }, [state.flag])
119
+
120
+
121
+ const updateFlagMode = () => {
122
+ if (flagMode?.flagMode && params.isSupportColor) {
123
+ saveFlagMode(deviceInfo.devId, JSON.stringify({
124
+ flagMode: false,
125
+ flagId: undefined
126
+ })).then()
127
+ setFlagMode({
128
+ flagMode: false,
129
+ flagId: undefined
130
+ })
131
+ }
132
+ }
133
+
134
+ const getSceneByMix = (mixMood: MixMainLampInfo): SceneInfo => {
135
+ return {
136
+ ...mixMood,
137
+ nodes: mixMood.nodes.map((item) => ({
138
+ ...item,
139
+ switchingInterval: mixMood.speed,
140
+ transitionTime: mixMood.speed,
141
+ transitionMode: mixMood.mode,
142
+ isColorNode: false
143
+ }))
144
+ }
145
+ }
146
+
147
+ const checkItemEnable = useCallback((mixMood: MixSceneInfo) => {
148
+ const { mainLamp, secondlyLamp } = mixMood
149
+ const isStatic = mainLamp.nodes.length === 1
150
+ return (workMode === WORK_MODE.SCENE || rgbcWorkMode === WORK_MODE.SCENE) &&
151
+ isStatic ? ((whiteSceneId === mainLamp.id) && mainLamp.id !== -1) :
152
+ (((whiteSceneId === mainLamp.id) && mainLamp.id !== -1) && ((fantasyId === secondlyLamp.id) && secondlyLamp.id !== -1))
153
+ }, [workMode, rgbcWorkMode, fantasyId, whiteSceneId])
154
+
155
+ const modDeleteFlag = async (mode: 'add' | 'edit' | 'del', currentMood: MixSceneInfo) => {
156
+ const list = mode === 'add' ? [currentMood, ...state.scenes] :
157
+ mode === 'del' ? state.scenes.filter(mood => (mood.mainLamp.id !== currentMood.mainLamp.id && mood.secondlyLamp.id !== currentMood.secondlyLamp.id)) :
158
+ state.scenes.map(mood => {
159
+ return ((mood.mainLamp.id === currentMood.mainLamp.id) && (mood.secondlyLamp.id === currentMood.secondlyLamp.id)) ? currentMood : mood
160
+ })
161
+ const saveRes = await saveMixMood(deviceInfo.devId, list)
162
+ if (saveRes.success) {
163
+ if (flagMode?.flagMode) {
164
+ saveFlagMode(deviceInfo.devId, JSON.stringify({
165
+ flagMode: false,
166
+ flagId: undefined
167
+ })).then()
168
+ setFlagMode({
169
+ flagMode: false,
170
+ flagId: undefined
171
+ })
172
+ }
173
+ if (list.length > 0) {
174
+ const { mainLamp, secondlyLamp } = currentMood
175
+ if (mode === 'del') {
176
+ if (whiteSceneId === mainLamp.id) {
177
+ setScene(deviceInfo.devId, getSceneByMix(list[0].mainLamp), params.sceneDpCode, params.workModeDpCode, params.switchLedDpCode, false, false).then()
178
+ }
179
+ if (fantasyId === secondlyLamp.id) {
180
+ setFantasyScene(list[0]?.secondlyLamp).then()
181
+ }
182
+ } else {
183
+ if (mainLamp.nodes.length > 0) {
184
+ setScene(deviceInfo.devId, getSceneByMix(mainLamp), params.sceneDpCode, params.workModeDpCode, params.switchLedDpCode, false, false).then()
185
+ }
186
+ if (secondlyLamp.nodes.length > 0) {
187
+ setFantasyScene(secondlyLamp).then()
188
+ }
189
+ }
190
+ } else {
191
+ await setWorkMode(WORK_MODE.WHITE)
192
+ await setRgbcWorkMode(WORK_MODE.COLOUR)
193
+ }
194
+ state.scenes = cloneDeep(list)
195
+ return { success: true }
196
+ }
197
+
198
+ return { success: false }
199
+ }
200
+
201
+ useEffect(() => {
202
+ console.log(state.filteredMoods, '< --- filterMoods')
203
+ }, [JSON.stringify(state.filteredMoods)])
204
+ return (
205
+ <>
206
+ <Page
207
+ backText={deviceInfo.name}
208
+ headlineText={Strings.getLang('mood_overview_headline_text')}
209
+ headlineIcon={state.scenes.length < MAX_MOOD_COUNT ? res.add : undefined}
210
+ onHeadlineIconClick={() => {
211
+ state.showAddMoodPopover = !state.showAddMoodPopover
212
+ }}
213
+ loading={state.loading}>
214
+ <View style={styles.tagLine}>
215
+ <Tag
216
+ checked={state.staticTagChecked}
217
+ text={Strings.getLang('mood_overview_filter_name_text1')}
218
+ onCheckedChange={checked => {
219
+ state.staticTagChecked = checked
220
+ }} />
221
+ <Spacer width={cx(8)} height={0} />
222
+ <Tag
223
+ checked={state.dynamicTagChecked}
224
+ text={Strings.getLang('mood_overview_filter_name_text2')}
225
+ onCheckedChange={checked => {
226
+ state.dynamicTagChecked = checked
227
+ }} />
228
+ </View>
229
+ <Spacer height={cx(10)} />
230
+ {
231
+ state.scenes.length >= MAX_MOOD_COUNT &&
232
+ <View style={styles.infoLine}>
233
+ <Spacer height={cx(10)} />
234
+ <InfoText
235
+ icon={res.ic_warning_amber}
236
+ text={Strings.getLang('mood_overview_warning_max_number_text')}
237
+ contentColor={'#ff9500'} />
238
+ <Spacer height={cx(6)} />
239
+ </View>
240
+ }
241
+ <FlatList
242
+ data={state.filteredMoods}
243
+ renderItem={({ item }) => {
244
+ return (
245
+ <MixMoodItem
246
+ enable={checkItemEnable(item)}
247
+ mixMood={item}
248
+ onPress={() => {
249
+ navigation.navigate(ui_biz_routerKey.ui_biz_mix_mood_edit, {
250
+ mode: 'edit',
251
+ isStatic: item.mainLamp.nodes?.length === 1,
252
+ currentMood: item,
253
+ moods: state.scenes,
254
+ moduleParams: params,
255
+ modDeleteFlag,
256
+ })
257
+ }}
258
+ onSwitch={async _ => {
259
+ if (checkItemEnable(item)) return
260
+ state.loading = true
261
+ await modDeleteFlag('edit', item)
262
+ state.currentScene = item
263
+ updateFlagMode()
264
+ state.loading = false
265
+ }}
266
+ />
267
+ )
268
+ }}
269
+ ListHeaderComponent={() => (<Spacer height={cx(10)} />)}
270
+ ItemSeparatorComponent={() => (<Spacer />)}
271
+ ListFooterComponent={() => (<Spacer />)}
272
+ keyExtractor={item => `${item.name}`} />
273
+ </Page>
274
+ <CustomListDialog
275
+ show={state.showAddMoodPopover}
276
+ style={styles.addMoodPopover}
277
+ itemStyle={styles.popoverItem}
278
+ onDismiss={() => {
279
+ state.showAddMoodPopover = false
280
+ }}
281
+ data={[
282
+ {
283
+ text: Strings.getLang('mood_overview_add_mood_text'),
284
+ value: true,
285
+ },
286
+ {
287
+ text: Strings.getLang('mood_overview_add_mood_text2'),
288
+ value: false,
289
+ },
290
+ ]}
291
+ onItemPress={onAddMoodDialogItemClick} />
292
+ </>
293
+ )
294
+ }
295
+
296
+
297
+ const styles = StyleSheet.create({
298
+ tagLine: {
299
+ flexDirection: 'row',
300
+ marginHorizontal: cx(24),
301
+ },
302
+ infoLine: {
303
+ marginHorizontal: cx(24),
304
+ },
305
+ addMoodPopover: {
306
+ position: 'absolute',
307
+ right: cx(60),
308
+ top: Platform.OS === 'android' ? cx(90) : cx(130),
309
+ maxWidth: cx(200),
310
+ backgroundColor: '#fff',
311
+ },
312
+ popoverItem: {
313
+ padding: cx(5),
314
+ alignItems: 'flex-start',
315
+ },
316
+ })
317
+
318
+ export default MixMoodPage