@ledvance/ui-biz-bundle 1.1.20 → 1.1.21

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/ui-biz-bundle",
5
5
  "pid": [],
6
6
  "uiid": "",
7
- "version": "1.1.20",
7
+ "version": "1.1.21",
8
8
  "scripts": {},
9
9
  "dependencies": {
10
10
  "@ledvance/base": "^1.x",
@@ -1,362 +1,381 @@
1
- import React, { useEffect } from 'react'
2
- import { FlatList, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
1
+ import React, { useCallback, useEffect } from "react";
2
+ import { View, Text, ScrollView, TouchableOpacity, StyleSheet, FlatList, Image } from "react-native"
3
+ import { useNavigation } from '@react-navigation/native'
4
+ import { RouteProp, useRoute } from '@react-navigation/core'
5
+ import { useReactive, useUpdateEffect } from "ahooks";
6
+ import res from '@ledvance/base/src/res'
7
+ import Card from '@ledvance/base/src/components/Card'
8
+ import Spacer from '@ledvance/base/src/components/Spacer'
3
9
  import I18n from '@ledvance/base/src/i18n'
4
- import Page from '@ledvance/base/src/components/Page'
5
- import { Utils } from 'tuya-panel-kit'
6
- import { useDeviceInfo } from '@ledvance/base/src/models/modules/NativePropsSlice'
10
+ import Page from "@ledvance/base/src/components/Page";
7
11
  import LdvPickerView from '@ledvance/base/src/components/ldvPickerView'
8
- import { useReactive } from 'ahooks'
9
- import Spacer from '@ledvance/base/src/components/Spacer'
10
- import DeleteButton from '@ledvance/base/src/components/DeleteButton'
11
- import { TaskStatus, timeFormat, TimerPageParams, TimerTask, useTimerTasks } from './TimerAction'
12
- import { cloneDeep, groupBy } from 'lodash'
13
- import { useParams } from '@ledvance/base/src/hooks/Hooks'
14
- import GroupBizRes from '../../res/GroupBizRes'
15
- import dayjs from 'dayjs'
16
- import Card from '@ledvance/base/src/components/Card'
17
- import TextButton from '@ledvance/base/src/components/TextButton'
18
- import { CircularProgress } from '@ledvance/base/src/components/CircularProgress'
12
+ import { Utils, Progress } from 'tuya-panel-kit'
13
+ import { useDeviceInfo } from "@ledvance/base/src/models/modules/NativePropsSlice";
14
+ import { useProgress } from "./TimerPageAction";
15
+ import { cloneDeep } from "lodash";
16
+ import dayjs from "dayjs";
17
+ import DeleteButton from "@ledvance/base/src/components/DeleteButton";
19
18
 
20
- const { convertX: cx } = Utils.RatioUtils
19
+ export type dpItem = {
20
+ label: string
21
+ value: string
22
+ dpId: string
23
+ enableDp: string
24
+ cloudKey: any
25
+ stringOn: any
26
+ stringOff: any
27
+ }
21
28
 
22
- const TimerPage = () => {
23
- const devInfo = useDeviceInfo()
29
+ type TimerPageRouteParams = {
30
+ params: { dps: dpItem[] }
31
+ }
24
32
 
25
- const params = useParams<TimerPageParams>()
26
- const [timerTasks, setTimerTasks, changeTimerTasks] = useTimerTasks(params.timerSettableDps)
33
+ type Props = {
34
+ route: RouteProp<TimerPageRouteParams, 'params'>
35
+ }
27
36
 
37
+ const { convertX: cx } = Utils.RatioUtils
38
+ const TimerPage = () => {
39
+ const devInfo = useDeviceInfo()
40
+ const navigation = useNavigation()
41
+ const { dps } = useRoute<Props['route']>().params
42
+ const progress = useProgress(dps)
28
43
  const state = useReactive({
29
44
  hour: '00',
30
- min: '01',
31
- tasks: cloneDeep(timerTasks),
32
- loading: false,
45
+ minute: '01',
46
+ skillList: [] as dpItem[],
47
+ selectedSkill: [] as dpItem[]
33
48
  })
34
49
 
50
+
51
+ const hasNotRunningTimer = () => {
52
+ return !!progress.find(p => p.progressHook.countdown === 0)
53
+ }
54
+
55
+ const runningTimer = () => {
56
+ return progress.filter(p => p.progressHook.countdown > 0)
57
+ }
58
+
59
+ const getProgressByDp = useCallback((dpId: string) => {
60
+ return progress.find(p => p.dpId === dpId)!.progressHook
61
+ }, [progress])
62
+
35
63
  useEffect(() => {
36
- state.tasks = cloneDeep(timerTasks)
37
- .map(task => {
38
- const newStatus = params.timerSettableDps.length === 1 && task.status !== TaskStatus.Started
39
- ? TaskStatus.Selected : task.status
40
- return { ...task, status: newStatus }
41
- })
42
- }, [JSON.stringify(timerTasks)])
64
+ if (progress.length > 1) {
65
+ state.skillList = progress.filter(p => p.progressHook.countdown === 0)
66
+ } else {
67
+ state.skillList = []
68
+ state.selectedSkill = cloneDeep(dps)
69
+ }
70
+ }, [runningTimer().length])
71
+
72
+ useUpdateEffect(() => {
73
+ if (state.hour === '00' && state.minute === '00') {
74
+ state.minute = '01'
75
+ }
76
+ }, [state.hour, state.minute])
43
77
 
44
- const tasksByStatus = groupBy(state.tasks, 'status')
45
78
 
46
- const noSelectedTasks = tasksByStatus[TaskStatus.NoSelected] || []
47
- const selectedTasks = tasksByStatus[TaskStatus.Selected] || []
48
- const startedTasks = tasksByStatus[TaskStatus.Started] || []
49
- const startEnable = selectedTasks.length > 0
79
+ const handelTimer = (timer: dpItem, isAdd: boolean) => {
80
+ if (isAdd) {
81
+ state.selectedSkill = [...state.selectedSkill, timer]
82
+ state.skillList = state.skillList.filter(item => item.dpId !== timer.dpId)
83
+ } else {
84
+ state.selectedSkill = state.selectedSkill.filter(item => item.dpId !== timer.dpId)
85
+ state.skillList = [...state.skillList, timer]
86
+ }
87
+ }
88
+
89
+ const onStartPress = async () => {
90
+ let mCountdown = 0
91
+ const hour = parseInt(state.hour)
92
+ const minute = parseInt(state.minute)
93
+ if (hour > 0) {
94
+ mCountdown += hour * 60 * 60
95
+ }
96
+ if (minute > 0) {
97
+ mCountdown += minute * 60
98
+ }
99
+ if (!state.selectedSkill.length || mCountdown === 0) return
100
+ for (let skill of state.selectedSkill) {
101
+ const time = dayjs()
102
+ .add(Number(hour), 'h')
103
+ .add(Number(minute), 'm')
104
+ .format('YYYY-MM-DD HH:mm:ss');
105
+ await getProgressByDp(skill.dpId).startTimer(mCountdown, time)
106
+ }
107
+ state.selectedSkill = []
108
+ }
109
+
110
+ const getActiveTimeString = (c: number) => {
111
+ const h = parseInt(String(c / 3600))
112
+ const m = Number(((c - 3600 * h) / 60).toFixed(0))
113
+ if (h > 0) {
114
+ if (m === 0) {
115
+ return h + ' h'
116
+ } else {
117
+ return h + ' h ' + m + ' min'
118
+ }
119
+ } else {
120
+ return m + ' min'
121
+ }
122
+ }
123
+
124
+ const getSingleLightEndTimeString = (mCountdown: number) => {
125
+ const date = new Date()
126
+ const now = date.getTime()
127
+ const after = new Date(now + mCountdown * 1000)
128
+ const timeString = `${after.getHours()}:${after.getMinutes().toString().padStart(2, '0')}`
129
+ return timeString
130
+ }
131
+
132
+ const showActiveView = () => {
133
+ return progress.length > 1
134
+ }
135
+
136
+ const renderItem = ({ item }) => {
137
+ return (
138
+ <View style={{
139
+ flexDirection: 'row',
140
+ justifyContent: 'space-between',
141
+ alignItems: 'center',
142
+ backgroundColor: '#fff',
143
+ marginBottom: cx(8)
144
+ }}>
145
+ <Text
146
+ style={{
147
+ color: '#000',
148
+ fontSize: 14,
149
+ marginHorizontal: cx(6),
150
+ marginVertical: cx(9),
151
+ }}
152
+ >
153
+ {item.label}
154
+ </Text>
155
+ {progress.length > 1 && <TouchableOpacity onPress={() => handelTimer(item, false)}>
156
+ <Image style={{ width: cx(16), height: cx(16), marginRight: cx(5) }} source={res.ic_arrows_nav_clear} />
157
+ </TouchableOpacity>}
158
+ </View>
159
+ );
160
+ };
50
161
 
51
162
  return (
52
163
  <Page
53
164
  backText={devInfo.name}
54
165
  headlineText={I18n.getLang('timer_nightplug_headline_text')}
55
- loading={state.loading}>
166
+ onBackClick={navigation.goBack}
167
+ >
56
168
  <ScrollView
57
- style={styles.root}
58
169
  nestedScrollEnabled={true}
59
170
  showsHorizontalScrollIndicator={false}
60
- showsVerticalScrollIndicator={false}>
61
- <View>
62
- {startedTasks.length !== state.tasks.length &&
63
- <View style={styles.content}>
64
- <LdvPickerView
65
- hour={state.hour}
66
- minute={state.min}
67
- unit={['h', 'min']}
68
- setHour={hour => {
69
- state.hour = hour
70
- }}
71
- setMinute={min => {
72
- state.min = min
73
- }}/>
74
- <Spacer height={cx(30)}/>
75
- <Text style={styles.applyFor}>{I18n.getLang('timeschedule_add_schedule_subheadline_text')}</Text>
76
- <Spacer height={cx(10)}/>
77
- <FlatList
78
- data={selectedTasks}
79
- style={styles.taskList}
80
- renderItem={({ item }) => {
81
- return (
82
- <View style={styles.taskItem}>
83
- <Text style={styles.taskItemText}>{item.name}</Text>
84
- {state.tasks.length > 1 &&
85
- <TouchableOpacity
86
- onPress={() => {
87
- item.status = TaskStatus.NoSelected
88
- changeTimerTasks([cloneDeep(item)])
89
- }}>
90
- <Image style={styles.taskItemIcon} source={GroupBizRes.ic_arrows_nav_clear}/>
91
- </TouchableOpacity>}
92
- </View>
93
- )
94
- }}
95
- keyExtractor={item => item.dp.key}
96
- ListEmptyComponent={() => <View style={styles.listEmptyView}>
97
- <Text style={styles.listEmptyText}>
98
- {I18n.getLang('timer_ceiling_fan_selectionfield_no_components_text')}
99
- </Text>
100
- </View>}/>
101
- <Spacer height={cx(6)}/>
102
- <FlatList
103
- data={noSelectedTasks}
104
- style={styles.noSelectTaskList}
105
- renderItem={({ item }) => {
106
- return (
107
- <TouchableOpacity
108
- style={styles.noSelectTaskItem}
109
- onPress={() => {
110
- item.status = TaskStatus.Selected
111
- changeTimerTasks([cloneDeep(item)])
112
- }}>
113
- <Spacer width={cx(8)}/>
114
- <Text style={styles.noSelectTaskText}>{item.name}</Text>
115
- <Image style={styles.taskItemIcon} source={GroupBizRes.device_panel_timer_add}/>
116
- </TouchableOpacity>
117
- )
118
- }}
119
- keyExtractor={item => item.dp.key}/>
120
- <Spacer/>
121
- <DeleteButton
122
- text={I18n.getLang('timer_sockets_button_text')}
123
- disabled={selectedTasks.length === 0}
124
- onPress={async () => {
125
- state.loading = true
126
- const tasks = selectedTasks.map(task => {
127
- return {
128
- ...task,
129
- startTime: dayjs().unix(),
130
- duration: (parseInt(state.hour) * 60 + parseInt(state.min)) * 60,
131
- status: TaskStatus.Started,
132
- }
133
- })
134
- tasks.push(...cloneDeep(startedTasks))
135
- await setTimerTasks(cloneDeep(tasks))
136
- state.loading = false
137
- }}
138
- textStyle={{ fontSize: cx(14) }}
139
- style={{ backgroundColor: !startEnable ? '#FFE0D4' : '#f60' }}/>
140
- </View>
141
- }
142
- {startedTasks.length > 0 && params.timerSettableDps.length > 1 &&
143
- <>
144
- <Spacer height={cx(30)}/>
145
- <View style={styles.content}>
146
- <Text style={styles.applyFor}>{
147
- I18n.formatValue('timer_nightplug_active_timer_subheadline2_text', startedTasks.length.toString())
148
- }</Text>
149
- </View>
150
- <FlatList
151
- data={startedTasks}
152
- renderItem={({ item }) => {
153
- return ActiveTimerItem(item, async () => {
154
- state.loading = true
155
- const tasks = cloneDeep(startedTasks)
156
- tasks.forEach(task => {
157
- if (task.dp.code === item.dp.code) {
158
- task.status = TaskStatus.NoSelected
159
- task.startTime = 0
160
- task.duration = 0
161
- }
162
- })
163
- await setTimerTasks(tasks)
164
- state.loading = false
165
- })
166
- }}
167
- keyExtractor={item => item.dp.key}
168
- ListHeaderComponent={() => (<Spacer height={cx(10)}/>)}
169
- ItemSeparatorComponent={() => (<Spacer height={cx(10)}/>)}
170
- ListFooterComponent={() => (<Spacer height={cx(20)}/>)}/>
171
- </>
172
- }
173
- {
174
- startedTasks.length === 1 && params.timerSettableDps.length === 1 &&
175
- <View style={{ justifyContent: 'center', marginHorizontal: cx(24) }}>
176
- <Spacer height={cx(116)}/>
171
+ showsVerticalScrollIndicator={false}
172
+ >
173
+ {/* picker */}
174
+ <View style={styles.content}>
175
+ {hasNotRunningTimer() && <>
176
+ <LdvPickerView
177
+ hour={state.hour}
178
+ minute={state.minute}
179
+ setHour={h => state.hour = h}
180
+ setMinute={m => state.minute = m}
181
+ unit={['h', 'min']}
182
+ />
183
+ {/* Apply for */}
184
+ <View>
185
+ <Text style={styles.itemTitle}>{I18n.getLang('timeschedule_add_schedule_subheadline_text')}</Text>
177
186
  <View
178
187
  style={{
188
+ backgroundColor: '#f6f6f6',
189
+ borderRadius: 4,
190
+ minHeight: cx(50),
191
+ flex: 1,
179
192
  justifyContent: 'center',
180
- alignItems: 'center',
181
- }}>
182
- <CircularProgress
183
- progress={(startedTasks[0].timeLeft / startedTasks[0].duration) * 100}
184
- size={cx(172)}
185
- strokeWidth={cx(14)}>
186
- <Text style={styles.activeTaskTimeText2}>{timeFormat(startedTasks[0].timeLeft)}</Text>
187
- </CircularProgress>
193
+ }}
194
+ >
195
+ {!state.selectedSkill.length ? <Text style={{ marginLeft: cx(10) }}>{I18n.getLang('timer_ceiling_fan_selectionfield_no_components_text')}</Text> :
196
+ <View
197
+ style={{
198
+ marginHorizontal: cx(8),
199
+ marginTop: cx(8),
200
+ borderRadius: 4,
201
+ }}
202
+ >
203
+ <FlatList
204
+ data={state.selectedSkill}
205
+ renderItem={item => renderItem(item)}
206
+ keyExtractor={(item: dpItem) => item.dpId}
207
+ />
208
+ </View>}
209
+ </View>
210
+ {state.skillList.map((skill: dpItem) => {
211
+ return (
212
+ <TouchableOpacity style={styles.skillListItem} key={skill.dpId} onPress={() => handelTimer(skill, true)}>
213
+ <Text style={{ color: '#000' }}>{skill.label}</Text>
214
+ <Image style={{ width: cx(16), height: cx(16) }} source={res.device_panel_timer_add} />
215
+ </TouchableOpacity>
216
+ )
217
+ })}
218
+ </View>
219
+
220
+ {/* Start */}
221
+ <Spacer />
222
+ <DeleteButton
223
+ text={I18n.getLang('timer_sockets_button_text')}
224
+ onPress={onStartPress}
225
+ textStyle={{ fontSize: cx(14) }}
226
+ style={{ backgroundColor: !state.selectedSkill.length ? '#FFE0D4' : '#f60' }}
227
+ />
228
+ <Spacer />
229
+ </>}
230
+
231
+ {/* Active timer */}
232
+ {!!runningTimer().length && (
233
+ showActiveView() ? <>
234
+ <Text style={styles.itemTitle}>{I18n.formatValue('timer_nightplug_active_timer_subheadline2_text', runningTimer().length.toString())}</Text>
235
+ {runningTimer().map(timer => (
236
+ <View key={timer.dpId}>
237
+ <Card containerStyle={styles.activeTimer}>
238
+ <View style={styles.activeTimerTitleBox}>
239
+ <Text style={styles.activeTimerTitle}>{timer.label}</Text>
240
+ <TouchableOpacity
241
+ onPress={() => {
242
+ getProgressByDp(timer.dpId).endTimer()
243
+ }}
244
+ style={styles.activeTimerCancelBtn}>
245
+ <Text style={{ color: '#fff', fontSize: cx(12) }}>{I18n.getLang('auto_scan_system_cancel')}</Text>
246
+ </TouchableOpacity>
247
+ </View>
248
+ <View style={styles.activeTimerTimeBox}>
249
+ <Progress.Double
250
+ style={{
251
+ width: 35,
252
+ height: 35,
253
+ // backgroundColor: 'red',
254
+ justifyContent: 'center',
255
+ alignItems: 'center',
256
+ }}
257
+ disabled={true}
258
+ minValue={0}
259
+ maxValue={getProgressByDp(timer.dpId).progressNumber}
260
+ startDegree={270}
261
+ andDegree={360}
262
+ scaleHeight={2}
263
+ thumbStrokeWidth={2}
264
+ thumbRadius={0}
265
+ />
266
+ <Text style={{ marginLeft: cx(20), fontSize: cx(22) }}>{getActiveTimeString(timer.progressHook.countdown)}</Text>
267
+ </View>
268
+ <Text>{I18n.formatValue(timer.enable ? timer.stringOff : timer.stringOn, getSingleLightEndTimeString(timer.progressHook.countdown))}</Text>
269
+ </Card>
270
+ <Spacer height={cx(40)} />
188
271
  </View>
189
- <Spacer height={cx(32)}/>
190
- <Text
191
- style={[styles.activeTaskDesc, { textAlign: 'center' }]}>{I18n.formatValue(startedTasks[0].dp.enable ? startedTasks[0].stringOff : startedTasks[0].stringOn, timeFormat(startedTasks[0].timeLeft))}</Text>
192
- <Spacer height={cx(32)}/>
193
- <TextButton
194
- text={I18n.getLang('auto_scan_system_cancel')}
195
- style={styles.activeTasBtn}
196
- textStyle={styles.activeTaskBtnText}
197
- onPress={async () => {
198
- state.loading = true
199
- const task = cloneDeep(startedTasks[0])
200
- task.status = TaskStatus.NoSelected
201
- task.startTime = 0
202
- task.duration = 0
203
- await setTimerTasks([task])
204
- state.loading = false
205
- }}/>
206
- <Spacer width={cx(20)}/>
272
+ ))}
273
+ </> :
274
+ <View
275
+ style={{
276
+ flexDirection: 'column',
277
+ alignItems: 'center',
278
+ marginVertical: cx(30),
279
+ }}
280
+ >
281
+ <Progress.Double
282
+ style={{
283
+ width: 172,
284
+ height: 172,
285
+ justifyContent: 'center',
286
+ alignItems: 'center',
287
+ }}
288
+ minValue={0}
289
+ maxValue={getProgressByDp(runningTimer()[0].dpId).progressNumber}
290
+ startDegree={270}
291
+ andDegree={360}
292
+ disabled={true}
293
+ foreColor={'#f60'}
294
+ scaleHeight={14}
295
+ minThumbFill={'#f60'}
296
+ minThumbStroke={'#f60'}
297
+ thumbStrokeWidth={0}
298
+ thumbRadius={14 / 2 - 0.5}
299
+ thumbFill={'#f60'}
300
+ thumbStroke={'#f60'}
301
+ renderCenterView={
302
+ <View style={{ position: 'absolute' }}>
303
+ <Text style={{ fontSize: cx(22), fontWeight: 'bold', color: '#666' }}>
304
+ {getActiveTimeString(runningTimer()[0].progressHook.countdown)}
305
+ </Text>
306
+ </View>
307
+ }
308
+ />
309
+ <View style={{ marginVertical: cx(30) }}>
310
+ <Text>
311
+ {I18n.formatValue(
312
+ runningTimer()[0].enable ?
313
+ runningTimer()[0].stringOff :
314
+ runningTimer()[0].stringOn, getSingleLightEndTimeString(runningTimer()[0].progressHook.countdown)
315
+ )}
316
+ </Text>
317
+ </View>
318
+ <DeleteButton
319
+ text={I18n.getLang('auto_scan_system_cancel')}
320
+ style={{ paddingHorizontal: cx(15), width: 'auto', height: cx(40) }}
321
+ textStyle={{ fontSize: cx(14) }}
322
+ onPress={() => {
323
+ getProgressByDp(runningTimer()[0].dpId).endTimer()
324
+ }}
325
+ />
207
326
  </View>
208
- }
209
- <Spacer/>
327
+ )}
210
328
  </View>
211
329
  </ScrollView>
212
330
  </Page>
213
331
  )
214
332
  }
215
333
 
216
- function ActiveTimerItem(task: TimerTask, onCancel: () => void) {
217
- return (
218
- <Card style={{ marginHorizontal: cx(24) }}>
219
- <Spacer/>
220
- <View style={styles.activeTaskHeadLine}>
221
- <Spacer width={cx(20)}/>
222
- <Text style={styles.activeTaskHeadLineText}>{task.name}</Text>
223
- <TextButton
224
- text={I18n.getLang('auto_scan_system_cancel')}
225
- style={styles.activeTaskHeadLineBtn}
226
- textStyle={styles.activeTaskHeadLineBtnText}
227
- onPress={onCancel}/>
228
- <Spacer width={cx(12)}/>
229
- </View>
230
- <Spacer height={cx(12)}/>
231
- <View style={styles.activeTaskHeadLine}>
232
- <Spacer width={cx(20)}/>
233
- <CircularProgress
234
- progress={(task.timeLeft / task.duration) * 100}
235
- size={cx(24)}
236
- strokeWidth={cx(2)}/>
237
- <Spacer width={cx(16)}/>
238
- <Text style={styles.activeTaskTimeText}>{timeFormat(task.timeLeft)}</Text>
239
- <Spacer width={cx(20)}/>
240
- </View>
241
- <Spacer/>
242
- <View style={{ marginHorizontal: cx(20) }}>
243
- <Text style={styles.activeTaskDesc}>{
244
- I18n.formatValue(task.dp.enable ? task.stringOff : task.stringOn, timeFormat(task.timeLeft))
245
- }</Text>
246
- </View>
247
- <Spacer height={cx(22)}/>
248
- </Card>
249
- )
250
- }
251
-
252
334
  const styles = StyleSheet.create({
253
- root: {
254
- flex: 1,
255
- },
256
335
  content: {
257
- marginHorizontal: cx(24),
336
+ marginHorizontal: cx(24)
258
337
  },
259
- applyFor: {
338
+ itemTitle: {
260
339
  color: '#000',
261
340
  fontSize: cx(16),
262
341
  fontWeight: 'bold',
263
342
  fontFamily: 'helvetica_neue_lt_std_bd',
343
+ marginTop: cx(30),
344
+ marginBottom: cx(10)
264
345
  },
265
- taskList: {
266
- backgroundColor: '#f6f6f6',
267
- paddingTop: cx(8),
268
- borderRadius: cx(4),
269
- },
270
- taskItem: {
271
- marginHorizontal: cx(8),
272
- marginBottom: cx(8),
346
+ skillListItem: {
273
347
  flexDirection: 'row',
274
348
  justifyContent: 'space-between',
275
- alignItems: 'center',
276
- backgroundColor: '#fff',
277
- },
278
- taskItemText: {
279
- color: '#000',
280
- fontSize: 14,
281
- marginHorizontal: cx(8),
282
- marginVertical: cx(9),
283
- },
284
- taskItemIcon: {
285
- width: cx(16),
286
- height: cx(16),
287
- marginRight: cx(8),
288
- },
289
- listEmptyView: {
290
- marginHorizontal: cx(8),
291
- marginTop: cx(6),
292
- marginBottom: cx(14),
293
- },
294
- listEmptyText: {
295
- color: '#666',
296
- fontSize: cx(14),
297
- },
298
- noSelectTaskList: {},
299
- noSelectTaskItem: {
300
349
  height: cx(30),
301
- flexDirection: 'row',
302
350
  alignItems: 'center',
351
+ marginVertical: cx(5)
303
352
  },
304
- noSelectTaskText: {
305
- flex: 1,
306
- color: '#000',
307
- fontSize: cx(14),
353
+ activeTimer: {
354
+ marginHorizontal: cx(20),
355
+ marginVertical: cx(20),
308
356
  },
309
- activeTaskHeadLine: {
357
+ activeTimerTitleBox: {
310
358
  flexDirection: 'row',
359
+ justifyContent: 'space-between',
311
360
  alignItems: 'center',
361
+ marginBottom: cx(20),
312
362
  },
313
- activeTaskHeadLineText: {
314
- flex: 1,
363
+ activeTimerTitle: {
364
+ fontSize: cx(20),
315
365
  color: '#000',
316
- fontSize: cx(14),
317
- },
318
- activeTaskHeadLineBtn: {
319
- minWidth: cx(50),
320
- height: cx(24),
321
- padding: 0,
322
- backgroundColor: '#666',
323
- borderRadius: cx(4),
324
- },
325
- activeTaskHeadLineBtnText: {
326
- color: '#fff',
327
- fontSize: cx(8.2),
328
- },
329
- activeTaskTimeText: {
330
- flex: 1,
331
- color: '#666',
332
- fontSize: cx(24),
333
- fontWeight: 'bold',
334
- fontFamily: 'helvetica_neue_lt_std_bd',
335
366
  },
336
- activeTaskDesc: {
337
- flex: 1,
338
- color: '#666',
339
- fontSize: cx(14),
340
- },
341
- activeTasBtn: {
342
- width: cx(75),
343
- height: cx(36),
344
- padding: 0,
367
+ activeTimerCancelBtn: {
368
+ paddingHorizontal: cx(8),
369
+ paddingVertical: cx(5),
370
+ alignItems: 'center',
371
+ justifyContent: 'center',
345
372
  backgroundColor: '#666',
346
- borderRadius: cx(4),
373
+ borderRadius: cx(5)
347
374
  },
348
- activeTaskBtnText: {
349
- color: '#fff',
350
- fontSize: cx(12.2),
351
- },
352
- activeTaskTimeText2: {
353
- flex: 1,
354
- marginHorizontal: cx(20),
355
- color: '#666',
356
- fontSize: cx(22),
357
- textAlign: 'center',
358
- fontWeight: 'bold',
359
- fontFamily: 'helvetica_neue_lt_std_bd',
375
+ activeTimerTimeBox: {
376
+ flexDirection: 'row',
377
+ alignItems: 'center',
378
+ marginBottom: cx(20),
360
379
  },
361
380
  })
362
381
 
@@ -1,174 +1,131 @@
1
- import { useFeatureHook } from '@ledvance/base/src/models/modules/NativePropsSlice'
2
- import { Result } from '@ledvance/base/src/models/modules/Result'
3
- import dayjs from 'dayjs'
4
- import { useCountDown, useReactive } from 'ahooks'
5
- import { useEffect, useMemo } from 'react'
6
-
7
- export interface TimerTask {
8
- name: string
9
- startTime: number
10
- duration: number
11
- timeLeft: number
12
- dp: {
13
- key: string
14
- code: string
15
- enable: boolean
16
- }
17
- status: TaskStatus
18
- stringOn: string
19
- stringOff: string
20
- }
21
-
22
- export enum TaskStatus {
23
- NoSelected,
24
- Selected,
25
- Started
1
+ import { useCallback, useEffect, useState } from "react"
2
+ import { useDeviceId, useDp } from "@ledvance/base/src/models/modules/NativePropsSlice"
3
+ import { Result } from "@ledvance/base/src/models/modules/Result"
4
+ import { dpItem } from "./TimerPage"
5
+ import { useCountDown as useCountDownAHook, useUpdateEffect } from 'ahooks'
6
+ import dayjs from "dayjs"
7
+ import { NativeApi } from "@ledvance/base/src/api/native"
8
+
9
+
10
+ export const useCountdowns = (dps: dpItem[]) => {
11
+ return dps.map(dp => {
12
+ return ({
13
+ countdown: useDp<number, (value: number) => Promise<Result<any>>>(dp.dpId),
14
+ enable: useDp<boolean, (value: boolean) => Promise<Result<any>>>(dp.enableDp),
15
+ ...dp
16
+ })
17
+ })
26
18
  }
27
19
 
28
- export interface TimerConfig {
29
- timerTasks: TimerTask[]
20
+ type FormateProgressType = {
21
+ progressNumber: number
22
+ countdown: number
23
+ startTimer: (c: number, time: string) => Promise<void>
24
+ endTimer: () => void
30
25
  }
31
-
32
- export function useTimerTasks(timerSettableDps: TimerSettableDp[]):
33
- [TimerTask[], (value: TimerTask[]) => Promise<Result<any>>, (tasks: TimerTask[]) => void] {
34
- const countdowns = timerSettableDps.map(dps => {
35
- const [countdown, setTargetDate] = useCountDown({ interval: 1000 })
36
- return {
37
- dpCode: dps.dp.code,
38
- countdown: {
39
- value: countdown,
40
- setTargetDate,
41
- },
42
- }
43
- })
44
-
45
- const defTasks = useMemo(() => {
46
- return timerSettableDps.map(timerSettableDp => ({
47
- name: timerSettableDp.label,
48
- startTime: 0,
49
- duration: 0,
50
- timeLeft: 0,
51
- dp: timerSettableDp.dp,
52
- status: TaskStatus.NoSelected,
53
- stringOn: timerSettableDp.stringOn,
54
- stringOff: timerSettableDp.stringOff,
55
- }))
56
- }, [JSON.stringify(timerSettableDps)])
57
-
58
- const [remoteTasks, setTasks] = useFeatureHook<TimerConfig, TimerTask[]>(
59
- 'timerTasks',
60
- defTasks,
61
- () => undefined,
62
- timerTasks => {
63
- const dps = {}
64
- timerTasks.forEach(task => {
65
- dps[task.dp.code] = task.duration
66
- })
67
- return dps
26
+ const useFormateProgress: (dp: dpItem) => FormateProgressType = (dp) => {
27
+ const [countdown, setCountdown] = useDp<number, (value: number) => Promise<Result<any>>>(dp.dpId)
28
+ const [progress, setProgress] = useCountDownAHook({
29
+ interval: 1000,
30
+ onEnd: () => {
31
+ initTimerFn()
32
+ setCountdown(0)
68
33
  },
69
- )
70
-
71
- const tasks = defTasks.map(defTask => {
72
- const findTask = remoteTasks.find(remoteTask => remoteTask.dp.code === defTask.dp.code)
73
- return findTask ? { ...findTask, dp: defTask.dp } : defTask
74
34
  })
75
-
76
- const state = useReactive({ tasks: tasks })
77
-
35
+ const devId = useDeviceId()
36
+ const [cloudProgressNumber, setCloudProgressNumber] = useState(0)
37
+ const [progressNumber, setProgressNumber] = useState(0)
38
+ const [cloudData, setCloudData] = useState<any>()
39
+ const [isTrigger, setIsTeigger] = useState(false)
40
+ const getCloudJson = () => {
41
+ NativeApi.getJson(devId, dp.cloudKey).then(res => {
42
+ if (res.success && res.data) {
43
+ const result = JSON.parse(res.data)
44
+ setCloudData(result)
45
+ setCloudProgressNumber(result.progressAllNumber)
46
+ if (countdown > 0) setProgress(Date.now() + countdown * 1000)
47
+ }
48
+ })
49
+ }
78
50
  useEffect(() => {
79
- state.tasks = tasks
80
- .map(task => {
81
- const taskEndTime = dayjs.unix(task.startTime)
82
- .add(task.duration, 'second')
83
- const taskIsEnd = task.status !== TaskStatus.Started ||
84
- getTaskLeftover(task) === 0
51
+ if(countdown > 0){
52
+ getCloudJson()
53
+ }
54
+ }, [])
85
55
 
86
- countdowns.forEach(countdown => {
87
- if (countdown.dpCode === task.dp.code) {
88
- countdown.countdown.setTargetDate(taskIsEnd ? undefined : taskEndTime.toDate())
89
- }
90
- })
56
+ useEffect(() => {
57
+ if (cloudProgressNumber > 0) {
58
+ const currentTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
59
+ const remainingTime = Number(cloudData && dayjs(cloudData[getKey('StartTime', dp.dpId)] || 0).diff(currentTime))
60
+ const time = (cloudData && cloudData[getKey('Status', dp.dpId)] && remainingTime >= -1000) ? remainingTime : Number(progress.toString().slice(0, -3) + "000")
61
+ const conversion = (time / 1000) / cloudProgressNumber * 100
62
+ setProgressNumber(!isNaN(conversion) ? conversion : 0)
63
+ }
64
+ }, [progress])
91
65
 
92
- return {
93
- ...task,
94
- startTime: taskIsEnd ? 0 : task.startTime,
95
- duration: taskIsEnd ? 0 : task.duration,
96
- timeLeft: taskIsEnd ? 0 : task.timeLeft,
97
- status: taskIsEnd ? TaskStatus.NoSelected : TaskStatus.Started,
98
- }
99
- })
66
+ useUpdateEffect(() =>{
67
+ if(countdown === 0){
68
+ setIsTeigger(false)
69
+ initTimerFn()
70
+ }
71
+ }, [countdown])
100
72
 
101
- return () => {
102
- countdowns.forEach(countdown => {
103
- countdown.countdown.setTargetDate(undefined)
104
- })
73
+ useUpdateEffect(() => {
74
+ if (countdown === 0) {
75
+ if(isTrigger){
76
+ NativeApi.putJson(devId, dp.cloudKey, JSON.stringify({ [getKey('Status', dp.dpId)]: false, [getKey('StartTime', dp.dpId)]: '', progressAllNumber: 0 })).then()
77
+ }
105
78
  }
106
- }, [JSON.stringify(tasks)])
107
79
 
108
- useEffect(() => {
109
- countdowns.forEach(cd => {
110
- state.tasks.forEach(task => {
111
- if (task.dp.code === cd.dpCode) {
112
- task.timeLeft = Math.trunc(cd.countdown.value / 1000)
113
- if (task.timeLeft > 0) {
114
- task.status = TaskStatus.Started
115
- } else {
116
- if (task.status === TaskStatus.Started) {
117
- task.status = TaskStatus.NoSelected
118
- }
119
- }
120
- }
121
- })
122
- })
123
- }, [JSON.stringify(countdowns.map(c => c.countdown.value))])
80
+ if (countdown > 0 && !cloudData && !isTrigger) {
81
+ getCloudJson()
82
+ }
83
+ }, [countdown, cloudData])
124
84
 
125
- const changeTasks = (timerTasks: TimerTask[]) => {
126
- timerTasks.forEach(newTask => {
127
- state.tasks.forEach(task => {
128
- if (task.dp.code === newTask.dp.code) {
129
- task.status = newTask.status
130
- }
131
- })
132
- })
133
- }
134
85
 
135
- return [state.tasks, setTasks, changeTasks]
136
- }
137
86
 
138
- export function getTaskLeftover(timerTask: TimerTask): number {
139
- // 当前时间和任务结束时间
140
- const currentTime = dayjs()
141
- const taskEndTime = dayjs.unix(timerTask.startTime).add(timerTask.duration, 'second')
87
+ const initTimerFn = useCallback(() =>{
88
+ setProgressNumber(0)
89
+ setCloudData(null)
90
+ setCloudProgressNumber(0)
91
+ setProgress(0)
92
+ }, [])
142
93
 
143
- // 剩余时间
144
- const timeLeft = taskEndTime.diff(currentTime, 'second')
94
+ const getKey = (suffix: string, dpId: string) => {
95
+ const key = dpId + suffix
96
+ return key
97
+ }
145
98
 
146
- // 如果剩余时间小于0,返回0
147
- return timeLeft > 0 ? timeLeft : 0
148
- }
99
+ const startTimer = async (time: number, t:string) => {
100
+ setIsTeigger(true)
101
+ await setCountdown(time)
102
+ const putData = { [getKey('Status', dp.dpId)]: true, [getKey('StartTime', dp.dpId)]: t, progressAllNumber: time }
103
+ await NativeApi.putJson(devId, dp.cloudKey, JSON.stringify(putData))
104
+ setCloudProgressNumber(time)
105
+ setProgress(Date.now() + time * 1000)
106
+ setCloudData(putData)
107
+ }
149
108
 
150
- export interface TimerSettableDp {
151
- label: string
152
- dp: {
153
- key: string
154
- code: string
155
- enable: boolean
109
+ const endTimer = async () =>{
110
+ setIsTeigger(false)
111
+ await setCountdown(0)
112
+ NativeApi.putJson(devId, dp.cloudKey, JSON.stringify({ [getKey('Status', dp.dpId)]: false, [getKey('StartTime', dp.dpId)]: '', progressAllNumber: 0 })).then(() => {
113
+ initTimerFn()
114
+ })
156
115
  }
157
- stringOn: string
158
- stringOff: string
159
- }
160
116
 
161
- export interface TimerPageParams {
162
- timerSettableDps: TimerSettableDp[]
117
+ return {
118
+ progressNumber,
119
+ countdown,
120
+ startTimer,
121
+ endTimer
122
+ }
163
123
  }
164
124
 
165
- export function timeFormat(time: number): string {
166
- let hour = Math.trunc(time / 3600)
167
- const beforeMin = (time % 3600) / 60
168
- let min = Math.ceil(beforeMin)
169
- if (min === 60) {
170
- hour += 1
171
- min = 0
172
- }
173
- return `${hour > 0 ? `${hour} h ` : ''}${min > 0 ? `${min} min` : ''}`
125
+ export const useProgress = (dps: dpItem[]) => {
126
+ return dps.map(dp => ({
127
+ progressHook: useFormateProgress(dp),
128
+ enable: useDp(dp.enableDp)[0] as boolean,
129
+ ...dp
130
+ }))
174
131
  }