@ledvance/ui-biz-bundle 1.0.71 → 1.0.72
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/modules/mood/FantasyMood.tsx +184 -0
- package/src/modules/mood/FantasyMoodEditPage.tsx +548 -0
- package/src/modules/mood/FantasyMoodItem.tsx +104 -0
- package/src/modules/scene/SceneAction.ts +155 -2
- package/src/modules/scene/SceneInfo.ts +164 -0
- package/src/navigation/Routers.ts +32 -0
package/package.json
CHANGED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import React, { useCallback, useEffect } from 'react'
|
|
2
|
+
import Page from '@ledvance/base/src/components/Page'
|
|
3
|
+
import { Utils } from 'tuya-panel-kit'
|
|
4
|
+
import { StripScenePageUIState, StripSceneUIState } from '../scene/SceneInfo'
|
|
5
|
+
import { getRemoteFantasyScene, useFantasyScene } from '../scene/SceneAction'
|
|
6
|
+
import { useDeviceInfo } from '@ledvance/base/src/models/modules/NativePropsSlice'
|
|
7
|
+
import { useReactive } from 'ahooks'
|
|
8
|
+
import Strings from '@ledvance/base/src/i18n'
|
|
9
|
+
import res from '@ledvance/base/src/res'
|
|
10
|
+
import { FlatList } from 'react-native'
|
|
11
|
+
import Spacer from '@ledvance/base/src/components/Spacer'
|
|
12
|
+
import FantasyMoodItem from './FantasyMoodItem'
|
|
13
|
+
import { useNavigation, useRoute } from '@react-navigation/core'
|
|
14
|
+
import { ui_biz_routerKey } from '../../navigation/Routers'
|
|
15
|
+
import { stripDp2Obj } from '../scene/SceneAction'
|
|
16
|
+
import { Buffer } from 'buffer'
|
|
17
|
+
import { NativeApi } from '@ledvance/base/src/api/native'
|
|
18
|
+
const cx = Utils.RatioUtils.convertX
|
|
19
|
+
|
|
20
|
+
interface MoodPageUIState extends StripScenePageUIState {
|
|
21
|
+
staticTagChecked: boolean
|
|
22
|
+
dynamicTagChecked: boolean
|
|
23
|
+
originScene: StripSceneUIState[]
|
|
24
|
+
filteredMoods: StripSceneUIState[]
|
|
25
|
+
loading: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface FantasyMoodPageProps {
|
|
29
|
+
featureId: string
|
|
30
|
+
switchLedDpCode: string
|
|
31
|
+
sceneDpCode: string
|
|
32
|
+
workModeDpCode: string
|
|
33
|
+
isStringLight?: boolean
|
|
34
|
+
isStripLight?: boolean
|
|
35
|
+
isSupportColor: boolean
|
|
36
|
+
isSupportTemperature: boolean
|
|
37
|
+
isSupportBrightness: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
const FantasyMoodPage = () => {
|
|
42
|
+
const routeParams = useRoute().params as FantasyMoodPageProps
|
|
43
|
+
const params: FantasyMoodPageProps = {
|
|
44
|
+
...routeParams,
|
|
45
|
+
}
|
|
46
|
+
const [sceneId, setScene] = useFantasyScene(params.sceneDpCode, params.workModeDpCode)
|
|
47
|
+
const deviceInfo = useDeviceInfo()
|
|
48
|
+
const navigation = useNavigation()
|
|
49
|
+
|
|
50
|
+
const state = useReactive<MoodPageUIState>({
|
|
51
|
+
currentScene: undefined,
|
|
52
|
+
staticTagChecked: true,
|
|
53
|
+
dynamicTagChecked: true,
|
|
54
|
+
scenes: [],
|
|
55
|
+
flag: Symbol(),
|
|
56
|
+
originScene: [],
|
|
57
|
+
filteredMoods: [],
|
|
58
|
+
loading: false,
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
useEffect(() =>{
|
|
62
|
+
console.log(stripDp2Obj(Buffer.from('AdgQMjIAAABkAABkAGQ8','base64').toString('hex')), '开合')
|
|
63
|
+
console.log(stripDp2Obj(Buffer.from('AdcPMjIAAABkAABkAGQ8','base64').toString('hex')), '乱闪')
|
|
64
|
+
console.log(stripDp2Obj(Buffer.from('AdYOMjIAAABkAABkAGQ8','base64').toString('hex')), '穿梭')
|
|
65
|
+
console.log(stripDp2Obj(Buffer.from('AdUNMjIAAABkAABkAGQ8','base64').toString('hex')), '反弹')
|
|
66
|
+
console.log(stripDp2Obj(Buffer.from('AdQMMjIAAABkAABkAGQ8','base64').toString('hex')), '闪现')
|
|
67
|
+
console.log(stripDp2Obj(Buffer.from('AdMJMjIAAABkAABkAGQ8','base64').toString('hex')), '飘动')
|
|
68
|
+
console.log(stripDp2Obj(Buffer.from('AdIIMjIAAABkAABkAGQ8','base64').toString('hex')), '追光')
|
|
69
|
+
console.log(stripDp2Obj(Buffer.from('AdEHMjIAAABkAABkAGQ8','base64').toString('hex')), '飘落')
|
|
70
|
+
console.log(stripDp2Obj(Buffer.from('AdAGMjIAAABkAABkAGQ8','base64').toString('hex')), '堆积')
|
|
71
|
+
console.log(stripDp2Obj(Buffer.from('Ac8FMjIAAABkAABkAGQ8','base64').toString('hex')), '流星')
|
|
72
|
+
console.log(stripDp2Obj(Buffer.from('Ac4LMjIAAABkAABkAGQ8','base64').toString('hex')), '彩虹')
|
|
73
|
+
console.log(stripDp2Obj(Buffer.from('Ac0KMjIAAABkAABkAGQ8','base64').toString('hex')), '流水')
|
|
74
|
+
console.log(stripDp2Obj(Buffer.from('AcwEMjIAAABkAABkAGQ8','base64').toString('hex')), '闪烁')
|
|
75
|
+
console.log(stripDp2Obj(Buffer.from('AcsDMjIAAABkAABkAGQ8','base64').toString('hex')), '呼吸2')
|
|
76
|
+
console.log(stripDp2Obj(Buffer.from('AcoCMjIAAABkAABkAGQ8','base64').toString('hex')), '呼吸1')
|
|
77
|
+
console.log(stripDp2Obj(Buffer.from('AckBMjIAAABkAABkAGQ8','base64').toString('hex')), '跳变')
|
|
78
|
+
}, [])
|
|
79
|
+
|
|
80
|
+
const getSceneList = useCallback(async (currentSceneId: number) => {
|
|
81
|
+
NativeApi.log(`getScene id ${currentSceneId}`)
|
|
82
|
+
const res = await getRemoteFantasyScene(routeParams.featureId, deviceInfo.devId, { isStringLight: routeParams.isStringLight, isStripLight: routeParams.isStripLight })
|
|
83
|
+
if (res.success) {
|
|
84
|
+
state.scenes = res.data || []
|
|
85
|
+
state.currentScene = state.scenes.find(scene => scene.id === currentSceneId)
|
|
86
|
+
}
|
|
87
|
+
}, [])
|
|
88
|
+
|
|
89
|
+
// useUpdateEffect(() => {
|
|
90
|
+
// state.currentScene = state.scenes.find(scene => scene.id === sceneId)
|
|
91
|
+
// }, [sceneId])
|
|
92
|
+
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
getSceneList(sceneId).then()
|
|
95
|
+
}, [state.flag, sceneId])
|
|
96
|
+
|
|
97
|
+
const navigateToEdit = useCallback((mode: 'edit' | 'add', currentMood?: StripSceneUIState) => {
|
|
98
|
+
navigation.navigate(ui_biz_routerKey.ui_biz_fantasy_mood_edit, {
|
|
99
|
+
mode,
|
|
100
|
+
currentMood,
|
|
101
|
+
moods: state.scenes,
|
|
102
|
+
moduleParams: params,
|
|
103
|
+
onSave: () => {
|
|
104
|
+
state.flag = Symbol()
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
}, [])
|
|
108
|
+
|
|
109
|
+
const getSwitchEnable = useCallback((id: number) =>{
|
|
110
|
+
return state.currentScene?.id === id
|
|
111
|
+
}, [state.currentScene?.id])
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<>
|
|
115
|
+
<Page
|
|
116
|
+
backText={deviceInfo.name}
|
|
117
|
+
headlineText={Strings.getLang('mood_overview_headline_text')}
|
|
118
|
+
headlineIcon={res.add}
|
|
119
|
+
onHeadlineIconClick={() => {
|
|
120
|
+
navigateToEdit('add', newScene)
|
|
121
|
+
}}
|
|
122
|
+
loading={state.loading}>
|
|
123
|
+
<Spacer height={cx(10)} />
|
|
124
|
+
<FlatList
|
|
125
|
+
data={state.scenes}
|
|
126
|
+
renderItem={({ item }) => {
|
|
127
|
+
return (
|
|
128
|
+
<FantasyMoodItem
|
|
129
|
+
enable={getSwitchEnable(item.id)}
|
|
130
|
+
mood={item}
|
|
131
|
+
onPress={() => {
|
|
132
|
+
navigateToEdit('edit', item)
|
|
133
|
+
}}
|
|
134
|
+
onSwitch={async _ => {
|
|
135
|
+
if (getSwitchEnable(item.id)) return
|
|
136
|
+
state.loading = true
|
|
137
|
+
await setScene(item)
|
|
138
|
+
state.loading = false
|
|
139
|
+
}} />
|
|
140
|
+
)
|
|
141
|
+
}}
|
|
142
|
+
ListHeaderComponent={() => (<Spacer height={cx(10)} />)}
|
|
143
|
+
ItemSeparatorComponent={() => (<Spacer />)}
|
|
144
|
+
ListFooterComponent={() => (<Spacer />)}
|
|
145
|
+
keyExtractor={item => `${item.id}`} />
|
|
146
|
+
</Page>
|
|
147
|
+
</>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const newScene = {
|
|
152
|
+
version: 1,
|
|
153
|
+
id: 1,
|
|
154
|
+
mode: 10,
|
|
155
|
+
name: '',
|
|
156
|
+
speed: 50,
|
|
157
|
+
loop: 0,
|
|
158
|
+
segmented: 0,
|
|
159
|
+
direction: 0,
|
|
160
|
+
excessive: 0,
|
|
161
|
+
optionB: 0,
|
|
162
|
+
optionC: 0,
|
|
163
|
+
image: '',
|
|
164
|
+
nodes: [
|
|
165
|
+
{
|
|
166
|
+
brightness: 0,
|
|
167
|
+
colorTemp: 0,
|
|
168
|
+
h: 0,
|
|
169
|
+
isColorNode: true,
|
|
170
|
+
s: 100,
|
|
171
|
+
v: 100
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
brightness: 0,
|
|
175
|
+
colorTemp: 0,
|
|
176
|
+
h: 60,
|
|
177
|
+
isColorNode: true,
|
|
178
|
+
s: 100,
|
|
179
|
+
v: 100
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export default FantasyMoodPage
|
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
import Page from '@ledvance/base/src/components/Page'
|
|
2
|
+
import I18n from '@ledvance/base/src/i18n'
|
|
3
|
+
import { useNavigation, useRoute } from '@react-navigation/native'
|
|
4
|
+
import { StackNavigationProp } from '@react-navigation/stack'
|
|
5
|
+
import React, { useCallback, useEffect, useMemo } from 'react'
|
|
6
|
+
import { useReactive } from 'ahooks'
|
|
7
|
+
import { useWorkMode, COLOUR } from 'hooks/DeviceDpStateHooks'
|
|
8
|
+
import { hsv2Hex, mapFloatToRange } from '@ledvance/base/src/utils'
|
|
9
|
+
import { cctToColor } from '@ledvance/base/src/utils/cctUtils'
|
|
10
|
+
import res from '@ledvance/base/src/res'
|
|
11
|
+
import { cloneDeep, find, isEqual, maxBy } from 'lodash'
|
|
12
|
+
import { FlatList, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
|
|
13
|
+
import TextField from '@ledvance/base/src/components/TextField'
|
|
14
|
+
import Card from '@ledvance/base/src/components/Card'
|
|
15
|
+
import Spacer from '@ledvance/base/src/components/Spacer'
|
|
16
|
+
import LampAdjustView from '@ledvance/base/src/components/LampAdjustView'
|
|
17
|
+
import { Utils } from 'tuya-panel-kit'
|
|
18
|
+
import LdvSlider from '@ledvance/base/src/components/ldvSlider'
|
|
19
|
+
import { useDeviceId, useRole } from '@ledvance/base/src/models/modules/NativePropsSlice'
|
|
20
|
+
import TextButton from '@ledvance/base/src/components/TextButton'
|
|
21
|
+
import { stringLightSceneMode, stripLightSceneMode, StripSceneUIState, StripNodeInfo, StripLightSceneMode } from '../scene/SceneInfo'
|
|
22
|
+
import TextFieldStyleButton from '@ledvance/base/src/components/TextFieldStyleButton'
|
|
23
|
+
import { SelectPageParams } from '../select/SelectPage'
|
|
24
|
+
import { showDeleteMoodDialog } from './tools'
|
|
25
|
+
import { ui_biz_routerKey } from '../../navigation/Routers'
|
|
26
|
+
import { FantasyMoodPageProps } from './FantasyMood'
|
|
27
|
+
import Segmented from '@ledvance/base/src/components/Segmented'
|
|
28
|
+
import { saveFantasyScene, useFantasyScene } from '../scene/SceneAction'
|
|
29
|
+
const cx = Utils.RatioUtils.convertX
|
|
30
|
+
|
|
31
|
+
interface FantasyMoodEditPageState {
|
|
32
|
+
headline: string
|
|
33
|
+
mood: StripSceneUIState
|
|
34
|
+
currentNode: StripNodeInfo
|
|
35
|
+
paintBucketSelected: boolean
|
|
36
|
+
sceneMode: StripLightSceneMode
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface FantasyMoodEditParams {
|
|
40
|
+
mode: 'add' | 'edit'
|
|
41
|
+
currentMood: StripSceneUIState
|
|
42
|
+
moods: StripSceneUIState[]
|
|
43
|
+
onSave: () => void
|
|
44
|
+
moduleParams: FantasyMoodPageProps
|
|
45
|
+
}
|
|
46
|
+
const FantasyMoodEditPage = () => {
|
|
47
|
+
const navigation = useNavigation<StackNavigationProp<any>>()
|
|
48
|
+
const routeParams = useRoute().params as FantasyMoodEditParams
|
|
49
|
+
const params = cloneDeep(routeParams)
|
|
50
|
+
const moduleParams = params.moduleParams
|
|
51
|
+
const deviceId = useDeviceId()
|
|
52
|
+
const [, setWorkMode] = useWorkMode(moduleParams.workModeDpCode)
|
|
53
|
+
const [, setFantasyScene] = useFantasyScene(params.moduleParams.sceneDpCode, params.moduleParams.workModeDpCode)
|
|
54
|
+
const role = useRole()
|
|
55
|
+
const state = useReactive<FantasyMoodEditPageState>({
|
|
56
|
+
headline: '',
|
|
57
|
+
mood: params.currentMood,
|
|
58
|
+
currentNode: params.currentMood.nodes[params.currentMood.nodes.length - 1],
|
|
59
|
+
paintBucketSelected: false,
|
|
60
|
+
sceneMode: params.moduleParams.isStringLight ? stringLightSceneMode : stripLightSceneMode
|
|
61
|
+
})
|
|
62
|
+
const canDelete = useMemo(() => (role === 0 || role === 2), [role])
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
state.headline = I18n.getLang(params.mode ? 'add_new_dynamic_mood_headline_text' : 'edit_static_mood_headline_text')
|
|
66
|
+
}, [params.mode])
|
|
67
|
+
|
|
68
|
+
const getColorBlockColor = useCallback(() => {
|
|
69
|
+
const s = Math.round(mapFloatToRange(state.currentNode.s / 100, 30, 100))
|
|
70
|
+
if (state.currentNode.isColorNode) {
|
|
71
|
+
return hsv2Hex(state.currentNode.h, s, 100)
|
|
72
|
+
}
|
|
73
|
+
if (moduleParams.isSupportTemperature) {
|
|
74
|
+
return cctToColor(state.currentNode.colorTemp.toFixed())
|
|
75
|
+
}
|
|
76
|
+
}, [state.currentNode, state.currentNode.colorTemp])
|
|
77
|
+
const getNodeColor = useCallback((node: StripNodeInfo) => {
|
|
78
|
+
if (node.isColorNode) {
|
|
79
|
+
const s = Math.round(mapFloatToRange(node.s / 100, 30, 100))
|
|
80
|
+
return hsv2Hex(node.h, s, 100)
|
|
81
|
+
}
|
|
82
|
+
return cctToColor(node.colorTemp.toFixed())
|
|
83
|
+
}, [])
|
|
84
|
+
|
|
85
|
+
const onPost = useCallback(async (isDelete: boolean) => {
|
|
86
|
+
const maxModeObject = maxBy(params.moods, 'mode');
|
|
87
|
+
const currentMood = {
|
|
88
|
+
...state.mood,
|
|
89
|
+
nodes: state.mood.nodes.map(node => {
|
|
90
|
+
if (node.isColorNode) {
|
|
91
|
+
node.brightness = 0;
|
|
92
|
+
node.colorTemp = 0;
|
|
93
|
+
} else {
|
|
94
|
+
node.h = 0
|
|
95
|
+
node.s = 0
|
|
96
|
+
node.v = 0
|
|
97
|
+
}
|
|
98
|
+
return node
|
|
99
|
+
}),
|
|
100
|
+
id: params.mode === 'add' ? maxModeObject && maxModeObject.mode > 200 ? maxModeObject.mode + 1 : 201 : state.mood.id
|
|
101
|
+
}
|
|
102
|
+
const list = params.mode === 'add' ? [currentMood,...params.moods] :
|
|
103
|
+
isDelete ? params.moods.filter(mood => mood.id !== currentMood.id) :
|
|
104
|
+
params.moods.map(mood => {
|
|
105
|
+
return mood.id === currentMood.id ? currentMood : mood
|
|
106
|
+
})
|
|
107
|
+
const saveRes = await saveFantasyScene(deviceId, params.moduleParams.featureId, list)
|
|
108
|
+
if (saveRes.success) {
|
|
109
|
+
if (list.length > 0) {
|
|
110
|
+
if (isDelete) {
|
|
111
|
+
|
|
112
|
+
} else {
|
|
113
|
+
await setFantasyScene(currentMood)
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
await setWorkMode(COLOUR)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
navigation.navigate(ui_biz_routerKey.ui_biz_fantasy_mood)
|
|
120
|
+
routeParams.onSave()
|
|
121
|
+
}, [])
|
|
122
|
+
|
|
123
|
+
const createSelectPageData = useCallback(() => {
|
|
124
|
+
return Object.values(state.sceneMode).map(scene => {
|
|
125
|
+
return {
|
|
126
|
+
text: scene.title,
|
|
127
|
+
selected: scene.mode === state.mood.mode,
|
|
128
|
+
value: scene.mode,
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
}, [state.mood.mode, state.sceneMode])
|
|
132
|
+
|
|
133
|
+
const getButtonStatus = () => {
|
|
134
|
+
return (params.mode === 'edit' && isEqual(state.mood, routeParams.currentMood)) ||
|
|
135
|
+
!(!!state.mood.name) ||
|
|
136
|
+
nameRepeat ||
|
|
137
|
+
state.mood.name.length > 32 ||
|
|
138
|
+
state.mood.nodes.length < 2
|
|
139
|
+
}
|
|
140
|
+
console.log(state.mood, '< -- mood')
|
|
141
|
+
console.log(state.sceneMode, '< --- sceneMode')
|
|
142
|
+
const nameRepeat = useMemo(() => {
|
|
143
|
+
return !!find(params.moods, m => (m.id !== state.mood.id && m.name === state.mood.name))
|
|
144
|
+
}, [state.mood.name])
|
|
145
|
+
return (
|
|
146
|
+
<Page
|
|
147
|
+
backText={I18n.getLang('mesh_device_detail_mode')}
|
|
148
|
+
showBackDialog={true}
|
|
149
|
+
backDialogTitle={
|
|
150
|
+
I18n.getLang(params.mode === 'add' ?
|
|
151
|
+
'string_light_pp_dialog_sm_add_headline_c' :
|
|
152
|
+
'manage_user_unsaved_changes_dialog_headline')
|
|
153
|
+
}
|
|
154
|
+
backDialogContent={
|
|
155
|
+
I18n.getLang(params.mode === 'add' ?
|
|
156
|
+
'strip_light_static_mood_add_step_2_dialog_text' :
|
|
157
|
+
'strip_light_static_mood_editor_step_2_dialog_text')
|
|
158
|
+
}
|
|
159
|
+
headlineText={state.headline}
|
|
160
|
+
rightButtonIcon={getButtonStatus() ? res.ic_uncheck : res.ic_check}
|
|
161
|
+
rightButtonDisabled={getButtonStatus()}
|
|
162
|
+
rightButtonIconClick={async () => {
|
|
163
|
+
await onPost(false)
|
|
164
|
+
}}>
|
|
165
|
+
<ScrollView
|
|
166
|
+
style={{ flex: 1 }}
|
|
167
|
+
nestedScrollEnabled={true}>
|
|
168
|
+
<View style={styles.root}>
|
|
169
|
+
<TextField
|
|
170
|
+
style={styles.name}
|
|
171
|
+
value={state.mood.name}
|
|
172
|
+
placeholder={I18n.getLang('edit_static_mood_inputfield_topic_text')}
|
|
173
|
+
onChangeText={text => {
|
|
174
|
+
state.mood.name = text
|
|
175
|
+
}}
|
|
176
|
+
showError={state.mood.name.length > 32 || nameRepeat}
|
|
177
|
+
tipColor={nameRepeat ? '#f00' : undefined}
|
|
178
|
+
tipIcon={nameRepeat ? res.ic_text_field_input_error : undefined}
|
|
179
|
+
errorText={I18n.getLang(nameRepeat ? 'string_light_pp_field_sm_add_error1' : 'add_new_dynamic_mood_alert_text')} />
|
|
180
|
+
<Card style={styles.adjustCard}>
|
|
181
|
+
<Spacer height={cx(16)} />
|
|
182
|
+
<View style={styles.lightLine}>
|
|
183
|
+
<Text style={styles.light}>
|
|
184
|
+
{I18n.getLang('light_sources_tile_tw_lighting_headline')}
|
|
185
|
+
</Text>
|
|
186
|
+
</View>
|
|
187
|
+
<Spacer height={cx(18)} />
|
|
188
|
+
<TextFieldStyleButton
|
|
189
|
+
style={styles.transitionMode}
|
|
190
|
+
text={state.sceneMode[state.mood.mode]?.title}
|
|
191
|
+
placeholder={I18n.getLang('add_new_dynamic_mood_color_changing_mode_headline')}
|
|
192
|
+
onPress={() => {
|
|
193
|
+
const paramsSelect: SelectPageParams<number> = {
|
|
194
|
+
title: I18n.getLang('add_new_dynamic_mood_color_changing_mode_headline'),
|
|
195
|
+
data: createSelectPageData(),
|
|
196
|
+
onSelect: selectPageData => {
|
|
197
|
+
state.mood.mode = selectPageData.value
|
|
198
|
+
},
|
|
199
|
+
}
|
|
200
|
+
navigation.navigate(ui_biz_routerKey.ui_biz_select_page, paramsSelect)
|
|
201
|
+
}} />
|
|
202
|
+
<Spacer height={cx(10)} />
|
|
203
|
+
<LdvSlider
|
|
204
|
+
title={I18n.getLang('add_new_dynamic_mood_lights_field_speed_topic_text')}
|
|
205
|
+
value={state.mood.speed}
|
|
206
|
+
onValueChange={() => { }}
|
|
207
|
+
onSlidingComplete={value => {
|
|
208
|
+
state.mood.speed = value
|
|
209
|
+
}} />
|
|
210
|
+
<Spacer height={cx(16)} />
|
|
211
|
+
{state.sceneMode[state.mood.mode]?.turnOn && <View style={styles.transitionMode}>
|
|
212
|
+
<Text style={{ color: '#000', fontSize: cx(14) }}>Turn to</Text>
|
|
213
|
+
<Segmented
|
|
214
|
+
value={state.mood.direction}
|
|
215
|
+
options={[
|
|
216
|
+
{
|
|
217
|
+
label: 'Positive',
|
|
218
|
+
value: 0
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
label: 'Negative',
|
|
222
|
+
value: 1
|
|
223
|
+
},
|
|
224
|
+
]}
|
|
225
|
+
onChange={(v) => state.mood.direction = Number(v)}
|
|
226
|
+
/>
|
|
227
|
+
<Spacer />
|
|
228
|
+
</View>}
|
|
229
|
+
{state.sceneMode[state.mood.mode]?.paragraph && <View style={styles.transitionMode}>
|
|
230
|
+
<Text style={{ color: '#000', fontSize: cx(14) }}>Paragraph</Text>
|
|
231
|
+
<Segmented
|
|
232
|
+
value={state.mood.segmented}
|
|
233
|
+
options={[
|
|
234
|
+
{
|
|
235
|
+
label: 'Paragraph',
|
|
236
|
+
value: 0
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
label: 'Segmented',
|
|
240
|
+
value: 1
|
|
241
|
+
},
|
|
242
|
+
]}
|
|
243
|
+
onChange={(v) => state.mood.segmented = Number(v)}
|
|
244
|
+
/>
|
|
245
|
+
<Spacer />
|
|
246
|
+
</View>}
|
|
247
|
+
{state.sceneMode[state.mood.mode]?.other && <View style={styles.transitionMode}>
|
|
248
|
+
<Text style={{ color: '#000', fontSize: cx(14) }}>Other</Text>
|
|
249
|
+
<Segmented
|
|
250
|
+
value={state.mood.direction}
|
|
251
|
+
options={state.sceneMode[state.mood.mode]?.other}
|
|
252
|
+
onChange={(v) => state.mood.direction = Number(v)}
|
|
253
|
+
/>
|
|
254
|
+
<Spacer />
|
|
255
|
+
</View>}
|
|
256
|
+
<View style={styles.nodesAdjust}>
|
|
257
|
+
<View style={styles.adjustButtons}>
|
|
258
|
+
<TouchableOpacity
|
|
259
|
+
onPress={() => {
|
|
260
|
+
state.paintBucketSelected = true
|
|
261
|
+
}}>
|
|
262
|
+
<Image
|
|
263
|
+
style={[styles.adjustButton, { tintColor: state.paintBucketSelected ? '#f60' : '#666' }]}
|
|
264
|
+
source={res.ic_paint_bucket} />
|
|
265
|
+
</TouchableOpacity>
|
|
266
|
+
<TouchableOpacity
|
|
267
|
+
onPress={() => {
|
|
268
|
+
state.paintBucketSelected = false
|
|
269
|
+
}}>
|
|
270
|
+
<Image
|
|
271
|
+
style={[styles.adjustButton, { tintColor: state.paintBucketSelected ? '#666' : '#f60' }]}
|
|
272
|
+
source={res.ic_colorize} />
|
|
273
|
+
</TouchableOpacity>
|
|
274
|
+
</View>
|
|
275
|
+
<FlatList
|
|
276
|
+
data={state.mood.nodes}
|
|
277
|
+
style={styles.nodeList}
|
|
278
|
+
renderItem={({ item, index }) => {
|
|
279
|
+
return (
|
|
280
|
+
<View style={styles.nodeItem}>
|
|
281
|
+
<TouchableOpacity
|
|
282
|
+
style={[
|
|
283
|
+
styles.nodeBlock,
|
|
284
|
+
{
|
|
285
|
+
backgroundColor: getNodeColor(item),
|
|
286
|
+
},
|
|
287
|
+
]}
|
|
288
|
+
onPress={() => {
|
|
289
|
+
state.currentNode = item
|
|
290
|
+
}} />
|
|
291
|
+
<TouchableOpacity
|
|
292
|
+
style={styles.nodeDeleteBtn}
|
|
293
|
+
disabled={state.mood.nodes.length < 3}
|
|
294
|
+
onPress={() => {
|
|
295
|
+
state.mood.nodes.splice(index, 1)
|
|
296
|
+
state.currentNode = state.mood.nodes[state.mood.nodes.length - 1]
|
|
297
|
+
}}>
|
|
298
|
+
<Image
|
|
299
|
+
style={[
|
|
300
|
+
styles.nodeDeleteIcon,
|
|
301
|
+
{
|
|
302
|
+
tintColor: state.mood.nodes.length < 3 ? '#ccc' : '#666',
|
|
303
|
+
},
|
|
304
|
+
]}
|
|
305
|
+
source={res.ic_mood_del} />
|
|
306
|
+
</TouchableOpacity>
|
|
307
|
+
</View>
|
|
308
|
+
)
|
|
309
|
+
}}
|
|
310
|
+
keyExtractor={(_, index) => `${index}`}
|
|
311
|
+
ItemSeparatorComponent={() => <Spacer height={cx(12)} />}
|
|
312
|
+
ListFooterComponent={() => {
|
|
313
|
+
if (state.mood.nodes.length >= 8) {
|
|
314
|
+
return (<></>)
|
|
315
|
+
}
|
|
316
|
+
return (
|
|
317
|
+
<View>
|
|
318
|
+
<Spacer height={cx(12)} />
|
|
319
|
+
<TouchableOpacity
|
|
320
|
+
style={styles.nodeAddBtn}
|
|
321
|
+
onPress={() => {
|
|
322
|
+
const node = {
|
|
323
|
+
...state.currentNode,
|
|
324
|
+
}
|
|
325
|
+
state.mood.nodes.push(node)
|
|
326
|
+
state.currentNode = node
|
|
327
|
+
}}>
|
|
328
|
+
<Image
|
|
329
|
+
style={{
|
|
330
|
+
width: cx(18),
|
|
331
|
+
height: cx(18),
|
|
332
|
+
tintColor: '#000',
|
|
333
|
+
}}
|
|
334
|
+
source={{ uri: res.add }} />
|
|
335
|
+
</TouchableOpacity>
|
|
336
|
+
</View>
|
|
337
|
+
)
|
|
338
|
+
}} />
|
|
339
|
+
</View>
|
|
340
|
+
<Spacer />
|
|
341
|
+
<View style={styles.lightLine}>
|
|
342
|
+
<Text style={styles.light}>
|
|
343
|
+
{I18n.getLang('add_new_dynamic_mood_lights_field_headline2_text')}
|
|
344
|
+
</Text>
|
|
345
|
+
<View style={[styles.preview, { backgroundColor: getColorBlockColor() }]} />
|
|
346
|
+
</View>
|
|
347
|
+
<Spacer />
|
|
348
|
+
<LampAdjustView
|
|
349
|
+
isSupportColor={!moduleParams.isStripLight && moduleParams.isSupportColor}
|
|
350
|
+
isSupportBrightness={!moduleParams.isStripLight && moduleParams.isSupportBrightness}
|
|
351
|
+
isSupportTemperature={moduleParams.isSupportTemperature}
|
|
352
|
+
isColorMode={state.currentNode.isColorNode}
|
|
353
|
+
reserveSV={true}
|
|
354
|
+
setIsColorMode={isColorMode => {
|
|
355
|
+
if (state.paintBucketSelected) {
|
|
356
|
+
state.mood.nodes.forEach(node => {
|
|
357
|
+
node.isColorNode = isColorMode
|
|
358
|
+
})
|
|
359
|
+
} else {
|
|
360
|
+
state.currentNode.isColorNode = isColorMode
|
|
361
|
+
}
|
|
362
|
+
}}
|
|
363
|
+
h={state.currentNode.h}
|
|
364
|
+
s={state.currentNode.s}
|
|
365
|
+
v={state.currentNode.v}
|
|
366
|
+
onHSVChange={(h, s, v) => {
|
|
367
|
+
if (state.paintBucketSelected) {
|
|
368
|
+
state.mood.nodes.forEach(node => {
|
|
369
|
+
node.isColorNode = true
|
|
370
|
+
node.h = h
|
|
371
|
+
node.s = s
|
|
372
|
+
node.v = v
|
|
373
|
+
})
|
|
374
|
+
} else {
|
|
375
|
+
state.currentNode.h = h
|
|
376
|
+
state.currentNode.s = s
|
|
377
|
+
state.currentNode.v = v
|
|
378
|
+
}
|
|
379
|
+
}}
|
|
380
|
+
onHSVChangeComplete={(h, s, v) => {
|
|
381
|
+
if (state.paintBucketSelected) {
|
|
382
|
+
state.mood.nodes.forEach(node => {
|
|
383
|
+
node.isColorNode = true
|
|
384
|
+
node.h = h
|
|
385
|
+
node.s = s
|
|
386
|
+
node.v = v
|
|
387
|
+
})
|
|
388
|
+
} else {
|
|
389
|
+
state.currentNode.h = h
|
|
390
|
+
state.currentNode.s = s
|
|
391
|
+
state.currentNode.v = v
|
|
392
|
+
}
|
|
393
|
+
}}
|
|
394
|
+
colorTemp={state.currentNode.colorTemp}
|
|
395
|
+
brightness={state.currentNode.brightness}
|
|
396
|
+
onCCTChange={cct => {
|
|
397
|
+
if (state.paintBucketSelected) {
|
|
398
|
+
state.mood.nodes.forEach(node => {
|
|
399
|
+
node.isColorNode = false
|
|
400
|
+
node.colorTemp = cct
|
|
401
|
+
})
|
|
402
|
+
} else {
|
|
403
|
+
state.currentNode.colorTemp = cct
|
|
404
|
+
}
|
|
405
|
+
}}
|
|
406
|
+
onCCTChangeComplete={cct => {
|
|
407
|
+
if (state.paintBucketSelected) {
|
|
408
|
+
state.mood.nodes.forEach(node => {
|
|
409
|
+
node.isColorNode = false
|
|
410
|
+
node.colorTemp = cct
|
|
411
|
+
})
|
|
412
|
+
} else {
|
|
413
|
+
state.currentNode.colorTemp = cct
|
|
414
|
+
}
|
|
415
|
+
}}
|
|
416
|
+
onBrightnessChange={brightness => {
|
|
417
|
+
if (state.paintBucketSelected) {
|
|
418
|
+
state.mood.nodes.forEach(node => {
|
|
419
|
+
node.isColorNode = false
|
|
420
|
+
node.brightness = brightness
|
|
421
|
+
})
|
|
422
|
+
} else {
|
|
423
|
+
state.currentNode.brightness = brightness
|
|
424
|
+
}
|
|
425
|
+
}}
|
|
426
|
+
onBrightnessChangeComplete={brightness => {
|
|
427
|
+
if (state.paintBucketSelected) {
|
|
428
|
+
state.mood.nodes.forEach(node => {
|
|
429
|
+
node.isColorNode = false
|
|
430
|
+
node.brightness = brightness
|
|
431
|
+
})
|
|
432
|
+
} else {
|
|
433
|
+
state.currentNode.brightness = brightness
|
|
434
|
+
}
|
|
435
|
+
}} />
|
|
436
|
+
</Card>
|
|
437
|
+
<Spacer />
|
|
438
|
+
{params.mode === 'edit' && canDelete &&
|
|
439
|
+
<View style={{ marginTop: cx(20), marginHorizontal: cx(24) }}>
|
|
440
|
+
<TextButton
|
|
441
|
+
style={styles.deleteBtn}
|
|
442
|
+
textStyle={styles.deleteBtnText}
|
|
443
|
+
text={I18n.getLang('edit_static_mood_button_delete_text')}
|
|
444
|
+
onPress={() => {
|
|
445
|
+
showDeleteMoodDialog(async (_, { close }) => {
|
|
446
|
+
close()
|
|
447
|
+
await onPost(true)
|
|
448
|
+
})
|
|
449
|
+
}} />
|
|
450
|
+
</View>}
|
|
451
|
+
<Spacer />
|
|
452
|
+
</View>
|
|
453
|
+
</ScrollView>
|
|
454
|
+
</Page>
|
|
455
|
+
)
|
|
456
|
+
}
|
|
457
|
+
const styles = StyleSheet.create({
|
|
458
|
+
root: {
|
|
459
|
+
flex: 1,
|
|
460
|
+
flexDirection: 'column',
|
|
461
|
+
},
|
|
462
|
+
name: {
|
|
463
|
+
marginHorizontal: cx(24),
|
|
464
|
+
},
|
|
465
|
+
adjustCard: {
|
|
466
|
+
marginVertical: cx(12),
|
|
467
|
+
marginHorizontal: cx(24),
|
|
468
|
+
},
|
|
469
|
+
fanAdjustCard: {
|
|
470
|
+
marginHorizontal: cx(24),
|
|
471
|
+
},
|
|
472
|
+
lightLine: {
|
|
473
|
+
flexDirection: 'row',
|
|
474
|
+
marginHorizontal: cx(16),
|
|
475
|
+
},
|
|
476
|
+
light: {
|
|
477
|
+
color: '#000',
|
|
478
|
+
fontSize: cx(18),
|
|
479
|
+
fontFamily: 'helvetica_neue_lt_std_bd',
|
|
480
|
+
},
|
|
481
|
+
transitionMode: {
|
|
482
|
+
marginHorizontal: cx(16),
|
|
483
|
+
},
|
|
484
|
+
preview: {
|
|
485
|
+
width: cx(20),
|
|
486
|
+
height: cx(20),
|
|
487
|
+
marginStart: cx(12),
|
|
488
|
+
borderRadius: cx(4),
|
|
489
|
+
},
|
|
490
|
+
nodesAdjust: {
|
|
491
|
+
flexDirection: 'row',
|
|
492
|
+
alignItems: 'center',
|
|
493
|
+
},
|
|
494
|
+
adjustButtons: {
|
|
495
|
+
width: cx(44),
|
|
496
|
+
marginStart: cx(16),
|
|
497
|
+
},
|
|
498
|
+
adjustButton: {
|
|
499
|
+
width: cx(44),
|
|
500
|
+
height: cx(44),
|
|
501
|
+
},
|
|
502
|
+
nodeList: {
|
|
503
|
+
flex: 1,
|
|
504
|
+
marginHorizontal: cx(16),
|
|
505
|
+
},
|
|
506
|
+
nodeItem: {
|
|
507
|
+
flexDirection: 'row',
|
|
508
|
+
alignItems: 'center',
|
|
509
|
+
},
|
|
510
|
+
nodeBlock: {
|
|
511
|
+
flex: 1,
|
|
512
|
+
height: cx(40),
|
|
513
|
+
borderRadius: cx(8),
|
|
514
|
+
},
|
|
515
|
+
nodeDeleteBtn: {
|
|
516
|
+
width: cx(24),
|
|
517
|
+
height: cx(30),
|
|
518
|
+
justifyContent: 'center',
|
|
519
|
+
alignItems: 'center',
|
|
520
|
+
},
|
|
521
|
+
nodeDeleteIcon: {
|
|
522
|
+
width: cx(16),
|
|
523
|
+
height: cx(16),
|
|
524
|
+
},
|
|
525
|
+
nodeAddBtn: {
|
|
526
|
+
height: cx(40),
|
|
527
|
+
justifyContent: 'center',
|
|
528
|
+
alignItems: 'center',
|
|
529
|
+
marginEnd: cx(26),
|
|
530
|
+
borderRadius: cx(8),
|
|
531
|
+
borderWidth: cx(1),
|
|
532
|
+
borderStyle: 'dashed',
|
|
533
|
+
borderColor: '#666',
|
|
534
|
+
backgroundColor: '#f6f6f6',
|
|
535
|
+
},
|
|
536
|
+
deleteBtn: {
|
|
537
|
+
width: '100%',
|
|
538
|
+
height: cx(50),
|
|
539
|
+
backgroundColor: '#666',
|
|
540
|
+
borderRadius: cx(8),
|
|
541
|
+
},
|
|
542
|
+
deleteBtnText: {
|
|
543
|
+
color: '#fff',
|
|
544
|
+
fontSize: cx(16),
|
|
545
|
+
fontFamily: 'helvetica_neue_lt_std_bd',
|
|
546
|
+
},
|
|
547
|
+
})
|
|
548
|
+
export default FantasyMoodEditPage
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React, { useEffect, memo } from 'react'
|
|
2
|
+
import { StyleSheet, Text, View, ViewProps, ViewStyle } from 'react-native'
|
|
3
|
+
import { StripSceneUIState } from '../scene/SceneInfo'
|
|
4
|
+
import { SwitchButton, Utils } from 'tuya-panel-kit'
|
|
5
|
+
import { useReactive } from 'ahooks'
|
|
6
|
+
import { hsv2Hex } from '@ledvance/base/src/utils'
|
|
7
|
+
import {cctToColor} from '@ledvance/base/src/utils/cctUtils'
|
|
8
|
+
import {mapFloatToRange} from '@ledvance/base/src/utils'
|
|
9
|
+
import Card from '@ledvance/base/src/components/Card'
|
|
10
|
+
import Spacer from '@ledvance/base/src/components/Spacer'
|
|
11
|
+
import MoodColorsLine from '@ledvance/base/src/components/MoodColorsLine'
|
|
12
|
+
|
|
13
|
+
const cx = Utils.RatioUtils.convertX
|
|
14
|
+
|
|
15
|
+
interface FantasyMoodItemProps extends ViewProps {
|
|
16
|
+
enable: boolean
|
|
17
|
+
mood: StripSceneUIState
|
|
18
|
+
style?: ViewStyle
|
|
19
|
+
onPress?: () => void
|
|
20
|
+
onSwitch: (enable: boolean) => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const FantasyMoodItem = (props: FantasyMoodItemProps) => {
|
|
24
|
+
const { mood } = props
|
|
25
|
+
const state = useReactive<{ colors: string[] }>({
|
|
26
|
+
colors: [],
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
state.colors = props.mood.nodes.map(node => {
|
|
31
|
+
if (node.isColorNode) {
|
|
32
|
+
return hsv2Hex(node.h, Math.round(node.s), Math.round(mapFloatToRange(node.v / 100, 50, 100)))
|
|
33
|
+
}
|
|
34
|
+
return cctToColor(node.colorTemp.toFixed())
|
|
35
|
+
})
|
|
36
|
+
}, [props.mood.nodes])
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Card
|
|
40
|
+
style={[styles.card, props.style]}
|
|
41
|
+
onPress={props.onPress}>
|
|
42
|
+
<View>
|
|
43
|
+
<Spacer height={cx(16)} />
|
|
44
|
+
<View style={styles.headline}>
|
|
45
|
+
<Text style={styles.headText}>{mood.name}</Text>
|
|
46
|
+
<SwitchButton
|
|
47
|
+
thumbStyle={{ elevation: 0 }}
|
|
48
|
+
value={props.enable}
|
|
49
|
+
onValueChange={props.onSwitch} />
|
|
50
|
+
</View>
|
|
51
|
+
<Spacer />
|
|
52
|
+
<View style={styles.gradientItem}>
|
|
53
|
+
<MoodColorsLine
|
|
54
|
+
type={'separate'}
|
|
55
|
+
colors={state.colors} />
|
|
56
|
+
</View>
|
|
57
|
+
<Spacer height={cx(16)} />
|
|
58
|
+
</View>
|
|
59
|
+
</Card>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default memo(FantasyMoodItem)
|
|
64
|
+
|
|
65
|
+
const styles = StyleSheet.create({
|
|
66
|
+
card: {
|
|
67
|
+
marginHorizontal: cx(24),
|
|
68
|
+
},
|
|
69
|
+
headline: {
|
|
70
|
+
flexDirection: 'row',
|
|
71
|
+
marginHorizontal: cx(16),
|
|
72
|
+
},
|
|
73
|
+
headText: {
|
|
74
|
+
flex: 1,
|
|
75
|
+
color: '#000',
|
|
76
|
+
fontSize: cx(16),
|
|
77
|
+
fontFamily: 'helvetica_neue_lt_std_bd',
|
|
78
|
+
lineHeight: cx(20),
|
|
79
|
+
},
|
|
80
|
+
gradientItem: {
|
|
81
|
+
alignItems: 'center',
|
|
82
|
+
},
|
|
83
|
+
gradient: {
|
|
84
|
+
borderRadius: cx(8),
|
|
85
|
+
},
|
|
86
|
+
moodTypeItem: {
|
|
87
|
+
flexDirection: 'row',
|
|
88
|
+
},
|
|
89
|
+
moodTypeLabel: {
|
|
90
|
+
marginStart: cx(16),
|
|
91
|
+
paddingHorizontal: cx(12.5),
|
|
92
|
+
backgroundColor: '#E6E7E8',
|
|
93
|
+
borderRadius: cx(8),
|
|
94
|
+
},
|
|
95
|
+
moodTypeLabelText: {
|
|
96
|
+
height: cx(16),
|
|
97
|
+
color: '#000000DD',
|
|
98
|
+
fontSize: cx(10),
|
|
99
|
+
textAlignVertical: 'center',
|
|
100
|
+
fontFamily: 'helvetica_neue_lt_std_roman',
|
|
101
|
+
lineHeight: cx(16),
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
|
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import {NativeResult, Result} from '@ledvance/base/src/models/modules/Result'
|
|
2
2
|
import {
|
|
3
3
|
getDefSceneList,
|
|
4
|
+
getDefFantasyScene,
|
|
4
5
|
RemoteSceneInfo,
|
|
5
6
|
SceneInfo,
|
|
6
7
|
SceneNodeInfo,
|
|
7
8
|
SceneNodeTransitionMode,
|
|
8
9
|
SceneUIState,
|
|
10
|
+
StripSceneUIState,
|
|
11
|
+
StripSceneInfo,
|
|
9
12
|
} from './SceneInfo'
|
|
10
|
-
import {getFeature, putFeature} from '@ledvance/base/src/api/native'
|
|
13
|
+
import {getFeature, putFeature, NativeApi} from '@ledvance/base/src/api/native'
|
|
11
14
|
import {setSceneDp} from '../../hooks/DeviceDpStateHooks'
|
|
12
15
|
import {hex2Int, spliceByStep} from '@ledvance/base/src/utils/common'
|
|
13
|
-
import {asyncSetDps, useDp} from '@ledvance/base/src/models/modules/NativePropsSlice'
|
|
16
|
+
import {asyncSetDps, useDp, useDps} from '@ledvance/base/src/models/modules/NativePropsSlice'
|
|
14
17
|
import {Dispatch, useState} from 'react'
|
|
15
18
|
import {useUpdateEffect} from 'ahooks'
|
|
16
19
|
import {useDispatch} from 'react-redux'
|
|
17
20
|
import res from '@ledvance/base/src/res'
|
|
21
|
+
import { SCENE } from '../../hooks/DeviceDpStateHooks'
|
|
22
|
+
import { Utils } from '@tuya/tuya-panel-lamp-sdk'
|
|
23
|
+
const { nToHS, toFixed, sToN, toN, formatterTransform } = Utils
|
|
18
24
|
|
|
19
25
|
type SetRemoteSceneListType = (
|
|
20
26
|
deviceId: string,
|
|
@@ -266,3 +272,150 @@ export async function saveScene(
|
|
|
266
272
|
})
|
|
267
273
|
return await setRemoteSceneList(deviceId, newScenes)
|
|
268
274
|
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
export function stripDp2Obj(dp: string): StripSceneInfo {
|
|
278
|
+
const version = hex2Int(dp.slice(0, 2))
|
|
279
|
+
const id = hex2Int(dp.slice(2, 4))
|
|
280
|
+
const mode = hex2Int(dp.slice(4, 6))
|
|
281
|
+
const intervalTime = hex2Int(dp.slice(6, 8))
|
|
282
|
+
const changeTime = hex2Int(dp.slice(8, 10))
|
|
283
|
+
const speed = intervalTime || changeTime
|
|
284
|
+
const optionA = toN(dp.slice(10, 12))
|
|
285
|
+
const optionAStr = toFixed(optionA.toString(2), 8);
|
|
286
|
+
const gn = formatterTransform(optionAStr);
|
|
287
|
+
const st = (n?: number) => gn.next(n);
|
|
288
|
+
st();
|
|
289
|
+
const segmented = sToN(st(1).value, 2)
|
|
290
|
+
const loop = sToN(st(1).value, 2)
|
|
291
|
+
const excessive = sToN(st(1).value, 2)
|
|
292
|
+
const direction = sToN(st(1).value, 2)
|
|
293
|
+
// 这两个目前灯具用不上
|
|
294
|
+
const optionB = toN(dp.slice(12, 14))
|
|
295
|
+
const optionC = toN(dp.slice(14, 16))
|
|
296
|
+
const nodes = spliceByStep(dp.slice(16, dp.length), 16).map(nodeHex => {
|
|
297
|
+
const v = hex2Int(nodeHex.slice(0, 2))
|
|
298
|
+
const h = hex2Int(nodeHex.slice(2, 6))
|
|
299
|
+
const s = hex2Int(nodeHex.slice(6, 8))
|
|
300
|
+
const brightness = hex2Int(nodeHex.slice(8, 12)) / 10
|
|
301
|
+
const colorTemp = hex2Int(nodeHex.slice(12, 16)) / 10
|
|
302
|
+
const isColorNode = v > 0 && brightness === 0
|
|
303
|
+
return { h, s, v, brightness, colorTemp, isColorNode }
|
|
304
|
+
})
|
|
305
|
+
return {
|
|
306
|
+
version,
|
|
307
|
+
id,
|
|
308
|
+
mode,
|
|
309
|
+
speed,
|
|
310
|
+
direction,
|
|
311
|
+
segmented,
|
|
312
|
+
loop,
|
|
313
|
+
excessive,
|
|
314
|
+
optionB,
|
|
315
|
+
optionC,
|
|
316
|
+
nodes
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function stripObj2Dp(scene: StripSceneInfo) {
|
|
321
|
+
const {
|
|
322
|
+
optionB = 0,
|
|
323
|
+
optionC = 0,
|
|
324
|
+
// mixedIds = [],
|
|
325
|
+
segmented = 0,
|
|
326
|
+
loop = 0,
|
|
327
|
+
excessive = 0,
|
|
328
|
+
direction = 0,
|
|
329
|
+
nodes
|
|
330
|
+
} = scene
|
|
331
|
+
const expand = 0
|
|
332
|
+
const versionHex = nToHS(scene.version)
|
|
333
|
+
const idHex = nToHS(scene.id)
|
|
334
|
+
const modeHex = nToHS(scene.mode)
|
|
335
|
+
const intervalTimeHex = nToHS(scene.speed)
|
|
336
|
+
const changeTimeHex = nToHS(scene.speed)
|
|
337
|
+
const optionAhex = nToHS(
|
|
338
|
+
parseInt(
|
|
339
|
+
`${segmented}${loop}${excessive}${direction}${toFixed(
|
|
340
|
+
expand.toString(2),
|
|
341
|
+
2
|
|
342
|
+
)}${0}${0}`,
|
|
343
|
+
2
|
|
344
|
+
)
|
|
345
|
+
);
|
|
346
|
+
const optionBhex = nToHS(optionB)
|
|
347
|
+
const optionChex = nToHS(optionC)
|
|
348
|
+
const nodeHex = nodes.map(node => {
|
|
349
|
+
return `${nToHS(node.v)}${nToHS(node.h, 4)}${nToHS(node.s)}${nToHS(node.brightness * 10, 4)}${nToHS(node.colorTemp * 10, 4)}`
|
|
350
|
+
}).join('')
|
|
351
|
+
return versionHex + idHex + modeHex + intervalTimeHex + changeTimeHex + optionAhex + optionBhex + optionChex + nodeHex
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function useFantasyScene(sceneDp: string, workModeDp: string): [number, (scene: StripSceneInfo) => Promise<Result<any>>] {
|
|
355
|
+
const [dps, setDps]: [any, (v: any) => Promise<Result<any>>] = useDps()
|
|
356
|
+
const dp = dps[sceneDp]
|
|
357
|
+
const [sceneState, setSceneState] = useState<StripSceneInfo>(stripDp2Obj(dp))
|
|
358
|
+
|
|
359
|
+
useUpdateEffect(() => {
|
|
360
|
+
setSceneState(stripDp2Obj(dp))
|
|
361
|
+
}, [dp])
|
|
362
|
+
|
|
363
|
+
const setFantasyScene = (scene: StripSceneInfo) => {
|
|
364
|
+
const sceneHex = stripObj2Dp(scene)
|
|
365
|
+
return setDps({
|
|
366
|
+
[sceneDp]: sceneHex,
|
|
367
|
+
[workModeDp]: SCENE
|
|
368
|
+
})
|
|
369
|
+
}
|
|
370
|
+
return [sceneState.id, setFantasyScene]
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
type LampType = {
|
|
374
|
+
isStringLight?: boolean
|
|
375
|
+
isStripLight?: boolean
|
|
376
|
+
}
|
|
377
|
+
export async function getRemoteFantasyScene(featureId: string, devId: string, lampType: LampType) {
|
|
378
|
+
const res = await NativeApi.getJson(devId, featureId)
|
|
379
|
+
if (res.success && res.data) {
|
|
380
|
+
return {
|
|
381
|
+
success: true,
|
|
382
|
+
data: JSON.parse(res.data)?.map(item => remoteFantasySceneInfo2SceneUIState(item)),
|
|
383
|
+
}
|
|
384
|
+
} else {
|
|
385
|
+
if (res.msg?.includes('资源未找到')) {
|
|
386
|
+
const defaultScene = getDefFantasyScene(lampType)
|
|
387
|
+
const res = await NativeApi.putJson(devId, featureId, JSON.stringify(defaultScene))
|
|
388
|
+
if (res.success) {
|
|
389
|
+
return {
|
|
390
|
+
success: true,
|
|
391
|
+
data: defaultScene.map(item => remoteFantasySceneInfo2SceneUIState(item))
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return { success: false }
|
|
395
|
+
}
|
|
396
|
+
return { success: false }
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function remoteFantasySceneInfo2SceneUIState(remoteScene: RemoteSceneInfo): StripSceneUIState {
|
|
402
|
+
const stripSceneInfo = stripDp2Obj(remoteScene.i)
|
|
403
|
+
return {
|
|
404
|
+
...stripSceneInfo,
|
|
405
|
+
name: remoteScene.n,
|
|
406
|
+
image: ''
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export function saveFantasyScene(devId: string, featureId: string, scenes: StripSceneUIState[]) {
|
|
411
|
+
const newScenes: RemoteSceneInfo[] = scenes.map(s => {
|
|
412
|
+
return {
|
|
413
|
+
n: s.name,
|
|
414
|
+
i: stripObj2Dp(s),
|
|
415
|
+
s: '',
|
|
416
|
+
t: 0,
|
|
417
|
+
e: false,
|
|
418
|
+
}
|
|
419
|
+
})
|
|
420
|
+
return NativeApi.putJson(devId, featureId, JSON.stringify(newScenes))
|
|
421
|
+
}
|
|
@@ -32,12 +32,94 @@ export interface SceneNodeInfo {
|
|
|
32
32
|
isColorNode: boolean
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
export interface StripSceneInfo {
|
|
36
|
+
// 版本号
|
|
37
|
+
version: number
|
|
38
|
+
// 场景号
|
|
39
|
+
id: number
|
|
40
|
+
// 变化方式
|
|
41
|
+
mode: number
|
|
42
|
+
// 速度
|
|
43
|
+
speed: number
|
|
44
|
+
// 段落 0 全段, 1 分段
|
|
45
|
+
segmented: number
|
|
46
|
+
// 循环 0 不循环, 1 循环
|
|
47
|
+
loop: number
|
|
48
|
+
// 过渡 0 不过渡, 1 过渡
|
|
49
|
+
excessive: number
|
|
50
|
+
// 方向 0 顺时针方向, 1 逆时针方向
|
|
51
|
+
direction: number
|
|
52
|
+
// 设置 目前灯用不上
|
|
53
|
+
optionB: number
|
|
54
|
+
optionC: number
|
|
55
|
+
nodes: StripNodeInfo[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface StripNodeInfo {
|
|
59
|
+
// 0~360
|
|
60
|
+
h: number
|
|
61
|
+
// 饱和度 0~100
|
|
62
|
+
s: number
|
|
63
|
+
// 明度 1~100
|
|
64
|
+
v: number
|
|
65
|
+
// 灯泡亮度 10~1000 4hex
|
|
66
|
+
brightness: number
|
|
67
|
+
// 色温值 0~1000 4hex
|
|
68
|
+
colorTemp: number
|
|
69
|
+
// 节点类型
|
|
70
|
+
isColorNode: boolean
|
|
71
|
+
}
|
|
72
|
+
|
|
35
73
|
export enum SceneNodeTransitionMode {
|
|
36
74
|
Static,
|
|
37
75
|
Jump,
|
|
38
76
|
Gradient
|
|
39
77
|
}
|
|
40
78
|
|
|
79
|
+
|
|
80
|
+
export interface StripLightSceneMode {
|
|
81
|
+
[key: string]: {
|
|
82
|
+
title: string;
|
|
83
|
+
mode: number;
|
|
84
|
+
turnOn?: boolean;
|
|
85
|
+
paragraph?: boolean;
|
|
86
|
+
other?: {
|
|
87
|
+
label: string
|
|
88
|
+
value: number
|
|
89
|
+
}[];
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const stringLightSceneMode: StripLightSceneMode = {
|
|
94
|
+
'13': { title: 'Flow', mode: 13, turnOn: true }, // 流水
|
|
95
|
+
'2': { title: 'Rainbow', mode: 2, turnOn: true }, // 彩虹
|
|
96
|
+
'14': { title: 'Chase', mode: 14, turnOn: true }, // 追光
|
|
97
|
+
'15': { title: 'Dazzle', mode: 15 }, // 炫彩
|
|
98
|
+
'16': { title: I18n.getLang('other_lights_modes_gradient_text'), mode: 16 }, // 渐变
|
|
99
|
+
'10': { title: I18n.getLang('other_lights_modes_jump_text'), mode: 10 }, // 跳变
|
|
100
|
+
'11': { title: 'Breathing', mode: 11 }, // 呼吸
|
|
101
|
+
'12': { title: 'Blink', mode: 12 } // 闪烁
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const stripLightSceneMode: StripLightSceneMode = {
|
|
105
|
+
'1': { title: 'Jump', mode: 1, paragraph: true }, // 跳变
|
|
106
|
+
'2': { title: 'Breath', mode: 2, paragraph: true }, // 呼吸1
|
|
107
|
+
'3': { title: 'Breathe', mode: 3, paragraph: true }, // 呼吸2
|
|
108
|
+
'4': { title: 'Blink', mode: 4, paragraph: true }, // 闪烁
|
|
109
|
+
'10': { title: 'Flow', mode: 10, turnOn: true }, // 流水
|
|
110
|
+
'11': { title: 'Rainbow', mode: 11, turnOn: true }, // 彩虹
|
|
111
|
+
'5': { title: 'Meteor', mode: 5, turnOn: true, other: [{label: 'Meteor',value: 0}, {label: 'Meteor shower',value: 1},{label: 'Magic meteor',value: 2}] }, // 流星
|
|
112
|
+
'6': { title: 'PileUp', mode: 6, turnOn: true, paragraph: true }, // 堆积
|
|
113
|
+
'7': { title: 'Falling', mode: 7, turnOn: true, paragraph: true }, // 飘落
|
|
114
|
+
'8': { title: 'Follow', mode: 8, turnOn: true }, // 追光
|
|
115
|
+
'9': { title: 'Flutter', mode: 9, turnOn: true }, // 飘动
|
|
116
|
+
'12': { title: 'flash', mode: 12, paragraph: true }, // 闪现
|
|
117
|
+
'13': { title: 'Rebound', mode: 13, other: [{label: 'Rebound',value: 0}, {label: 'Magic rebound',value: 1}]}, // 反弹
|
|
118
|
+
'14': { title: 'Shuttle', mode: 14, }, // 穿梭
|
|
119
|
+
'15': { title: 'Random', mode: 15 }, // 乱闪
|
|
120
|
+
'16': { title: 'Switch', mode: 16, other: [{label: 'Meanwhile',value: 0}, {label: 'Staggered',value: 1}] } // 开合
|
|
121
|
+
}
|
|
122
|
+
|
|
41
123
|
/**
|
|
42
124
|
* 萤石云端 Scene 物模型
|
|
43
125
|
*/
|
|
@@ -70,6 +152,17 @@ export interface SceneUIState extends SceneInfo {
|
|
|
70
152
|
image: string
|
|
71
153
|
}
|
|
72
154
|
|
|
155
|
+
export interface StripScenePageUIState {
|
|
156
|
+
currentScene: StripSceneUIState | undefined
|
|
157
|
+
scenes: StripSceneUIState[]
|
|
158
|
+
flag: symbol
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface StripSceneUIState extends StripSceneInfo {
|
|
162
|
+
name: string
|
|
163
|
+
image: string
|
|
164
|
+
}
|
|
165
|
+
|
|
73
166
|
export function getDefSceneList(
|
|
74
167
|
isRGBWLamp: boolean = false,
|
|
75
168
|
isRGBLamp: boolean = false,
|
|
@@ -99,6 +192,16 @@ export function getDefSceneList(
|
|
|
99
192
|
return []
|
|
100
193
|
}
|
|
101
194
|
|
|
195
|
+
export function getDefFantasyScene({
|
|
196
|
+
isStringLight = false,
|
|
197
|
+
isStripLight = false
|
|
198
|
+
}) {
|
|
199
|
+
if (isStringLight || isStripLight) {
|
|
200
|
+
return getStringLightSceneList()
|
|
201
|
+
}
|
|
202
|
+
return getStringLightSceneList()
|
|
203
|
+
}
|
|
204
|
+
|
|
102
205
|
function getRGBWDefSceneList(): RemoteSceneInfo[] {
|
|
103
206
|
return [
|
|
104
207
|
{
|
|
@@ -339,6 +442,67 @@ function getDIMDefSceneList(): RemoteSceneInfo[] {
|
|
|
339
442
|
]
|
|
340
443
|
}
|
|
341
444
|
|
|
445
|
+
function getStringLightSceneList(): RemoteSceneInfo[] {
|
|
446
|
+
return [
|
|
447
|
+
{
|
|
448
|
+
n: 'Flow',
|
|
449
|
+
i: '010d0d3232000000640000640000000064003c64000000006400f06400000000',
|
|
450
|
+
s: '',
|
|
451
|
+
t: 0,
|
|
452
|
+
e: false,
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
n: 'Rainbow',
|
|
456
|
+
i: '0102023232000000640000640000000064003c64000000006400f06400000000',
|
|
457
|
+
s: '',
|
|
458
|
+
t: 0,
|
|
459
|
+
e: false,
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
n: 'Chase',
|
|
463
|
+
i: '010e0e3232000000640000640000000064003c640000000064007864000000006400b464000000006400f0640000000064012c6400000000',
|
|
464
|
+
s: '',
|
|
465
|
+
t: 0,
|
|
466
|
+
e: false,
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
n: 'Dazzle',
|
|
470
|
+
i: '010f0f3232000000640000640000000064003c640000000064007864000000006400b464000000006400f0640000000064012c6400000000',
|
|
471
|
+
s: '',
|
|
472
|
+
t: 0,
|
|
473
|
+
e: false
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
n: 'Gradient',
|
|
477
|
+
i: '01101032320000006400f0640000000064012c640000000064003c6400000000',
|
|
478
|
+
s: '',
|
|
479
|
+
t: 0,
|
|
480
|
+
e: false,
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
n: 'Jump',
|
|
484
|
+
i: '010a0a32320000006400f0640000000064012c640000000064003c6400000000',
|
|
485
|
+
s: '',
|
|
486
|
+
t: 0,
|
|
487
|
+
e: false,
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
n: 'Breathing',
|
|
491
|
+
i: '010b0b32320000006400f0640000000064012c640000000064003c6400000000',
|
|
492
|
+
s: '',
|
|
493
|
+
t: 0,
|
|
494
|
+
e: false,
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
n: 'Blink',
|
|
498
|
+
i: '010c0c32320000006400f0640000000064012c640000000064003c6400000000',
|
|
499
|
+
s: '',
|
|
500
|
+
t: 0,
|
|
501
|
+
e: false,
|
|
502
|
+
},
|
|
503
|
+
]
|
|
504
|
+
}
|
|
505
|
+
|
|
342
506
|
export const MAXSCENE = 8
|
|
343
507
|
|
|
344
508
|
export const AddScene = {
|
|
@@ -5,6 +5,8 @@ import DynamicMoodEditorPage from '../modules/mood/DynamicMoodEditorPage'
|
|
|
5
5
|
import StaticMoodEditorPage from '../modules/mood/StaticMoodEditorPage'
|
|
6
6
|
import AddMoodPage from '../modules/mood/AddMoodPage'
|
|
7
7
|
import MoodPage from '../modules/mood/MoodPage'
|
|
8
|
+
import FantasyMoodPage from '../modules/mood/FantasyMood'
|
|
9
|
+
import FantasyMoodEditPage from '../modules/mood/FantasyMoodEditPage'
|
|
8
10
|
import TimerPage from '../modules/timer/TimerPage'
|
|
9
11
|
import SleepWakeUpPage from 'modules/sleepWakeup/SleepWakeUpPage'
|
|
10
12
|
import SleepWakeUpDetailPage from 'modules/sleepWakeup/SleepWakeUpDetailPage'
|
|
@@ -21,6 +23,8 @@ import RandomTimeDetailPage from '../modules/randomTime/RandomTimeDetailPage'
|
|
|
21
23
|
export const ui_biz_routerKey = {
|
|
22
24
|
'ui_biz_time_schedule': 'ui_biz_time_schedule',
|
|
23
25
|
'ui_biz_time_schedule_edit': 'ui_biz_time_schedule_edit',
|
|
26
|
+
'ui_biz_fantasy_mood': 'ui_biz_fantasy_mood',
|
|
27
|
+
'ui_biz_fantasy_mood_edit': 'ui_biz_fantasy_mood_edit',
|
|
24
28
|
'ui_biz_mood': 'ui_biz_mood',
|
|
25
29
|
'ui_biz_mood_add': 'ui-biz_mood_add',
|
|
26
30
|
'ui_biz_static_mood_edit': 'ui_biz_static_mood_edit',
|
|
@@ -129,6 +133,34 @@ export const MoodPageRouters: NavigationRoute[] = [
|
|
|
129
133
|
},
|
|
130
134
|
]
|
|
131
135
|
|
|
136
|
+
export const FantasyMoodRouters: NavigationRoute[] = [
|
|
137
|
+
{
|
|
138
|
+
name: ui_biz_routerKey.ui_biz_fantasy_mood,
|
|
139
|
+
component: FantasyMoodPage,
|
|
140
|
+
options: {
|
|
141
|
+
hideTopbar: true,
|
|
142
|
+
showOfflineView: false,
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: ui_biz_routerKey.ui_biz_fantasy_mood_edit,
|
|
147
|
+
component: FantasyMoodEditPage,
|
|
148
|
+
options: {
|
|
149
|
+
hideTopbar: true,
|
|
150
|
+
showOfflineView: false,
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: ui_biz_routerKey.ui_biz_select_page,
|
|
155
|
+
component: SelectPage,
|
|
156
|
+
options: {
|
|
157
|
+
gesture: true,
|
|
158
|
+
hideTopbar: true,
|
|
159
|
+
...TransitionPresets.ModalPresentationIOS,
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
]
|
|
163
|
+
|
|
132
164
|
export const TimerPageRouters: NavigationRoute[] = [
|
|
133
165
|
{
|
|
134
166
|
name: ui_biz_routerKey.ui_biz_timer,
|