@ledvance/group-ui-biz-bundle 1.0.31 → 1.0.32
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/MoodPage.tsx +1 -0
- package/src/modules/randomTimeForPlug/ItemCard.tsx +76 -0
- package/src/modules/randomTimeForPlug/RandomTimeForPlugAction.ts +136 -0
- package/src/modules/randomTimeForPlug/RandomTimeForPlugDetailPage.tsx +268 -0
- package/src/modules/randomTimeForPlug/RandomTimeForPlugPage.tsx +192 -0
- package/src/modules/randomTimeForPlug/Summary.tsx +123 -0
- package/src/modules/randomTimingForLight/ItemCard.tsx +76 -0
- package/src/modules/randomTimingForLight/RandomTimingForLightAction.ts +43 -0
- package/src/modules/randomTimingForLight/RandomTimingForLightDetailPage.tsx +342 -0
- package/src/modules/randomTimingForLight/RandomTimingForLightPage.tsx +192 -0
- package/src/modules/randomTimingForLight/Summary.tsx +123 -0
- package/src/modules/timer/TimerAction.ts +1 -1
- package/src/navigation/Routers.ts +46 -0
package/package.json
CHANGED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Card from "@ledvance/base/src/components/Card";
|
|
3
|
+
import Spacer from "@ledvance/base/src/components/Spacer";
|
|
4
|
+
import {StyleSheet, Text, View} from "react-native";
|
|
5
|
+
import {SwitchButton, Utils} from "tuya-panel-kit";
|
|
6
|
+
import {loopText} from "@ledvance/base/src/utils/common";
|
|
7
|
+
import {parseHour12} from "@tuya/tuya-panel-lamp-sdk/lib/utils";
|
|
8
|
+
|
|
9
|
+
const {convertX: cx} = Utils.RatioUtils
|
|
10
|
+
const {parseTimer} = Utils.TimeUtils
|
|
11
|
+
|
|
12
|
+
export interface ItemCardProps<T> {
|
|
13
|
+
item: T & {
|
|
14
|
+
enable: boolean
|
|
15
|
+
startTime: number
|
|
16
|
+
endTime: number
|
|
17
|
+
weeks: number[]
|
|
18
|
+
name?: string
|
|
19
|
+
},
|
|
20
|
+
is24Hour?: boolean,
|
|
21
|
+
onSwitch: (enable: boolean) => void
|
|
22
|
+
onPress: () => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const ItemCard = <T, >(props: ItemCardProps<T>) => {
|
|
26
|
+
const {item, is24Hour, onSwitch, onPress} = props
|
|
27
|
+
return (
|
|
28
|
+
<Card style={styles.itemCard} onPress={onPress}>
|
|
29
|
+
<Spacer height={cx(16)}/>
|
|
30
|
+
<View style={styles.switchLine}>
|
|
31
|
+
<Text style={styles.time}>
|
|
32
|
+
{is24Hour ?
|
|
33
|
+
`${parseTimer(item.startTime * 60)} - ${parseTimer(item.endTime * 60)}`
|
|
34
|
+
:
|
|
35
|
+
`${parseHour12(item.startTime * 60)} - ${parseHour12(item.endTime * 60)}`
|
|
36
|
+
}
|
|
37
|
+
</Text>
|
|
38
|
+
<SwitchButton
|
|
39
|
+
style={styles.switchBtn}
|
|
40
|
+
value={item.enable}
|
|
41
|
+
thumbStyle={{elevation: 0}}
|
|
42
|
+
onValueChange={onSwitch}/>
|
|
43
|
+
</View>
|
|
44
|
+
<Text style={styles.loopText}>{loopText(item.weeks, parseTimer(item.startTime * 60))}</Text>
|
|
45
|
+
<Spacer height={cx(5)}/>
|
|
46
|
+
<Text style={styles.loopText}>{item.name}</Text>
|
|
47
|
+
<Spacer/>
|
|
48
|
+
</Card>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const styles = StyleSheet.create({
|
|
53
|
+
itemCard: {
|
|
54
|
+
marginHorizontal: cx(24),
|
|
55
|
+
paddingHorizontal: cx(16),
|
|
56
|
+
},
|
|
57
|
+
switchLine: {
|
|
58
|
+
flexDirection: 'row',
|
|
59
|
+
alignItems: 'center',
|
|
60
|
+
},
|
|
61
|
+
time: {
|
|
62
|
+
flex: 1,
|
|
63
|
+
color: '#000',
|
|
64
|
+
fontSize: cx(16),
|
|
65
|
+
fontWeight: 'bold',
|
|
66
|
+
fontFamily: 'helvetica_neue_lt_std_bd',
|
|
67
|
+
},
|
|
68
|
+
switchBtn: {},
|
|
69
|
+
loopText: {
|
|
70
|
+
color: '#000',
|
|
71
|
+
fontSize: cx(14),
|
|
72
|
+
fontFamily: 'helvetica_neue_lt_std_roman',
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
export default ItemCard
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 随机定时:最大 16 个定时功能。每 6 个字节代表含义如下:
|
|
3
|
+
*
|
|
4
|
+
* 第 1 个字节:通道号。bit0 代表开关,bit7-bit1 代表通道号,排插位数 0-6。
|
|
5
|
+
* 第 2 个字节:星期。
|
|
6
|
+
* 第 3 个字节和第 4 个字节:起始时间。单位:分钟。
|
|
7
|
+
* 第 5 个字节和第 6 个字节:结束时间。单位:分钟。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {useFeatureHook} from "@ledvance/base/src/models/modules/NativePropsSlice";
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export interface RandomTimeParam {
|
|
14
|
+
channels: string[],
|
|
15
|
+
is24Hour?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RandomTimeItem {
|
|
19
|
+
enable: boolean
|
|
20
|
+
channel?: number
|
|
21
|
+
weeks: number[]
|
|
22
|
+
startTime: number
|
|
23
|
+
endTime: number
|
|
24
|
+
name?: string
|
|
25
|
+
index?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface PlugConfig {
|
|
29
|
+
randomTime: RandomTimeItem[]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function useRandomTime() {
|
|
33
|
+
return useFeatureHook<PlugConfig, RandomTimeItem[]>('randomTime', [], (val) => randomTimeItemsToBase64(val))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
37
|
+
|
|
38
|
+
const decodeBase64 = (base64: string): Uint8Array => {
|
|
39
|
+
const bufferLength = base64.length * 0.75;
|
|
40
|
+
const buffer = new Uint8Array(bufferLength);
|
|
41
|
+
let encoded1: number, encoded2: number, encoded3: number, encoded4: number;
|
|
42
|
+
let p = 0;
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < base64.length; i += 4) {
|
|
45
|
+
encoded1 = base64Chars.indexOf(base64[i]);
|
|
46
|
+
encoded2 = base64Chars.indexOf(base64[i + 1]);
|
|
47
|
+
encoded3 = base64Chars.indexOf(base64[i + 2]);
|
|
48
|
+
encoded4 = base64Chars.indexOf(base64[i + 3]);
|
|
49
|
+
|
|
50
|
+
buffer[p++] = (encoded1 << 2) | (encoded2 >> 4);
|
|
51
|
+
buffer[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
|
|
52
|
+
buffer[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return buffer;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const encodeBase64 = (buffer: Uint8Array): string => {
|
|
59
|
+
let base64 = '';
|
|
60
|
+
let p = 0;
|
|
61
|
+
const length = buffer.length;
|
|
62
|
+
|
|
63
|
+
while (p < length) {
|
|
64
|
+
const byte1 = buffer[p++];
|
|
65
|
+
const byte2 = p < length ? buffer[p++] : 0;
|
|
66
|
+
const byte3 = p < length ? buffer[p++] : 0;
|
|
67
|
+
|
|
68
|
+
const encoded1 = byte1 >> 2;
|
|
69
|
+
const encoded2 = ((byte1 & 3) << 4) | (byte2 >> 4);
|
|
70
|
+
const encoded3 = ((byte2 & 15) << 2) | (byte3 >> 6);
|
|
71
|
+
const encoded4 = byte3 & 63;
|
|
72
|
+
|
|
73
|
+
base64 += base64Chars[encoded1] + base64Chars[encoded2];
|
|
74
|
+
base64 += p < length + 2 ? base64Chars[encoded3] : '=';
|
|
75
|
+
base64 += p < length + 1 ? base64Chars[encoded4] : '=';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return base64;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// @ts-ignore
|
|
82
|
+
const randomTimeItemsFromBase64 = (base64: string): RandomTimeItem[] => {
|
|
83
|
+
const bytes = decodeBase64(base64);
|
|
84
|
+
const items: RandomTimeItem[] = [];
|
|
85
|
+
|
|
86
|
+
for (let i = 0; i < bytes.length; i += 6) {
|
|
87
|
+
const channelByte = bytes[i];
|
|
88
|
+
const enable = (channelByte & 1) === 1;
|
|
89
|
+
const channel = (channelByte >> 1) & 0x7F;
|
|
90
|
+
const week = bytes[i + 1];
|
|
91
|
+
const startTime = (bytes[i + 2] << 8) | bytes[i + 3];
|
|
92
|
+
const endTime = (bytes[i + 4] << 8) | bytes[i + 5];
|
|
93
|
+
|
|
94
|
+
items.push({
|
|
95
|
+
enable,
|
|
96
|
+
channel,
|
|
97
|
+
weeks: [
|
|
98
|
+
(week & 0b00000001) >> 0,
|
|
99
|
+
(week & 0b00000010) >> 1,
|
|
100
|
+
(week & 0b00000100) >> 2,
|
|
101
|
+
(week & 0b00001000) >> 3,
|
|
102
|
+
(week & 0b00010000) >> 4,
|
|
103
|
+
(week & 0b00100000) >> 5,
|
|
104
|
+
(week & 0b01000000) >> 6,
|
|
105
|
+
(week & 0b10000000) >> 7,
|
|
106
|
+
],
|
|
107
|
+
startTime,
|
|
108
|
+
endTime,
|
|
109
|
+
index: i % 6
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return items;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const randomTimeItemsToBase64 = (items: RandomTimeItem[]): string => {
|
|
117
|
+
const bytes = new Uint8Array(items.length * 6);
|
|
118
|
+
|
|
119
|
+
items.forEach((item, index) => {
|
|
120
|
+
const channelByte = (item.channel!! << 1) | (item.enable ? 1 : 0);
|
|
121
|
+
const week = item.weeks.reduce((acc, bit, i) => acc | (bit << i), 0);
|
|
122
|
+
const startTime = item.startTime;
|
|
123
|
+
const endTime = item.endTime;
|
|
124
|
+
|
|
125
|
+
const offset = index * 6;
|
|
126
|
+
bytes[offset] = channelByte;
|
|
127
|
+
bytes[offset + 1] = week;
|
|
128
|
+
bytes[offset + 2] = (startTime >> 8) & 0xFF;
|
|
129
|
+
bytes[offset + 3] = startTime & 0xFF;
|
|
130
|
+
bytes[offset + 4] = (endTime >> 8) & 0xFF;
|
|
131
|
+
bytes[offset + 5] = endTime & 0xFF;
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return encodeBase64(bytes);
|
|
135
|
+
}
|
|
136
|
+
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import React, {useMemo} from 'react'
|
|
2
|
+
import Page from "@ledvance/base/src/components/Page";
|
|
3
|
+
import I18n from "@ledvance/base/src/i18n";
|
|
4
|
+
import {Image, ScrollView, StyleSheet, Text, TouchableOpacity, View} from "react-native";
|
|
5
|
+
import {TimerPicker, Utils} from "tuya-panel-kit";
|
|
6
|
+
import {RandomTimeItem} from "./RandomTimeForPlugAction";
|
|
7
|
+
import res from "@ledvance/base/src/res/index";
|
|
8
|
+
import {useReactive} from "ahooks";
|
|
9
|
+
import {useNavigation, useRoute} from '@react-navigation/core';
|
|
10
|
+
import LdvTopName from "@ledvance/base/src/components/ldvTopName";
|
|
11
|
+
import TextField from "@ledvance/base/src/components/TextField";
|
|
12
|
+
import LdvWeekView from "@ledvance/base/src/components/weekSelect";
|
|
13
|
+
import Spacer from "@ledvance/base/src/components/Spacer";
|
|
14
|
+
import {loopText, showDialog} from "@ledvance/base/src/utils/common";
|
|
15
|
+
import DeleteButton from "@ledvance/base/src/components/DeleteButton";
|
|
16
|
+
import dayjs from "dayjs";
|
|
17
|
+
import {cloneDeep, isEqual} from "lodash";
|
|
18
|
+
import Summary from "./Summary";
|
|
19
|
+
import {RandomTimeParam} from "./RandomTimeForPlugAction";
|
|
20
|
+
import {parseHour12} from "@tuya/tuya-panel-lamp-sdk/lib/utils";
|
|
21
|
+
|
|
22
|
+
const {parseTimer} = Utils.TimeUtils
|
|
23
|
+
const {convertX: cx} = Utils.RatioUtils;
|
|
24
|
+
|
|
25
|
+
interface RandomTimeDetailParam extends RandomTimeParam {
|
|
26
|
+
mode: 'add' | 'edit',
|
|
27
|
+
item: RandomTimeItem,
|
|
28
|
+
onPost: (mode: 'add' | 'edit' | 'del', item: RandomTimeItem, goBack?: boolean) => Promise<void>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const newRandomTimeItem = (): RandomTimeItem => {
|
|
32
|
+
const startTime = dayjs().hour() * 60 + dayjs().minute()
|
|
33
|
+
return {
|
|
34
|
+
enable: true,
|
|
35
|
+
channel: 0,
|
|
36
|
+
weeks: [0, 0, 0, 0, 0, 0, 0, 0],
|
|
37
|
+
startTime: startTime,
|
|
38
|
+
endTime: startTime + 30
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const RandomTimeForPlugDetailPage = () => {
|
|
43
|
+
const navigation = useNavigation()
|
|
44
|
+
const params = useRoute().params as RandomTimeDetailParam
|
|
45
|
+
const state = useReactive({
|
|
46
|
+
item: params.mode === 'add' ? newRandomTimeItem() : cloneDeep(params.item),
|
|
47
|
+
loading: false
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const canSave = useMemo(() => {
|
|
51
|
+
return state.item.name && state.item.name.length < 33 && state.item.channel !== undefined
|
|
52
|
+
}, [state.item.name, state.item.channel])
|
|
53
|
+
|
|
54
|
+
const showConfirm = useMemo(() => {
|
|
55
|
+
return params.mode === 'edit' && !isEqual(state.item, params.item)
|
|
56
|
+
}, [JSON.stringify(state.item), JSON.stringify(params.item)])
|
|
57
|
+
|
|
58
|
+
const onSave = async () => {
|
|
59
|
+
if (!canSave) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
const timeInterval = state.item.endTime - state.item.startTime;
|
|
63
|
+
if (timeInterval >= 0 && timeInterval < 30) {
|
|
64
|
+
showDialog({
|
|
65
|
+
method: 'alert',
|
|
66
|
+
title: I18n.getLang('conflict_dialog_save_item_randomtimecycle_tips'),
|
|
67
|
+
subTitle: I18n.getLang('conflict_dialog_save_item_randomtimecycle_interval_description'),
|
|
68
|
+
onConfirm: async (_, { close }) => {
|
|
69
|
+
close()
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
await params.onPost(params.mode, cloneDeep(state.item), true);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const onDelete = () => {
|
|
78
|
+
return showDialog({
|
|
79
|
+
method: 'confirm',
|
|
80
|
+
title: I18n.getLang('cancel_dialog_delete_item_randomtimecycle_titel'),
|
|
81
|
+
subTitle: I18n.getLang('cancel_dialog_delete_item_randomtimecycle_description'),
|
|
82
|
+
onConfirm: async (_, {close}) => {
|
|
83
|
+
close()
|
|
84
|
+
await params.onPost('del', state.item, true)
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
return (
|
|
89
|
+
<Page
|
|
90
|
+
backText={I18n.getLang('randomtimecycle_sockets_headline_text')}
|
|
91
|
+
onBackClick={!showConfirm ? navigation.goBack : undefined}
|
|
92
|
+
rightButtonIcon={canSave ? res.ic_check : res.ic_uncheck}
|
|
93
|
+
rightButtonDisabled={state.loading}
|
|
94
|
+
loading={state.loading}
|
|
95
|
+
showBackDialog={showConfirm}
|
|
96
|
+
backDialogTitle={I18n.getLang('cancel_dialog_leave_unsaved_titel')}
|
|
97
|
+
backDialogContent={I18n.getLang('cancel_dialog_delete_item_randomtimecycle_description')}
|
|
98
|
+
rightButtonIconClick={onSave}
|
|
99
|
+
>
|
|
100
|
+
<ScrollView nestedScrollEnabled={true}>
|
|
101
|
+
<LdvTopName
|
|
102
|
+
title={I18n.getLang(params.mode === 'add' ? 'add_fixedtimecycle_headline_text' : 'edit_fixedtimecycle_headline_text')}/>
|
|
103
|
+
<TextField
|
|
104
|
+
style={styles.cardContainer}
|
|
105
|
+
value={state.item.name}
|
|
106
|
+
showError={(state.item.name?.length || 0) > 32}
|
|
107
|
+
maxLength={33}
|
|
108
|
+
errorText={I18n.getLang('add_new_dynamic_mood_alert_text')}
|
|
109
|
+
placeholder={I18n.getLang('add_new_trigger_time_inputfield_value_text')}
|
|
110
|
+
onChangeText={(t: string) => {
|
|
111
|
+
state.item.name = t;
|
|
112
|
+
}}
|
|
113
|
+
/>
|
|
114
|
+
{/* pick */}
|
|
115
|
+
<TimerPicker
|
|
116
|
+
itemTextColor='#aeadb5'
|
|
117
|
+
style={{paddingVertical: cx(0), marginVertical: cx(0)}}
|
|
118
|
+
is12Hours={!params.is24Hour}
|
|
119
|
+
startTime={state.item.startTime}
|
|
120
|
+
endTime={state.item.endTime}
|
|
121
|
+
onTimerChange={(startTime, endTime) => {
|
|
122
|
+
state.item.startTime = startTime
|
|
123
|
+
state.item.endTime = endTime
|
|
124
|
+
}}/>
|
|
125
|
+
<LdvWeekView
|
|
126
|
+
value={state.item.weeks}
|
|
127
|
+
style={styles.cardContainer}
|
|
128
|
+
onSelect={(index: number) => {
|
|
129
|
+
const rawIndex = index - 1
|
|
130
|
+
state.item.weeks[rawIndex] = state.item.weeks[rawIndex] === 1 ? 0 : 1
|
|
131
|
+
}}/>
|
|
132
|
+
<Spacer/>
|
|
133
|
+
<Text style={styles.cardContainer}>{loopText(state.item.weeks, parseTimer(state.item.startTime * 60))}</Text>
|
|
134
|
+
<Spacer/>
|
|
135
|
+
{/*Apply for */}
|
|
136
|
+
<View style={styles.cardContainer}>
|
|
137
|
+
<Text style={styles.itemTitle}>{I18n.getLang('timeschedule_add_schedule_subheadline_text')}</Text>
|
|
138
|
+
<Spacer height={cx(10)}/>
|
|
139
|
+
<View style={styles.applyContent}>
|
|
140
|
+
{state.item.channel === undefined ?
|
|
141
|
+
<Text>{I18n.getLang('timer_ceiling_fan_selectionfield_no_components_text')}</Text> :
|
|
142
|
+
<View style={[styles.applyItem, {marginBottom: cx(10), borderRadius: 4}]}>
|
|
143
|
+
<Text style={{color: '#000'}}>{params.channels[state.item.channel]}</Text>
|
|
144
|
+
{params.channels.length > 1 && <TouchableOpacity
|
|
145
|
+
onPress={() => {
|
|
146
|
+
state.item.channel = undefined
|
|
147
|
+
}}
|
|
148
|
+
style={{paddingHorizontal: cx(5)}}>
|
|
149
|
+
<Image style={{width: cx(16), height: cx(16)}} source={res.ic_arrows_nav_clear}/>
|
|
150
|
+
</TouchableOpacity>}
|
|
151
|
+
</View>
|
|
152
|
+
}
|
|
153
|
+
</View>
|
|
154
|
+
{params.channels.map((item: string, index: number) => {
|
|
155
|
+
if (state.item.channel === index) {
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
return (
|
|
159
|
+
<TouchableOpacity
|
|
160
|
+
style={styles.applyItem}
|
|
161
|
+
key={item}
|
|
162
|
+
onPress={() => {
|
|
163
|
+
state.item.channel = index
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
<Text style={{color: '#000'}}>{item}</Text>
|
|
167
|
+
<Image style={{width: cx(16), height: cx(16)}} source={res.device_panel_timer_add}/>
|
|
168
|
+
</TouchableOpacity>
|
|
169
|
+
)
|
|
170
|
+
})}
|
|
171
|
+
</View>
|
|
172
|
+
<Spacer/>
|
|
173
|
+
{/* summary */}
|
|
174
|
+
<Summary
|
|
175
|
+
frequency={loopText(state.item.weeks)}
|
|
176
|
+
time={params.is24Hour ?
|
|
177
|
+
`${parseTimer(state.item.startTime * 60)} - ${parseTimer(state.item.endTime * 60)}`
|
|
178
|
+
:
|
|
179
|
+
`${parseHour12(state.item.startTime * 60)} - ${parseHour12(state.item.endTime * 60)}`
|
|
180
|
+
}
|
|
181
|
+
hideActions={true}
|
|
182
|
+
/>
|
|
183
|
+
<Spacer/>
|
|
184
|
+
{params.mode === 'edit' &&
|
|
185
|
+
<View style={{marginHorizontal: cx(24)}}>
|
|
186
|
+
<DeleteButton
|
|
187
|
+
text={I18n.getLang('edit_fixedtimecycle_bttn_text')}
|
|
188
|
+
onPress={onDelete}/>
|
|
189
|
+
</View>
|
|
190
|
+
}
|
|
191
|
+
<Spacer/>
|
|
192
|
+
</ScrollView>
|
|
193
|
+
</Page>
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const styles = StyleSheet.create({
|
|
198
|
+
cardContainer: {
|
|
199
|
+
marginHorizontal: cx(24)
|
|
200
|
+
},
|
|
201
|
+
itemTitle: {
|
|
202
|
+
color: '#000',
|
|
203
|
+
fontSize: cx(16),
|
|
204
|
+
fontWeight: 'bold',
|
|
205
|
+
fontFamily: 'helvetica_neue_lt_std_bd',
|
|
206
|
+
},
|
|
207
|
+
applyContent: {
|
|
208
|
+
backgroundColor: '#f6f6f6',
|
|
209
|
+
borderRadius: 4,
|
|
210
|
+
minHeight: cx(55),
|
|
211
|
+
flex: 1,
|
|
212
|
+
justifyContent: 'center',
|
|
213
|
+
paddingHorizontal: cx(10),
|
|
214
|
+
paddingTop: cx(10)
|
|
215
|
+
},
|
|
216
|
+
applyItem: {
|
|
217
|
+
paddingLeft: cx(5),
|
|
218
|
+
flexDirection: 'row',
|
|
219
|
+
justifyContent: 'space-between',
|
|
220
|
+
alignItems: 'center',
|
|
221
|
+
backgroundColor: '#fff',
|
|
222
|
+
height: cx(35),
|
|
223
|
+
},
|
|
224
|
+
summaryContainer: {
|
|
225
|
+
flexDirection: 'row',
|
|
226
|
+
justifyContent: 'flex-start',
|
|
227
|
+
marginBottom: cx(10)
|
|
228
|
+
},
|
|
229
|
+
summaryLeft: {
|
|
230
|
+
flexDirection: 'row',
|
|
231
|
+
justifyContent: 'flex-start',
|
|
232
|
+
alignItems: 'center',
|
|
233
|
+
},
|
|
234
|
+
summaryImg: {
|
|
235
|
+
width: cx(12),
|
|
236
|
+
height: cx(12),
|
|
237
|
+
marginRight: cx(6)
|
|
238
|
+
},
|
|
239
|
+
leftTitle: {
|
|
240
|
+
fontSize: cx(14),
|
|
241
|
+
color: '#000'
|
|
242
|
+
},
|
|
243
|
+
summaryRight: {
|
|
244
|
+
flexDirection: 'column',
|
|
245
|
+
marginLeft: cx(21),
|
|
246
|
+
borderRadius: cx(16),
|
|
247
|
+
backgroundColor: '#cbcbcb',
|
|
248
|
+
},
|
|
249
|
+
rightItem: {
|
|
250
|
+
paddingHorizontal: cx(12),
|
|
251
|
+
color: '#000',
|
|
252
|
+
},
|
|
253
|
+
rightTitle: {
|
|
254
|
+
paddingLeft: cx(12),
|
|
255
|
+
paddingRight: cx(12),
|
|
256
|
+
fontSize: cx(10),
|
|
257
|
+
textAlign: 'center',
|
|
258
|
+
alignSelf: 'flex-start'
|
|
259
|
+
},
|
|
260
|
+
filletCorner: {
|
|
261
|
+
flexDirection: 'row',
|
|
262
|
+
backgroundColor: '#cbcbcb',
|
|
263
|
+
borderRadius: cx(16),
|
|
264
|
+
alignSelf: 'flex-start'
|
|
265
|
+
}
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
export default RandomTimeForPlugDetailPage
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import Page from "@ledvance/base/src/components/Page";
|
|
2
|
+
import {useDeviceInfo} from "@ledvance/base/src/models/modules/NativePropsSlice";
|
|
3
|
+
import {useNavigation, useRoute} from '@react-navigation/native'
|
|
4
|
+
import I18n from "@ledvance/base/src/i18n/index";
|
|
5
|
+
import res from "@ledvance/base/src/res/index";
|
|
6
|
+
import {useReactive} from "ahooks";
|
|
7
|
+
import React, {useCallback, useMemo} from "react";
|
|
8
|
+
import {FlatList, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View} from "react-native";
|
|
9
|
+
import {Utils} from "tuya-panel-kit";
|
|
10
|
+
import {ui_biz_routerKey} from "../../navigation/Routers";
|
|
11
|
+
import {RandomTimeItem, RandomTimeParam, useRandomTime} from "./RandomTimeForPlugAction";
|
|
12
|
+
import Spacer from "@ledvance/base/src/components/Spacer";
|
|
13
|
+
import ItemCard from "./ItemCard";
|
|
14
|
+
import {cloneDeep} from "lodash";
|
|
15
|
+
|
|
16
|
+
const {convertX: cx, topBarHeight} = Utils.RatioUtils;
|
|
17
|
+
const MAX_NUM = 16
|
|
18
|
+
|
|
19
|
+
const RandomTimeForPlugPage = () => {
|
|
20
|
+
const devInfo = useDeviceInfo()
|
|
21
|
+
const navigation = useNavigation()
|
|
22
|
+
const params = useRoute().params as RandomTimeParam
|
|
23
|
+
const [randomTimeList, setRandomTimeList] = useRandomTime()
|
|
24
|
+
const state = useReactive({
|
|
25
|
+
loading: false
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const isMaxNum = useMemo(() => {
|
|
29
|
+
return randomTimeList.length >= MAX_NUM
|
|
30
|
+
}, [randomTimeList])
|
|
31
|
+
|
|
32
|
+
const onAddOrEditItem = (mode: 'add' | 'edit', item?: RandomTimeItem) => {
|
|
33
|
+
const path = ui_biz_routerKey.group_ui_biz_random_time_plug_detail
|
|
34
|
+
navigation.navigate(path, {
|
|
35
|
+
...params,
|
|
36
|
+
mode,
|
|
37
|
+
item,
|
|
38
|
+
onPost
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const onPost = useCallback(async (mode: 'add' | 'edit' | 'del', randomTime: RandomTimeItem, goBack?: boolean) => {
|
|
43
|
+
state.loading = true
|
|
44
|
+
const cloneRandomTimeList = cloneDeep(randomTimeList)
|
|
45
|
+
const idx = randomTimeList.findIndex(it => it.index === randomTime.index)
|
|
46
|
+
if (mode === 'edit') {
|
|
47
|
+
cloneRandomTimeList.splice(idx, 1, randomTime)
|
|
48
|
+
}
|
|
49
|
+
if (mode === 'del') cloneRandomTimeList.splice(idx, 1)
|
|
50
|
+
const newRandomTimeList = mode === 'add' ? [...randomTimeList, {
|
|
51
|
+
...randomTime,
|
|
52
|
+
index: randomTimeList.length
|
|
53
|
+
}] : cloneRandomTimeList
|
|
54
|
+
const res = await setRandomTimeList(newRandomTimeList)
|
|
55
|
+
state.loading = false
|
|
56
|
+
if (res.success && goBack) {
|
|
57
|
+
navigation.goBack()
|
|
58
|
+
}
|
|
59
|
+
}, [randomTimeList])
|
|
60
|
+
|
|
61
|
+
const renderList = () => {
|
|
62
|
+
return (
|
|
63
|
+
<ScrollView nestedScrollEnabled={true}>
|
|
64
|
+
<Text style={{
|
|
65
|
+
color: '#000',
|
|
66
|
+
marginLeft: cx(24),
|
|
67
|
+
}}>{I18n.getLang('timeschedule_overview_description_text')}</Text>
|
|
68
|
+
<Spacer height={cx(10)}/>
|
|
69
|
+
{isMaxNum && <View style={{marginHorizontal: cx(24), flexDirection: 'row'}}>
|
|
70
|
+
<Image style={{width: cx(16), height: cx(16), tintColor: '#ff9500'}} source={res.ic_warning_amber}/>
|
|
71
|
+
<Text>{I18n.getLang('fixedtimecycle_warning_max_number_text')}</Text>
|
|
72
|
+
</View>}
|
|
73
|
+
<FlatList
|
|
74
|
+
data={randomTimeList}
|
|
75
|
+
renderItem={({item}) => (
|
|
76
|
+
<ItemCard
|
|
77
|
+
item={item}
|
|
78
|
+
is24Hour={params.is24Hour}
|
|
79
|
+
onSwitch={async (v) => {
|
|
80
|
+
await onPost('edit', {
|
|
81
|
+
...item,
|
|
82
|
+
enable: v,
|
|
83
|
+
})
|
|
84
|
+
}}
|
|
85
|
+
onPress={() => {
|
|
86
|
+
onAddOrEditItem('edit', item)
|
|
87
|
+
}}
|
|
88
|
+
/>
|
|
89
|
+
)}
|
|
90
|
+
keyExtractor={(item: any) => `${item?.index}`}
|
|
91
|
+
ItemSeparatorComponent={() => <Spacer/>}
|
|
92
|
+
ListHeaderComponent={<Spacer height={cx(10)}/>}
|
|
93
|
+
ListFooterComponent={<Spacer/>}
|
|
94
|
+
/>
|
|
95
|
+
</ScrollView>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
const renderEmpty = () => {
|
|
99
|
+
return (
|
|
100
|
+
<View style={styles.emptyListCon}>
|
|
101
|
+
<Image
|
|
102
|
+
style={styles.emptyImage}
|
|
103
|
+
source={{uri: res.ldv_timer_empty}}
|
|
104
|
+
resizeMode="contain"/>
|
|
105
|
+
<View
|
|
106
|
+
style={{
|
|
107
|
+
marginHorizontal: cx(24),
|
|
108
|
+
marginTop: cx(30),
|
|
109
|
+
flexDirection: 'row',
|
|
110
|
+
alignItems: 'center',
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
<Image
|
|
114
|
+
source={{uri: res.device_panel_schedule_alert}}
|
|
115
|
+
style={{width: cx(16), height: cx(16)}}
|
|
116
|
+
/>
|
|
117
|
+
<Text style={styles.emptyNoTime}>{I18n.getLang('randomtimecycle_empty_information_text')}</Text>
|
|
118
|
+
</View>
|
|
119
|
+
<View
|
|
120
|
+
style={{
|
|
121
|
+
height: cx(36),
|
|
122
|
+
width: cx(150),
|
|
123
|
+
marginVertical: cx(25),
|
|
124
|
+
marginHorizontal: cx(24),
|
|
125
|
+
borderRadius: cx(6),
|
|
126
|
+
backgroundColor: '#f60',
|
|
127
|
+
}}
|
|
128
|
+
>
|
|
129
|
+
<TouchableOpacity
|
|
130
|
+
style={{
|
|
131
|
+
flex: 1,
|
|
132
|
+
justifyContent: 'center',
|
|
133
|
+
alignItems: 'center',
|
|
134
|
+
}}
|
|
135
|
+
onPress={() => onAddOrEditItem('add')}
|
|
136
|
+
>
|
|
137
|
+
<Text style={{fontSize: cx(12), fontWeight: 'bold', color: '#fff', textAlign: 'center'}}>
|
|
138
|
+
{I18n.getLang('randomtimecycle_empty_bttn_text')}
|
|
139
|
+
</Text>
|
|
140
|
+
</TouchableOpacity>
|
|
141
|
+
</View>
|
|
142
|
+
</View>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
return (
|
|
146
|
+
<Page
|
|
147
|
+
backText={devInfo.name}
|
|
148
|
+
headlineText={I18n.getLang('randomtimecycle_sockets_headline_text')}
|
|
149
|
+
headlineIcon={!isMaxNum && res.device_panel_schedule_add || undefined}
|
|
150
|
+
onHeadlineIconClick={() => onAddOrEditItem('add')}
|
|
151
|
+
loading={state.loading}
|
|
152
|
+
>
|
|
153
|
+
{randomTimeList.length > 0 ? renderList() : renderEmpty()}
|
|
154
|
+
</Page>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const styles = StyleSheet.create({
|
|
159
|
+
bg: {
|
|
160
|
+
marginTop: topBarHeight,
|
|
161
|
+
flex: 1,
|
|
162
|
+
},
|
|
163
|
+
emptyListCon: {
|
|
164
|
+
justifyContent: 'center',
|
|
165
|
+
alignItems: 'center',
|
|
166
|
+
marginTop: cx(30),
|
|
167
|
+
},
|
|
168
|
+
emptyImage: {
|
|
169
|
+
width: cx(225),
|
|
170
|
+
height: cx(198)
|
|
171
|
+
},
|
|
172
|
+
emptyNoTime: {
|
|
173
|
+
fontSize: cx(12),
|
|
174
|
+
color: '#000'
|
|
175
|
+
},
|
|
176
|
+
emptyTimeTip: {
|
|
177
|
+
fontSize: cx(12),
|
|
178
|
+
lineHeight: cx(17),
|
|
179
|
+
marginTop: cx(6),
|
|
180
|
+
width: '76%',
|
|
181
|
+
textAlign: 'center'
|
|
182
|
+
},
|
|
183
|
+
listContainer: {
|
|
184
|
+
bottom: cx(74)
|
|
185
|
+
},
|
|
186
|
+
categoryList: {
|
|
187
|
+
marginHorizontal: cx(24),
|
|
188
|
+
marginBottom: cx(12),
|
|
189
|
+
},
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
export default RandomTimeForPlugPage
|