@ledvance/group-ui-biz-bundle 1.0.3 → 1.0.5

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 CHANGED
@@ -4,7 +4,7 @@
4
4
  "name": "@ledvance/group-ui-biz-bundle",
5
5
  "pid": [],
6
6
  "uiid": "",
7
- "version": "1.0.3",
7
+ "version": "1.0.5",
8
8
  "scripts": {},
9
9
  "dependencies": {
10
10
  "@ledvance/base": "^1.x",
@@ -0,0 +1,201 @@
1
+ import { useFeatureHook } from "@ledvance/base/src/models/modules/NativePropsSlice";
2
+ import { SceneNodeTransitionMode } from "@ledvance/group-ui-biz-bundle/src/modules/mood/SceneInfo";
3
+ import { parseJSON } from "@tuya/tuya-panel-lamp-sdk/lib/utils";
4
+ import { cloneDeep } from "lodash";
5
+ import { FlagItemInfo, FlagUiInfo, defFlagList } from "./FlagInfo";
6
+ import { hex2Int } from "@ledvance/base/src/utils/common";
7
+ import { spliceByStep } from "@ledvance/base/src/utils/common";
8
+ import { NativeApi } from "@ledvance/base/src/api/native";
9
+ import { hsv2Hex } from "@ledvance/base/src/utils";
10
+ import { WorkMode } from "@ledvance/base/src/utils/interface";
11
+
12
+ interface LightType {
13
+ isStringLight?: boolean
14
+ isStripLight?: boolean
15
+ isMixLight?: boolean
16
+ }
17
+
18
+ interface FlagDataType {
19
+ sceneStatus?: {
20
+ sceneDataDpCode: string
21
+ }
22
+ drawToolStatus?: {
23
+ drawToolCode: string
24
+ drawToolObj2dp: (colors: string[]) => string;
25
+ }
26
+ flag?: FlagItemInfo
27
+ workModeDpCode: string
28
+ workMode: WorkMode
29
+ switchLedDpCode: string
30
+ }
31
+
32
+ export interface FlagConfigType {
33
+ switchLed: boolean
34
+ workMode: WorkMode
35
+ flagSceneConfig?: FlagDataType
36
+ }
37
+ const featureId = 'Flag_data'
38
+
39
+ type UseFlagType = (params: LightType) => [FlagDataType | undefined, (flagData: FlagDataType) => Promise<any>]
40
+
41
+ export const useFlag: UseFlagType = (params) => {
42
+ return useFeatureHook<FlagConfigType, FlagDataType | undefined>('flagSceneConfig', undefined, undefined, (flagData) =>{
43
+ console.log(flagData, '< -- flagData')
44
+ if(flagData){
45
+ const dps = {}
46
+ const { flag, drawToolStatus, sceneStatus, switchLedDpCode, workMode, workModeDpCode} = flagData
47
+ if(params.isStripLight && drawToolStatus){
48
+ dps[drawToolStatus.drawToolCode] = flag ? drawToolStatus.drawToolObj2dp(flag.colors.map(hsv => hsv2Hex(hsv.h, hsv.s, hsv.v))) : ''
49
+ }else{
50
+ if(sceneStatus){
51
+ dps[sceneStatus.sceneDataDpCode] = flag ? obj2Dp(flag, {isMixLight: params.isMixLight}) : ''
52
+ }
53
+ }
54
+ dps[switchLedDpCode] = true
55
+ dps[workModeDpCode] = workMode
56
+ return dps
57
+ }
58
+ }, flagData => ({workMode: flagData?.workMode, switchLed: true}))
59
+ }
60
+
61
+ export interface FlagOption {
62
+ isFan?: boolean
63
+ isUVCFan?: boolean
64
+ isMixLight?: boolean
65
+ }
66
+
67
+ export function obj2Dp(flagItem: FlagItemInfo, option?: FlagOption): string {
68
+ let fanEnableHex = ''
69
+ let fanSpeedHex = ''
70
+ const version = '00'
71
+ const idHex = flagItem.id.toString(16).padStart(2, '0')
72
+ if (option?.isMixLight) {
73
+ const whiteEnable = '01'
74
+ const whiteNum = '01'
75
+ const whiteStatus = '02'
76
+ const whiteHex = whiteEnable + whiteNum + whiteStatus + flagItem.whiteColors.map(colors => {
77
+ const speed = '00'
78
+ const mode = '00'
79
+ const bHex = (colors.brightness * 10).toString(16).padStart(4, '0')
80
+ const tHex = (colors.colorTemp * 10).toString(16).padStart(4, '0')
81
+ return speed + speed + mode + bHex + tHex
82
+ }).join('')
83
+ const colorEnable = '01'
84
+ const colorNum = flagItem.colors.length.toString(16).padStart(2, '0')
85
+ const colorStatus = '03'
86
+ const colorHex = colorEnable + colorNum + colorStatus + flagItem.colors.map(item => {
87
+ const speed = flagItem.speed.toString(16).padStart(2, '0')
88
+ const mode = flagItem.mode.toString(16).padStart(2, '0')
89
+ const hHex = item.h.toString(16).padStart(4, '0')
90
+ const sHex = (item.s * 10).toString(16).padStart(4, '0')
91
+ const vHex = (item.v * 10).toString(16).padStart(4, '0')
92
+ return speed + speed + mode + hHex + sHex + vHex
93
+ }).join('')
94
+ return version + idHex + whiteHex + colorHex
95
+ } else {
96
+ if (option?.isFan) {
97
+ fanEnableHex = (flagItem.fanEnable ? 1 : 0).toString(16).padStart(2, '0')
98
+ fanSpeedHex = (flagItem.fanSpeed || 1).toString(16).padStart(2, '0')
99
+ }
100
+ const switchingIntervalHex = (option?.isUVCFan ? 101 - flagItem.speed : flagItem.speed).toString(16).padStart(2, '0')
101
+ const transitionTimeHex = (option?.isUVCFan ? 101 - flagItem.speed : flagItem.speed).toString(16).padStart(2, '0')
102
+ const transitionModeHex = flagItem.mode.toString(16).padStart(2, '0')
103
+ const hex = idHex + fanEnableHex + fanSpeedHex + flagItem.colors.map(color => {
104
+ const hHex = Math.round(color.h).toString(16).padStart(4, '0')
105
+ const sHex = Math.round(color.s * 10).toString(16).padStart(4, '0')
106
+ const vHex = Math.round(color.v * 10).toString(16).padStart(4, '0')
107
+ const brightnessHex = '0000'
108
+ const colorTempHex = '0000'
109
+ return switchingIntervalHex + transitionTimeHex + transitionModeHex + hHex + sHex + vHex + brightnessHex + colorTempHex
110
+ }).join('')
111
+ return hex
112
+ }
113
+ }
114
+
115
+ export function dp2Obj(dp: string, option?: FlagOption): FlagItemInfo | undefined {
116
+ if (!dp) return
117
+ const result = {} as FlagItemInfo
118
+ if (option?.isMixLight) {
119
+ result.version = hex2Int(dp.slice(0, 2))
120
+ result.id = hex2Int(dp.slice(2, 4))
121
+ let lampHex = dp.slice(16)
122
+ result.whiteColors = [
123
+ { brightness: hex2Int(lampHex.slice(0, 4)) / 10, colorTemp: hex2Int(lampHex.slice(4, 8)) / 10 }
124
+ ]
125
+ lampHex = lampHex.slice(14)
126
+ result.colors = spliceByStep(lampHex, 18).map(lamp => {
127
+ result.speed = hex2Int(lamp.slice(0, 2))
128
+ result.mode = hex2Int(lamp.slice(4, 6))
129
+ return {
130
+ h: hex2Int(lamp.slice(6, 10)),
131
+ s: hex2Int(lamp.slice(10, 14)) / 10,
132
+ v: hex2Int(lamp.slice(14, 18)) / 10
133
+ }
134
+ })
135
+ return result
136
+ } else {
137
+ const id = hex2Int(dp.slice(0, 2))
138
+ let fanEnable: any = undefined
139
+ let fanSpeed: any = undefined
140
+ if (option?.isFan) {
141
+ fanEnable = hex2Int(dp.slice(2, 4)) === 1
142
+ fanSpeed = hex2Int(dp.slice(4, 6))
143
+ }
144
+ const c = option?.isFan ? 6 : 2
145
+ const sliceDp = dp.slice(c)
146
+ let mode = SceneNodeTransitionMode.Jump
147
+ let speed = 70
148
+ const colors = spliceByStep(sliceDp, 26).map(nodeHex => {
149
+ speed = option?.isUVCFan ? 101 - hex2Int(nodeHex.slice(0, 2)) : hex2Int(nodeHex.slice(0, 2))
150
+ mode = hex2Int(nodeHex.slice(4, 6))
151
+ const h = hex2Int(nodeHex.slice(6, 10))
152
+ const s = hex2Int(nodeHex.slice(10, 14))
153
+ const v = hex2Int(nodeHex.slice(14, 18))
154
+ return {
155
+ h,
156
+ s: s / 10,
157
+ v: v / 10
158
+ }
159
+ })
160
+ const whiteColors = [{ brightness: 100, colorTemp: 0 }]
161
+ return {
162
+ id,
163
+ mode,
164
+ speed,
165
+ fanEnable,
166
+ fanSpeed,
167
+ colors,
168
+ whiteColors
169
+ }
170
+ }
171
+
172
+ }
173
+
174
+
175
+ export async function getRemoteFlag(devId: string) {
176
+ const res = await NativeApi.getJson(devId, featureId)
177
+ const isNormalData = Array.isArray(parseJSON(res?.data))
178
+ if (res.success && isNormalData) {
179
+ return {
180
+ success: true,
181
+ data: JSON.parse(res.data)
182
+ }
183
+ } else {
184
+ if (res.msg?.includes('资源未找到') || !isNormalData) {
185
+ const res = await NativeApi.putJson(devId, featureId, JSON.stringify(defFlagList))
186
+ if (res.success) {
187
+ return {
188
+ success: true,
189
+ data: cloneDeep(defFlagList)
190
+ }
191
+ }
192
+ return { success: false }
193
+ }
194
+ return { success: false }
195
+ }
196
+ }
197
+
198
+ export function saveFlag(devId: string, flagInfo: FlagUiInfo[]) {
199
+ return NativeApi.putJson(devId, featureId, JSON.stringify(flagInfo))
200
+ }
201
+
@@ -0,0 +1,431 @@
1
+ import React, { useCallback, useEffect, useMemo } from 'react'
2
+ import I18n from '@ledvance/base/src/i18n'
3
+ import { useNavigation, useRoute } from '@react-navigation/native'
4
+ import Page from '@ledvance/base/src/components/Page'
5
+ import res from '@ledvance/base/src/res'
6
+ import { ScrollView, View, FlatList, StyleSheet, Image, TouchableOpacity, Text } from 'react-native'
7
+ import Spacer from '@ledvance/base/src/components/Spacer'
8
+ import { useReactive } from 'ahooks'
9
+ import { Utils } from 'tuya-panel-kit'
10
+ import TextField from '@ledvance/base/src/components/TextField'
11
+ import Card from '@ledvance/base/src/components/Card'
12
+ import LdvSlider from '@ledvance/base/src/components/ldvSlider'
13
+ import TextButton from '@ledvance/base/src/components/TextButton'
14
+ import { hex2Hsv, hsv2Hex } from '@ledvance/base/src/utils'
15
+ import { cloneDeep, find, isEqual } from 'lodash'
16
+ import { FlagUiInfo } from './FlagInfo'
17
+ import ColorAdjustView from '@ledvance/base/src/components/ColorAdjustView'
18
+ import { Result } from '@ledvance/base/src/models/modules/Result'
19
+ import { FlagOption } from './FlagActions'
20
+ import { FlagPageProps } from './FlagPage'
21
+ import ColorTempAdjustView from '@ledvance/base/src/components/ColorTempAdjustView'
22
+ import { showDialog } from '@ledvance/base/src/utils/common'
23
+ import { cctToColor } from '@ledvance/base/src/utils/cctUtils'
24
+
25
+ const cx = Utils.RatioUtils.convertX
26
+
27
+ export interface FlagEditParams {
28
+ mode: 'add' | 'edit'
29
+ currentMood: FlagUiInfo
30
+ moods: FlagUiInfo[]
31
+ moduleParams: FlagPageProps
32
+ modDeleteFlag: (mode: 'add' | 'edit' | 'del', currentMood: FlagUiInfo, options?: FlagOption) => Promise<Result<any>>
33
+ }
34
+
35
+ const FlagEditPage = () => {
36
+ const navigation = useNavigation()
37
+ const params = cloneDeep(useRoute().params as FlagEditParams)
38
+ const state = useReactive({
39
+ showAddMoodPopover: true,
40
+ mood: params.currentMood,
41
+ currentWhiteNode: params.currentMood.whiteColors[params.currentMood.whiteColors.length - 1]!,
42
+ currentNode: params.currentMood.colors[params.currentMood.colors.length - 1],
43
+ whitePaintBucketIdx: params.currentMood.colors.length - 1,
44
+ colorPaintBucketIdx: params.currentMood.colors.length - 1,
45
+ whitePaintBucketSelected: true,
46
+ colorPaintBucketSelected: false,
47
+ loading: false
48
+ })
49
+ useEffect(() => {
50
+ }, [])
51
+
52
+ const getButtonStatus = () => {
53
+ return (params.mode === 'edit' && isEqual(state.mood, params.currentMood)) ||
54
+ !(!!state.mood.name) ||
55
+ nameRepeat ||
56
+ state.mood.name.length > 32
57
+ }
58
+
59
+ const getColorBlockColor = useCallback((isMainLight?: boolean) => {
60
+ if (isMainLight) {
61
+ return cctToColor(state.currentWhiteNode.colorTemp, Math.max(...[state.currentWhiteNode.brightness, 50]))
62
+ }
63
+ const { h, s, v } = state.currentNode
64
+ return hsv2Hex(h, s, v)
65
+ }, [state.currentNode, state.currentWhiteNode])
66
+
67
+ const nameRepeat = useMemo(() => {
68
+ return !!find(params.moods, m => (m.id !== state.mood.id && m.name === state.mood.name))
69
+ }, [state.mood.name])
70
+
71
+ return (
72
+ <Page
73
+ backText={I18n.getLang('mesh_device_detail_mode')}
74
+ showBackDialog={true}
75
+ loading={state.loading}
76
+ backDialogTitle={
77
+ I18n.getLang(params.mode === 'add' ?
78
+ 'flag_canceladding' :
79
+ 'flag_canceledit')
80
+ }
81
+ backDialogContent={
82
+ I18n.getLang('flag_cancelinfo')
83
+ }
84
+ headlineText={I18n.getLang(params.mode === 'add' ? 'flag_addanewflag' : 'flag_edittheflag')}
85
+ rightButtonIcon={getButtonStatus() ? res.ic_uncheck : res.ic_check}
86
+ rightButtonDisabled={getButtonStatus()}
87
+ rightButtonIconClick={async () => {
88
+ if (state.loading) return
89
+ state.loading = true
90
+ const res = await params.modDeleteFlag(params.mode, state.mood)
91
+ if (res.success) {
92
+ state.loading = false
93
+ navigation.goBack()
94
+ }
95
+ }}>
96
+ <ScrollView
97
+ style={{ flex: 1 }}
98
+ nestedScrollEnabled={true}>
99
+ <View style={styles.root}>
100
+ <TextField
101
+ style={styles.name}
102
+ value={state.mood.name}
103
+ placeholder={I18n.getLang('edit_static_mood_inputfield_topic_text')}
104
+ onChangeText={text => {
105
+ state.mood.name = text
106
+ }}
107
+ maxLength={33}
108
+ showError={state.mood.name.length > 32 || nameRepeat}
109
+ tipColor={nameRepeat ? '#f00' : undefined}
110
+ tipIcon={nameRepeat ? res.ic_text_field_input_error : undefined}
111
+ errorText={I18n.getLang(nameRepeat ? 'string_light_pp_field_sm_add_error1' : 'add_new_dynamic_mood_alert_text')} />
112
+ {params.moduleParams.isSupportMixScene && <><Card style={styles.adjustCard}>
113
+ <Spacer height={cx(16)} />
114
+ <View style={styles.lightLine}>
115
+ <Text style={styles.light}>
116
+ {I18n.getLang('light_sources_tile_main_lighting_headline')}
117
+ </Text>
118
+ <View style={[styles.preview, { backgroundColor: getColorBlockColor(true) }]} />
119
+ </View>
120
+ <Spacer />
121
+ <ColorTempAdjustView
122
+ isSupportBrightness={!!params.moduleParams.isSupportBrightness}
123
+ isSupportTemperature={!!params.moduleParams.isSupportTemperature}
124
+ colorTemp={state.currentWhiteNode.colorTemp}
125
+ brightness={state.currentWhiteNode.brightness}
126
+ onCCTChange={(cct) => {
127
+ state.currentWhiteNode.colorTemp = cct
128
+ }}
129
+ onCCTChangeComplete={(cct) => {
130
+ state.currentWhiteNode.colorTemp = cct
131
+ state.mood.whiteColors = state.mood.whiteColors.map((item, idx) => {
132
+ if (idx === state.whitePaintBucketIdx) {
133
+ return {
134
+ ...state.mood.whiteColors[idx],
135
+ colorTemp: cct
136
+ }
137
+ }
138
+ return item
139
+ })
140
+ }}
141
+ onBrightnessChange={() => { }}
142
+ onBrightnessChangeComplete={(bright) => {
143
+ state.currentWhiteNode.brightness = bright
144
+ state.mood.whiteColors = state.mood.whiteColors.map((item, idx) => {
145
+ if (idx === state.whitePaintBucketIdx) {
146
+ return {
147
+ ...state.mood.whiteColors[idx],
148
+ brightness: bright
149
+ }
150
+ }
151
+ return item
152
+ })
153
+ }}
154
+ />
155
+ <Spacer height={cx(16)} />
156
+ </Card>
157
+ <Spacer />
158
+ </>}
159
+ <Card style={styles.adjustCard}>
160
+ <Spacer height={cx(16)} />
161
+ <View style={styles.lightLine}>
162
+ <Text style={styles.light}>
163
+ {I18n.getLang(params.moduleParams.isSupportMixScene ? 'light_sources_tile_sec_lighting_headline' : 'light_sources_tile_tw_lighting_headline')}
164
+ </Text>
165
+ </View>
166
+ <Spacer height={cx(10)} />
167
+ <LdvSlider
168
+ title={I18n.getLang('add_new_dynamic_mood_lights_field_speed_topic_text')}
169
+ value={state.mood.speed}
170
+ onValueChange={value => {
171
+ state.mood.speed = value
172
+ }}
173
+ onSlidingComplete={value => {
174
+ state.mood.speed = value
175
+ }} />
176
+ <Spacer height={cx(16)} />
177
+ <View style={styles.nodesAdjust}>
178
+ <View style={styles.adjustButtons}>
179
+ <TouchableOpacity
180
+ onPress={() => {
181
+ state.colorPaintBucketSelected = true
182
+ }}>
183
+ <Image
184
+ style={[styles.adjustButton, { tintColor: state.colorPaintBucketSelected ? '#f60' : '#666' }]}
185
+ source={res.ic_paint_bucket} />
186
+ </TouchableOpacity>
187
+ <TouchableOpacity
188
+ onPress={() => {
189
+ state.colorPaintBucketSelected = false
190
+ }}>
191
+ <Image
192
+ style={[styles.adjustButton, { tintColor: state.colorPaintBucketSelected ? '#666' : '#f60' }]}
193
+ source={res.ic_colorize} />
194
+ </TouchableOpacity>
195
+ </View>
196
+ <FlatList
197
+ data={state.mood.colors}
198
+ style={styles.nodeList}
199
+ renderItem={({ item, index }) => {
200
+ return (
201
+ <View style={styles.nodeItem}>
202
+ <TouchableOpacity
203
+ style={[
204
+ styles.nodeBlock,
205
+ {
206
+ backgroundColor: hsv2Hex(item.h, item.s, item.v),
207
+ },
208
+ ]}
209
+ onPress={() => {
210
+ state.currentNode = item
211
+ state.colorPaintBucketIdx = index
212
+ }} />
213
+ <TouchableOpacity
214
+ style={styles.nodeDeleteBtn}
215
+ disabled={state.mood.colors.length < 3}
216
+ onPress={() => {
217
+ state.mood.colors.splice(index, 1)
218
+ state.currentNode = hex2Hsv(state.mood.colors[state.mood.colors.length - 1])!
219
+ }}>
220
+ <Image
221
+ style={[
222
+ styles.nodeDeleteIcon,
223
+ {
224
+ tintColor: state.mood.colors.length < 3 ? '#ccc' : '#666',
225
+ },
226
+ ]}
227
+ source={res.ic_mood_del} />
228
+ </TouchableOpacity>
229
+ </View>
230
+ )
231
+ }}
232
+ keyExtractor={(_, index) => `${index}`}
233
+ ItemSeparatorComponent={() => <Spacer height={cx(12)} />}
234
+ ListFooterComponent={() => {
235
+ if (state.mood.colors.length >= 8) {
236
+ return (<></>)
237
+ }
238
+ return (
239
+ <View>
240
+ <Spacer height={cx(12)} />
241
+ <TouchableOpacity
242
+ style={styles.nodeAddBtn}
243
+ onPress={() => {
244
+ const node = {
245
+ ...state.currentNode,
246
+ }
247
+ state.mood.colors.push(node)
248
+ state.currentNode = node
249
+ state.colorPaintBucketIdx = state.mood.colors.length - 1
250
+ }}>
251
+ <Image
252
+ style={{
253
+ width: cx(18),
254
+ height: cx(18),
255
+ tintColor: '#000',
256
+ }}
257
+ source={{ uri: res.add }} />
258
+ </TouchableOpacity>
259
+ </View>
260
+ )
261
+ }} />
262
+ </View>
263
+ <Spacer />
264
+ <View style={styles.lightLine}>
265
+ <Text style={styles.light}>
266
+ {I18n.getLang('add_new_dynamic_mood_lights_field_headline2_text')}
267
+ </Text>
268
+ <View style={[styles.preview, { backgroundColor: getColorBlockColor() }]} />
269
+ </View>
270
+ <Spacer />
271
+ <ColorAdjustView
272
+ h={state.currentNode.h}
273
+ s={state.currentNode.s}
274
+ v={state.currentNode.v}
275
+ reserveSV={true}
276
+ onHSVChange={(h, s, v) => {
277
+ if (state.colorPaintBucketSelected) {
278
+ state.mood.colors = state.mood.colors.map(() => (
279
+ { h, s, v }
280
+ ))
281
+ } else {
282
+ state.currentNode.h = h
283
+ state.currentNode.s = s
284
+ state.currentNode.v = v
285
+ }
286
+ }}
287
+ onHSVChangeComplete={(h, s, v) => {
288
+ if (state.colorPaintBucketSelected) {
289
+ state.mood.colors = state.mood.colors.map(() => (
290
+ { h, s, v }
291
+ ))
292
+ } else {
293
+ state.currentNode.h = h
294
+ state.currentNode.s = s
295
+ state.currentNode.v = v
296
+ state.mood.colors = state.mood.colors.map((item, idx) => {
297
+ if (idx === state.colorPaintBucketIdx) {
298
+ return { h, s, v }
299
+ }
300
+ return item
301
+ })
302
+ }
303
+ }}
304
+ />
305
+ <Spacer height={cx(16)} />
306
+ </Card>
307
+ <Spacer />
308
+ {params.mode === 'edit' &&
309
+ <View style={{ marginTop: cx(20), marginHorizontal: cx(24) }}>
310
+ <TextButton
311
+ style={styles.deleteBtn}
312
+ textStyle={styles.deleteBtnText}
313
+ text={I18n.getLang('flag_deleteflag')}
314
+ onPress={() => {
315
+ showDialog({
316
+ method: 'confirm',
317
+ title: I18n.getLang('flag_deletepopup'),
318
+ subTitle: I18n.getLang('strip_light_static_mood_edit_dialog_text'),
319
+ onConfirm: async (_, { close }) => {
320
+ close()
321
+ if (state.loading) return
322
+ state.loading = true
323
+ const res = await params.modDeleteFlag('del', state.mood)
324
+ if (res.success) {
325
+ state.loading = false
326
+ navigation.goBack()
327
+ }
328
+ }
329
+ })
330
+ }} />
331
+ </View>}
332
+ <Spacer />
333
+ </View>
334
+ </ScrollView>
335
+ </Page>
336
+ )
337
+ }
338
+
339
+ const styles = StyleSheet.create({
340
+ root: {
341
+ flex: 1,
342
+ flexDirection: 'column',
343
+ },
344
+ name: {
345
+ marginHorizontal: cx(24),
346
+ },
347
+ adjustCard: {
348
+ marginVertical: cx(12),
349
+ marginHorizontal: cx(24),
350
+ },
351
+ fanAdjustCard: {
352
+ marginHorizontal: cx(24),
353
+ },
354
+ lightLine: {
355
+ flexDirection: 'row',
356
+ marginHorizontal: cx(16),
357
+ },
358
+ light: {
359
+ color: '#000',
360
+ fontSize: cx(18),
361
+ fontFamily: 'helvetica_neue_lt_std_bd',
362
+ },
363
+ transitionMode: {
364
+ marginHorizontal: cx(16),
365
+ },
366
+ preview: {
367
+ width: cx(20),
368
+ height: cx(20),
369
+ marginStart: cx(12),
370
+ borderRadius: cx(4),
371
+ },
372
+ nodesAdjust: {
373
+ flexDirection: 'row',
374
+ alignItems: 'center',
375
+ },
376
+ adjustButtons: {
377
+ width: cx(44),
378
+ marginStart: cx(16),
379
+ },
380
+ adjustButton: {
381
+ width: cx(44),
382
+ height: cx(44),
383
+ },
384
+ nodeList: {
385
+ flex: 1,
386
+ marginHorizontal: cx(16),
387
+ },
388
+ nodeItem: {
389
+ flexDirection: 'row',
390
+ alignItems: 'center',
391
+ },
392
+ nodeBlock: {
393
+ flex: 1,
394
+ height: cx(40),
395
+ borderRadius: cx(8),
396
+ },
397
+ nodeDeleteBtn: {
398
+ width: cx(24),
399
+ height: cx(30),
400
+ justifyContent: 'center',
401
+ alignItems: 'center',
402
+ },
403
+ nodeDeleteIcon: {
404
+ width: cx(16),
405
+ height: cx(16),
406
+ },
407
+ nodeAddBtn: {
408
+ height: cx(40),
409
+ justifyContent: 'center',
410
+ alignItems: 'center',
411
+ marginEnd: cx(26),
412
+ borderRadius: cx(8),
413
+ borderWidth: cx(1),
414
+ borderStyle: 'dashed',
415
+ borderColor: '#666',
416
+ backgroundColor: '#f6f6f6',
417
+ },
418
+ deleteBtn: {
419
+ width: '100%',
420
+ height: cx(50),
421
+ backgroundColor: '#666',
422
+ borderRadius: cx(8),
423
+ },
424
+ deleteBtnText: {
425
+ color: '#fff',
426
+ fontSize: cx(16),
427
+ fontFamily: 'helvetica_neue_lt_std_bd',
428
+ },
429
+ })
430
+
431
+ export default FlagEditPage
@@ -0,0 +1,309 @@
1
+ import I18n from "@ledvance/base/src/i18n";
2
+ import { SceneNodeTransitionMode } from "@ledvance/ui-biz-bundle/src/modules/scene/SceneInfo";
3
+
4
+ type HSV = {
5
+ h: number
6
+ s: number
7
+ v: number
8
+ }
9
+
10
+ type BT = {
11
+ brightness: number
12
+ colorTemp: number
13
+ }
14
+
15
+ export interface FlagItemInfo {
16
+ id: number
17
+ version?: number
18
+ mode: number
19
+ speed: number
20
+ colors: HSV[]
21
+ whiteColors: BT []
22
+ fanEnable?: boolean
23
+ fanSpeed?: number
24
+ }
25
+
26
+ export interface FlagUiInfo extends FlagItemInfo{
27
+ name: string
28
+ }
29
+
30
+ export const defFlagList: FlagUiInfo[] = [
31
+ {
32
+ id: 255,
33
+ version: 0,
34
+ mode: SceneNodeTransitionMode.Jump,
35
+ speed: 70,
36
+ name: I18n.getLang('country_DE'),
37
+ whiteColors: [
38
+ { brightness: 100, colorTemp: 0},
39
+ ],
40
+ colors: [
41
+ {h: 48, s: 100, v: 100},
42
+ {h: 360, s: 100, v: 100},
43
+ {h: 0, s: 0, v:0}],
44
+ },
45
+ {
46
+ id: 254,
47
+ version: 0,
48
+ mode: SceneNodeTransitionMode.Jump,
49
+ speed: 70,
50
+ name: I18n.getLang('country_BE'),
51
+ whiteColors: [
52
+ { brightness: 100, colorTemp: 0},
53
+ ],
54
+ colors: [{h: 360, s: 100, v: 100}, {h: 48, s: 100, v: 100}, {h: 0, s: 0, v: 0}]
55
+ },
56
+ {
57
+ id: 253,
58
+ version: 0,
59
+ mode: SceneNodeTransitionMode.Jump,
60
+ speed: 70,
61
+ name: I18n.getLang('country_FR'),
62
+ whiteColors: [
63
+ { brightness: 100, colorTemp: 0},
64
+ ],
65
+ colors: [{h: 360, s: 100, v: 100}, {h: 0, s: 0, v: 100}, {h: 212, s: 100, v: 100}]
66
+ },
67
+ {
68
+ id: 252,
69
+ version: 0,
70
+ mode: SceneNodeTransitionMode.Jump,
71
+ speed: 70,
72
+ name: I18n.getLang('country_PT'),
73
+ whiteColors: [
74
+ { brightness: 100, colorTemp: 0},
75
+ ],
76
+ colors: [{h: 360, s: 100, v: 100}, {h: 360, s: 100, v: 100}, {h: 150, s: 96, v: 100}]
77
+ },
78
+ {
79
+ id: 251,
80
+ version: 0,
81
+ mode: SceneNodeTransitionMode.Jump,
82
+ speed: 70,
83
+ name: I18n.getLang('country_scotland'),
84
+ whiteColors: [
85
+ { brightness: 100, colorTemp: 0},
86
+ ],
87
+ colors: [{h: 209, s: 100, v: 100}, {h: 0, s: 0, v: 100}, {h: 209, s: 100, v: 100}]
88
+ },
89
+ {
90
+ id: 250,
91
+ version: 0,
92
+ mode: SceneNodeTransitionMode.Jump,
93
+ speed: 70,
94
+ name: I18n.getLang('country_ES'),
95
+ whiteColors: [
96
+ { brightness: 100, colorTemp: 0},
97
+ ],
98
+ colors: [{h: 357, s: 87, v: 100}, {h: 47, s: 100, v: 100}, {h: 357, s: 87, v: 100}]
99
+ },
100
+ {
101
+ id: 249,
102
+ version: 0,
103
+ mode: SceneNodeTransitionMode.Jump,
104
+ speed: 70,
105
+ name: I18n.getLang('country_TR'),
106
+ whiteColors: [
107
+ { brightness: 100, colorTemp: 0},
108
+ ],
109
+ colors: [{h: 360, s: 100, v: 100}, {h: 360, s: 100, v: 100}, {h: 0, s: 0, v: 100}]
110
+ },
111
+ {
112
+ id: 248,
113
+ version: 0,
114
+ mode: SceneNodeTransitionMode.Jump,
115
+ speed: 70,
116
+ name: I18n.getLang('country_AT'),
117
+ whiteColors: [
118
+ { brightness: 100, colorTemp: 0},
119
+ ],
120
+ colors: [{h: 360, s: 100, v: 100}, {h: 0, s: 0, v: 100}, {h: 360, s: 100, v: 100}]
121
+ },
122
+ {
123
+ id: 247,
124
+ version: 0,
125
+ mode: SceneNodeTransitionMode.Jump,
126
+ speed: 70,
127
+ name: I18n.getLang('country_england'),
128
+ whiteColors: [
129
+ { brightness: 100, colorTemp: 0},
130
+ ],
131
+ colors: [{h: 0, s: 0, v: 100}, {h: 360, s: 100, v: 100}, {h: 0, s: 0, v: 100}]
132
+ },
133
+ {
134
+ id: 246,
135
+ version: 0,
136
+ mode: SceneNodeTransitionMode.Jump,
137
+ speed: 70,
138
+ name: I18n.getLang('country_HU'),
139
+ whiteColors: [
140
+ { brightness: 100, colorTemp: 0},
141
+ ],
142
+ colors: [{h: 133, s: 36, v: 100}, {h: 0, s: 0, v: 100}, {h: 360, s: 100, v: 100}]
143
+ },
144
+ {
145
+ id: 245,
146
+ version: 0,
147
+ mode: SceneNodeTransitionMode.Jump,
148
+ speed: 70,
149
+ name: I18n.getLang('country_SK'),
150
+ whiteColors: [
151
+ { brightness: 100, colorTemp: 0},
152
+ ],
153
+ colors: [{h: 360, s: 100, v: 100}, {h: 213, s: 93, v: 100}, {h: 0, s: 0, v: 100}]
154
+ },
155
+ {
156
+ id: 244,
157
+ version: 0,
158
+ mode: SceneNodeTransitionMode.Jump,
159
+ speed: 70,
160
+ name: I18n.getLang('country_AL'),
161
+ whiteColors: [
162
+ { brightness: 100, colorTemp: 0},
163
+ ],
164
+ colors: [{h: 360, s: 100, v: 100}, {h: 0, s: 0, v:0}, {h: 360, s: 100, v: 100}]
165
+ },
166
+ {
167
+ id: 243,
168
+ version: 0,
169
+ mode: SceneNodeTransitionMode.Jump,
170
+ speed: 70,
171
+ name: I18n.getLang('country_DK'),
172
+ whiteColors: [
173
+ { brightness: 100, colorTemp: 0},
174
+ ],
175
+ colors: [{h: 360, s: 100, v: 100}, {h: 0, s: 0, v:0}, {h: 360, s: 100, v: 100}]
176
+ },
177
+ {
178
+ id: 242,
179
+ version: 0,
180
+ mode: SceneNodeTransitionMode.Jump,
181
+ speed: 70,
182
+ name: I18n.getLang('country_NL'),
183
+ whiteColors: [
184
+ { brightness: 100, colorTemp: 0},
185
+ ],
186
+ colors: [{h: 217, s: 100, v: 100}, {h: 0, s: 0, v:0}, {h: 360, s: 100, v: 100}]
187
+ },
188
+ {
189
+ id: 241,
190
+ version: 0,
191
+ mode: SceneNodeTransitionMode.Jump,
192
+ speed: 70,
193
+ name: I18n.getLang('country_RO'),
194
+ whiteColors: [
195
+ { brightness: 100, colorTemp: 0},
196
+ ],
197
+ colors: [{h: 360, s: 100, v: 100}, {h: 48, s: 91, v: 100}, {h: 219, s: 100, v: 100}]
198
+ },
199
+ {
200
+ id: 240,
201
+ version: 0,
202
+ mode: SceneNodeTransitionMode.Jump,
203
+ speed: 70,
204
+ name: I18n.getLang('country_CH'),
205
+ whiteColors: [
206
+ { brightness: 100, colorTemp: 0},
207
+ ],
208
+ colors: [{h: 360, s: 100, v: 100}, {h: 0, s: 0, v: 100}, {h: 360, s: 100, v: 100}]
209
+ },
210
+ {
211
+ id: 239,
212
+ version: 0,
213
+ mode: SceneNodeTransitionMode.Jump,
214
+ speed: 70,
215
+ name: I18n.getLang('country_RS'),
216
+ whiteColors: [
217
+ { brightness: 100, colorTemp: 0},
218
+ ],
219
+ colors: [{h: 0, s: 0, v: 100}, {h: 210, s: 89, v: 100}, {h: 360, s: 100, v: 100}]
220
+ },
221
+ {
222
+ id: 238,
223
+ version: 0,
224
+ mode: SceneNodeTransitionMode.Jump,
225
+ speed: 70,
226
+ name: I18n.getLang('country_IT'),
227
+ whiteColors: [
228
+ { brightness: 100, colorTemp: 0},
229
+ ],
230
+ colors: [{h: 360, s: 100, v: 100}, {h: 212, s: 4, v: 100}, {h: 149, s: 100, v: 100}]
231
+ },
232
+ {
233
+ id: 237,
234
+ version: 0,
235
+ mode: SceneNodeTransitionMode.Jump,
236
+ speed: 70,
237
+ name: I18n.getLang('country_CZ'),
238
+ whiteColors: [
239
+ { brightness: 100, colorTemp: 0},
240
+ ],
241
+ colors: [{h: 360, s: 100, v: 100}, {h: 0, s: 0, v: 100}, {h: 211, s: 86, v: 100}]
242
+ },
243
+ {
244
+ id: 236,
245
+ version: 0,
246
+ mode: SceneNodeTransitionMode.Jump,
247
+ speed: 70,
248
+ name: I18n.getLang('country_SI'),
249
+ whiteColors: [
250
+ { brightness: 100, colorTemp: 0},
251
+ ],
252
+ colors: [{h: 360, s: 100, v: 100}, {h: 217, s: 100, v: 100}, {h: 0, s: 0, v: 100}]
253
+ },
254
+ {
255
+ id: 235,
256
+ version: 0,
257
+ mode: SceneNodeTransitionMode.Jump,
258
+ speed: 70,
259
+ name: I18n.getLang('country_HR'),
260
+ whiteColors: [
261
+ { brightness: 100, colorTemp: 0},
262
+ ],
263
+ colors: [{h: 0, s: 0, v: 100}, {h: 220, s: 89, v: 100}, {h: 360, s: 100, v: 100}]
264
+ },
265
+ {
266
+ id: 234,
267
+ version: 0,
268
+ mode: SceneNodeTransitionMode.Jump,
269
+ speed: 70,
270
+ name: I18n.getLang('country_SE'),
271
+ whiteColors: [
272
+ { brightness: 100, colorTemp: 0},
273
+ ],
274
+ colors: [{h: 201, s: 100, v: 100}, {h: 48, s: 99, v: 100}, {h: 201, s: 100, v: 100}]
275
+ },
276
+ {
277
+ id: 233,
278
+ version: 0,
279
+ mode: SceneNodeTransitionMode.Jump,
280
+ speed: 70,
281
+ name: I18n.getLang('country_NO'),
282
+ whiteColors: [
283
+ { brightness: 100, colorTemp: 0},
284
+ ],
285
+ colors: [{h: 0, s: 0, v: 100}, {h: 218, s: 100, v: 100}, {h: 360, s: 100, v: 100}]
286
+ },
287
+ {
288
+ id: 232,
289
+ version: 0,
290
+ mode: SceneNodeTransitionMode.Jump,
291
+ speed: 70,
292
+ name: I18n.getLang('country_PL'),
293
+ whiteColors: [
294
+ { brightness: 100, colorTemp: 0},
295
+ ],
296
+ colors: [{h: 360, s: 100, v: 100}, {h: 0, s: 0, v: 100}]
297
+ },
298
+ {
299
+ id: 231,
300
+ version: 0,
301
+ mode: SceneNodeTransitionMode.Jump,
302
+ speed: 70,
303
+ name: I18n.getLang('flag_leverkusen'),
304
+ whiteColors: [
305
+ { brightness: 100, colorTemp: 0},
306
+ ],
307
+ colors: [{h: 360, s: 100, v: 100}, {h: 0, s: 0, v: 100}, {h: 60, s: 100, v: 100}]
308
+ },
309
+ ]
@@ -0,0 +1,84 @@
1
+ import { StyleSheet, View, Text, ViewStyle } from 'react-native'
2
+ import React from 'react'
3
+ import Card from '@ledvance/base/src/components/Card'
4
+ import { SwitchButton, Utils } from 'tuya-panel-kit'
5
+ import MoodColorsLine from '@ledvance/base/src/components/MoodColorsLine'
6
+ import Spacer from '@ledvance/base/src/components/Spacer'
7
+
8
+ const cx = Utils.RatioUtils.convertX
9
+
10
+ interface RecommendMoodItemProps {
11
+ enable: boolean
12
+ title: string
13
+ colors: string[]
14
+ style?: ViewStyle
15
+ onPress?: () => void
16
+ onSwitch: (enable: boolean) => void
17
+ }
18
+
19
+ export default function FlagItem(props: RecommendMoodItemProps) {
20
+ return (
21
+ <Card
22
+ style={[styles.card, props.style]}
23
+ onPress={props.onPress}>
24
+ <View>
25
+ <Spacer height={cx(16)} />
26
+ <View style={styles.headline}>
27
+ <Text style={styles.headText}>{props.title}</Text>
28
+ <SwitchButton
29
+ thumbStyle={{ elevation: 0 }}
30
+ value={props.enable}
31
+ onValueChange={props.onSwitch} />
32
+ </View>
33
+ <Spacer />
34
+ <View style={styles.gradientItem}>
35
+ <MoodColorsLine
36
+ type={'separate'}
37
+ nodeStyle={{ borderColor: '#ccc', borderWidth: 1 }}
38
+ colors={props.colors} />
39
+ </View>
40
+ <Spacer height={cx(16)} />
41
+ </View>
42
+ </Card>
43
+ )
44
+ }
45
+
46
+ const styles = StyleSheet.create({
47
+ card: {
48
+ marginHorizontal: cx(24),
49
+ },
50
+ headline: {
51
+ flexDirection: 'row',
52
+ marginHorizontal: cx(16),
53
+ },
54
+ headText: {
55
+ flex: 1,
56
+ color: '#000',
57
+ fontSize: cx(16),
58
+ fontFamily: 'helvetica_neue_lt_std_bd',
59
+ lineHeight: cx(20),
60
+ },
61
+ gradientItem: {
62
+ alignItems: 'center',
63
+ },
64
+ gradient: {
65
+ borderRadius: cx(8),
66
+ },
67
+ moodTypeItem: {
68
+ flexDirection: 'row',
69
+ },
70
+ moodTypeLabel: {
71
+ marginStart: cx(16),
72
+ paddingHorizontal: cx(12.5),
73
+ backgroundColor: '#E6E7E8',
74
+ borderRadius: cx(8),
75
+ },
76
+ moodTypeLabelText: {
77
+ height: cx(16),
78
+ color: '#000000DD',
79
+ fontSize: cx(10),
80
+ textAlignVertical: 'center',
81
+ fontFamily: 'helvetica_neue_lt_std_roman',
82
+ lineHeight: cx(16),
83
+ },
84
+ })
@@ -0,0 +1,226 @@
1
+ import React, { useEffect, useMemo } from "react";
2
+ import Page from "@ledvance/base/src/components/Page";
3
+ import { useDeviceInfo, useUAGroupInfo } from "@ledvance/base/src/models/modules/NativePropsSlice";
4
+ import { FlatList } from "react-native";
5
+ import Spacer from "@ledvance/base/src/components/Spacer";
6
+ import { Utils } from "tuya-panel-kit";
7
+ import FlagItem from "./FlagItem";
8
+ import { FlagUiInfo } from "./FlagInfo";
9
+ import { getRemoteFlag, saveFlag, useFlag } from "./FlagActions";
10
+ import { useRoute, useNavigation } from '@react-navigation/core'
11
+ import I18n from "@ledvance/base/src/i18n";
12
+ import { useReactive } from "ahooks";
13
+ import { cloneDeep, difference, isEqual, last, map, range } from "lodash";
14
+ import { ui_biz_routerKey } from "../../navigation/Routers";
15
+ import res from "@ledvance/base/src/res";
16
+ import { hsv2Hex } from "@ledvance/base/src/utils";
17
+ import { SceneNodeTransitionMode } from "@ledvance/group-ui-biz-bundle/src/modules/mood/SceneInfo";
18
+ import { Result } from "@ledvance/base/src/models/modules/Result";
19
+ import { WorkMode } from "@ledvance/base/src/utils/interface";
20
+ const cx = Utils.RatioUtils.convertX
21
+
22
+ export interface FlagPageProps {
23
+ isSupportMixScene?: boolean
24
+ isStripLight?: boolean
25
+ isStringLight?: boolean
26
+ isSupportTemperature?: boolean
27
+ isSupportBrightness?: boolean
28
+ drawToolLight?: {
29
+ drawToolCode: string
30
+ drawToolObj2dp: (colors: string[]) => string
31
+ }
32
+ getRemoteMoodList?: (groupId: string) => Promise<Result<any>>
33
+ sceneDataCode?: string
34
+ workModeCode: string
35
+ switchLedCode: string
36
+ }
37
+
38
+ const FlagPage = () => {
39
+ const params = useRoute().params as FlagPageProps
40
+ const devInfo = useDeviceInfo()
41
+ const uaGroupInfo = useUAGroupInfo()
42
+ const navigation = useNavigation()
43
+ const [flagState, setFlag] = useFlag({
44
+ isStripLight: params.isStripLight,
45
+ isStringLight: params.isStringLight,
46
+ isMixLight: params.isSupportMixScene
47
+ })
48
+ const state = useReactive({
49
+ loading: true,
50
+ flags: [] as FlagUiInfo[],
51
+ moods: [] as any[]
52
+ })
53
+ const defParams = {
54
+ workModeDpCode: params.workModeCode,
55
+ switchLedDpCode: params.switchLedCode,
56
+ workMode: WorkMode.Scene,
57
+ sceneStatus: !params.isSupportMixScene && params.sceneDataCode ? {
58
+ sceneDataDpCode: params.sceneDataCode
59
+ } : undefined,
60
+ drawToolStatus: params.isStripLight && params.drawToolLight ? {
61
+ drawToolCode: params.drawToolLight.drawToolCode,
62
+ drawToolObj2dp: params.drawToolLight.drawToolObj2dp
63
+ } : undefined
64
+ }
65
+ const flagId = useMemo(() => {
66
+ if (flagState && state.flags?.length) {
67
+ const { flag } = flagState
68
+ if(params.isStripLight && flag?.colors !== undefined){
69
+
70
+ }
71
+ // if (flag?.colors !== undefined) {
72
+ // const flag = state.flags.find(item => {
73
+ // const hexColors = item.colors.map(c => hsv2Hex(c.h, c.s, c.v).toLocaleUpperCase())
74
+ // return isEqual(hexColors, flag.colors)
75
+ // })
76
+ // return flag?.id
77
+ // }
78
+ if (flag?.id !== undefined) {
79
+ return flag.id
80
+ }
81
+ }
82
+
83
+ return -1
84
+ }, [JSON.stringify(flagState), JSON.stringify(state.flags)])
85
+
86
+ useEffect(() =>{
87
+ console.log(flagState, '< --- flagstate')
88
+ console.log(state.flags, '< --- state.flags')
89
+ }, [flagState, JSON.stringify(state.flags)])
90
+ useEffect(() => {
91
+ getRemoteFlagInfo().then()
92
+ if (params.getRemoteMoodList && !params.isStripLight) {
93
+ params.getRemoteMoodList(uaGroupInfo.tyGroupId.toString()).then(res => {
94
+ if (res.success && Array.isArray(res.data)) {
95
+ state.moods = res.data
96
+ }
97
+ })
98
+ }
99
+ }, [])
100
+
101
+ const getRemoteFlagInfo = async () => {
102
+ const res = await getRemoteFlag(uaGroupInfo.tyGroupId.toString())
103
+ if (res.success) {
104
+ let cloneFlag: FlagUiInfo[] = cloneDeep(res.data) || []
105
+ if(params.drawToolLight){
106
+ cloneFlag = cloneFlag.map(flag => {
107
+ flag.colors = flag.colors.reverse()
108
+ return flag
109
+ })
110
+ }
111
+ state.flags = cloneFlag
112
+ }
113
+ }
114
+
115
+ const navigationRoute = (mode: 'add' | 'edit', currentMood?: FlagUiInfo) => {
116
+ const path = ui_biz_routerKey.group_ui_biz_flag_page_edit
117
+ navigation.navigate(path, {
118
+ mode,
119
+ moods: state.flags,
120
+ currentMood,
121
+ moduleParams: params,
122
+ modDeleteFlag
123
+ })
124
+ }
125
+
126
+ const modDeleteFlag = async (mode: 'add' | 'edit' | 'del', currentMood: FlagUiInfo) => {
127
+ const checkedMood: FlagUiInfo = {
128
+ ...currentMood,
129
+ }
130
+
131
+ let newScene: FlagUiInfo[] = []
132
+ if (mode === 'add') {
133
+ newScene = [
134
+ checkedMood,
135
+ ...state.flags,
136
+ ]
137
+ } else if (mode === 'del') {
138
+ newScene = state.flags.filter(item => item.id !== checkedMood.id)
139
+ } else {
140
+ newScene = state.flags.map(item => {
141
+ if (item.id === checkedMood.id) {
142
+ return checkedMood
143
+ }
144
+ return item
145
+ })
146
+ }
147
+ const mood = mode === 'del' ? (newScene.length === 0 ? undefined : newScene[0]) : checkedMood
148
+ const res = await saveFlag(uaGroupInfo.tyGroupId.toString(), cloneDeep(newScene))
149
+ if (res.success) {
150
+ state.flags = cloneDeep(newScene)
151
+ if(mode === 'del' && flagState?.flag && (checkedMood.id !== flagState.flag.id)){
152
+ return{
153
+ success: true
154
+ }
155
+ }
156
+ return setFlag({
157
+ ...defParams,
158
+ flag: cloneDeep(mood)
159
+ })
160
+ }
161
+ return {
162
+ success: false
163
+ }
164
+ }
165
+
166
+ return (
167
+ <Page
168
+ headlineText={I18n.getLang('Feature_devicepanel_flags')}
169
+ backText={devInfo.name}
170
+ headlineIcon={res.add}
171
+ onHeadlineIconClick={() => {
172
+ const useIds = map([
173
+ ...state.flags,
174
+ ...state.moods
175
+ ], 'id')
176
+ console.log(useIds, '< --- useIds')
177
+ const idRange = range(0, 256)
178
+ const unuseId = last(difference(idRange, useIds))
179
+ if (unuseId !== undefined) {
180
+ navigationRoute('add', newFlag(unuseId))
181
+ }
182
+ }}
183
+ >
184
+ <FlatList
185
+ data={state.flags}
186
+ renderItem={({ item }) => <FlagItem
187
+ enable={flagId === item.id}
188
+ title={item.name}
189
+ colors={item.colors.map(item => hsv2Hex(item.h, item.s, item.v))}
190
+ onSwitch={async (enable) => {
191
+ if (enable) {
192
+ await setFlag({
193
+ ...defParams,
194
+ flag: cloneDeep(item),
195
+ })
196
+ }
197
+ }}
198
+ onPress={() => {
199
+ navigationRoute('edit', item)
200
+ }}
201
+ />}
202
+ keyExtractor={item => item.name}
203
+ ListHeaderComponent={() => (<Spacer height={cx(10)} />)}
204
+ ItemSeparatorComponent={() => (<Spacer />)}
205
+ ListFooterComponent={() => (<Spacer />)}
206
+ />
207
+ </Page>
208
+ )
209
+ }
210
+
211
+ export const newFlag = (id: number) => {
212
+ return {
213
+ version: 0,
214
+ id,
215
+ name: '',
216
+ mode: SceneNodeTransitionMode.Jump,
217
+ speed: 70,
218
+ colors: [
219
+ { h: 48, s: 100, v: 100 },
220
+ { h: 360, s: 100, v: 86 },
221
+ ],
222
+ whiteColors: [{ brightness: 100, colorTemp: 0 }],
223
+ }
224
+ }
225
+
226
+ export default FlagPage
@@ -7,6 +7,8 @@ import AddMoodPage from '../modules/mood/AddMoodPage'
7
7
  import DynamicMoodEditorPage from '../modules/mood/DynamicMoodEditorPage'
8
8
  import StaticMoodEditorPage from '../modules/mood/StaticMoodEditorPage'
9
9
  import SelectPage from '../modules/select/SelectPage'
10
+ import FlagPage from '../modules/flags/FlagPage'
11
+ import FlagEditPage from '../modules/flags/FlagEditPage'
10
12
 
11
13
  export const ui_biz_routerKey = {
12
14
  'group_ui_biz_timer': 'group_ui_biz_timer',
@@ -17,6 +19,8 @@ export const ui_biz_routerKey = {
17
19
  'group_ui_biz_static_mood_edit': 'group_ui_biz_static_mood_edit',
18
20
  'group_ui_biz_dynamic_mood_edit': 'group_ui_biz_dynamic_mood_edit',
19
21
  'group_ui_biz_select_page': 'group_ui_biz_select_page',
22
+ 'group_ui_biz_flag_page': 'group_ui_biz_flag_page',
23
+ 'group_ui_biz_flag_page_edit': 'group_ui_biz_flag_page_edit'
20
24
  }
21
25
 
22
26
  export const TimerRouters: NavigationRoute[] = [
@@ -94,4 +98,23 @@ export const SelectPageRouters: NavigationRoute[] = [
94
98
  ...TransitionPresets.ModalPresentationIOS,
95
99
  },
96
100
  },
101
+ ]
102
+
103
+ export const FlagPageRouters: NavigationRoute[] = [
104
+ {
105
+ name: ui_biz_routerKey.group_ui_biz_flag_page,
106
+ component: FlagPage,
107
+ options: {
108
+ hideTopbar: true,
109
+ showOfflineView: false,
110
+ }
111
+ },
112
+ {
113
+ name: ui_biz_routerKey.group_ui_biz_flag_page_edit,
114
+ component: FlagEditPage,
115
+ options: {
116
+ hideTopbar: true,
117
+ showOfflineView: false,
118
+ }
119
+ },
97
120
  ]