Skip to content

Commit 6e346ba

Browse files
committed
refactor: popup rules
1 parent d53289e commit 6e346ba

7 files changed

Lines changed: 233 additions & 298 deletions

File tree

src/pages/popup/index.tsx

Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -34,48 +34,24 @@ const basicStyle = css`
3434
display: flex;
3535
flex-direction: column;
3636
37-
.item-block {
37+
.group-title {
38+
padding: 8px;
39+
font-weight: bold;
40+
color: var(--semi-color-tertiary);
3841
display: flex;
39-
flex-direction: column;
40-
41-
> .title {
42-
padding: 8px;
43-
font-weight: bold;
44-
color: var(--semi-color-tertiary);
45-
display: flex;
46-
align-items: center;
47-
justify-content: space-between;
42+
align-items: center;
43+
justify-content: space-between;
4844
49-
&.group {
50-
padding-top: 4px;
51-
padding-right: 4px;
52-
padding-bottom: 4px;
53-
}
45+
&.toggle {
46+
padding-top: 4px;
47+
padding-right: 4px;
48+
padding-bottom: 4px;
5449
}
50+
}
5551
56-
.item {
57-
display: flex;
58-
flex-direction: row;
59-
gap: 8px;
60-
align-items: center;
61-
background-color: var(--semi-color-bg-1);
62-
border-top: 1px solid var(--semi-color-border);
63-
padding-left: 8px;
64-
padding-right: 8px;
65-
66-
> * {
67-
flex-grow: 0;
68-
flex-shrink: 0;
69-
}
70-
71-
> .name {
72-
flex-grow: 1;
73-
flex-shrink: 1;
74-
font-size: 14px;
75-
padding-top: 8px;
76-
padding-bottom: 8px;
77-
}
78-
}
52+
.main-list {
53+
display: flex;
54+
flex-direction: column;
7955
}
8056
}
8157
`;

src/pages/popup/rules/all-rules.tsx

Lines changed: 45 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,67 @@
1-
import { IconBranch } from '@douyinfe/semi-icons';
2-
import { Button, Popover, Spin, Switch } from '@douyinfe/semi-ui';
3-
import { css, cx } from '@emotion/css';
1+
import { Spin } from '@douyinfe/semi-ui';
42
import { useRequest } from 'ahooks';
5-
import { flatten } from 'lodash-es';
6-
import { Fragment, useEffect } from 'react';
7-
import RuleContentSwitcher from '@/share/components/rule-content-switcher';
8-
import RuleDetail from '@/share/components/rule-detail';
3+
import { flatten, groupBy } from 'lodash-es';
4+
import { useEffect } from 'react';
95
import { EVENTs, VIRTUAL_KEY } from '@/share/core/constant';
106
import notify from '@/share/core/notify';
11-
import type { Rule } from '@/share/core/types';
7+
import type { RuleWithVirtualKey } from '@/share/core/types';
128
import { getVirtualKey } from '@/share/core/utils';
139
import Api from '@/share/pages/api';
14-
import { textEllipsis } from '@/share/pages/styles';
15-
import QuickEdit from './quick-edit';
10+
import GroupItem from './common/group-item';
1611

1712
const AllRules = () => {
1813
const {
19-
data = [],
14+
data = {},
2015
loading,
2116
mutate,
2217
} = useRequest(
23-
async () => {
24-
const res = await Api.getAllRules();
25-
return flatten(Object.values(res))
26-
.sort((a, b) => a.group.localeCompare(b.group))
27-
.map(x => ({ ...x, [VIRTUAL_KEY]: getVirtualKey(x) }));
28-
},
18+
async () =>
19+
groupBy(
20+
flatten(Object.values(await Api.getAllRules())).map(x => ({
21+
...x,
22+
[VIRTUAL_KEY]: getVirtualKey(x),
23+
})),
24+
'group',
25+
),
2926
{
3027
manual: false,
3128
},
3229
);
3330

3431
useEffect(() => {
3532
const handleRuleUpdate = (request: any) => {
36-
const rule: Rule = request.target;
37-
const key = getVirtualKey(rule);
33+
const key = getVirtualKey(request.target);
34+
const rule: RuleWithVirtualKey = {
35+
...request.target,
36+
[VIRTUAL_KEY]: key,
37+
};
3838
mutate(currentData => {
3939
if (!currentData) {
4040
return;
4141
}
42-
const index = currentData.findIndex(x => x[VIRTUAL_KEY] === key);
43-
if (index === -1) {
44-
return currentData;
42+
const oldGroup = Object.entries(currentData).find(([_, value]) =>
43+
value.some(x => x[VIRTUAL_KEY] === key),
44+
);
45+
const newData = { ...currentData };
46+
if (!oldGroup) {
47+
// new
48+
newData[rule.group] = [...(currentData[rule.group] || []), rule];
49+
return newData;
50+
}
51+
if (oldGroup[0] !== rule.group) {
52+
// group changed
53+
newData[oldGroup[0]] = oldGroup[1].filter(
54+
x => x[VIRTUAL_KEY] !== key,
55+
);
56+
newData[rule.group] = [...(currentData[rule.group] || []), rule];
57+
return newData;
58+
} else {
59+
// group not change
60+
const index = oldGroup[1].findIndex(x => x[VIRTUAL_KEY] === key);
61+
newData[oldGroup[0]] = [...oldGroup[1]];
62+
newData[oldGroup[0]][index] = rule;
63+
return newData;
4564
}
46-
const result = [...currentData];
47-
result.splice(index, 1, {
48-
...rule,
49-
[VIRTUAL_KEY]: key,
50-
});
51-
return result;
5265
});
5366
};
5467

@@ -59,65 +72,12 @@ const AllRules = () => {
5972
};
6073
}, []);
6174

62-
if (data.length === 0 && !loading) {
63-
return null;
64-
}
65-
66-
const renderedGroup = new Set<string>();
67-
6875
return (
6976
<Spin spinning={loading}>
70-
<div className="item-block">
71-
{data.map(item => {
72-
const itemDOM = (
73-
<div className="item" key={item[VIRTUAL_KEY]}>
74-
<Switch
75-
size="small"
76-
checked={item.enable}
77-
onChange={checked =>
78-
Api.saveRule({
79-
...item,
80-
enable: checked,
81-
})
82-
}
83-
/>
84-
<Popover
85-
showArrow
86-
position="top"
87-
content={<RuleDetail rule={item} size="small" />}
88-
style={{ maxWidth: '300px' }}
89-
>
90-
<div className={cx(textEllipsis, 'name')}>{item.name}</div>
91-
</Popover>
92-
<div className="actions">
93-
<QuickEdit rule={item} />
94-
<RuleContentSwitcher
95-
rule={item}
96-
type={item.ruleType}
97-
size="small"
98-
add={false}
99-
>
100-
<Button
101-
theme="borderless"
102-
type="tertiary"
103-
size="small"
104-
icon={<IconBranch />}
105-
/>
106-
</RuleContentSwitcher>
107-
</div>
108-
</div>
109-
);
110-
111-
if (renderedGroup.has(item.group)) {
112-
return itemDOM;
113-
}
114-
return (
115-
<Fragment key={item.group}>
116-
<div className="title">{item.group}</div>
117-
{itemDOM}
118-
</Fragment>
119-
);
120-
})}
77+
<div className="main-list">
78+
{Object.entries(data).map(([group, rules]) => (
79+
<GroupItem key={group} group={group} rules={rules} hasToggle />
80+
))}
12181
</div>
12282
</Spin>
12383
);

src/pages/popup/rules/common/group-item.tsx

Lines changed: 58 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,74 @@
1-
import { Spin } from '@douyinfe/semi-ui';
2-
import { useLatest, useRequest } from 'ahooks';
3-
import { type FC, useEffect } from 'react';
4-
import { EVENTs, TABLE_NAMES_ARR, VIRTUAL_KEY } from '@/share/core/constant';
5-
import notify from '@/share/core/notify';
6-
import type { Rule } from '@/share/core/types';
7-
import { getVirtualKey } from '@/share/core/utils';
1+
import { IconLock, IconUnlock } from '@douyinfe/semi-icons';
2+
import { Button, ButtonGroup, Tooltip } from '@douyinfe/semi-ui';
3+
import { cx } from '@emotion/css';
4+
import { flatten } from 'lodash-es';
5+
import type { FC } from 'react';
6+
import { VIRTUAL_KEY } from '@/share/core/constant';
7+
import type { RuleWithVirtualKey } from '@/share/core/types';
8+
import { t } from '@/share/core/utils';
89
import Api from '@/share/pages/api';
10+
import { Toast } from '@/share/pages/toast';
911
import RuleItem from './rule-item';
1012

13+
const toggleGroup = async (name: string, target: boolean) => {
14+
const rules = flatten(Object.values(await Api.getAllRules()));
15+
const toUpdate = rules.filter(x => x.group === name);
16+
try {
17+
await Promise.all(
18+
toUpdate.map(x => {
19+
x.enable = target;
20+
return Api.saveRule(x);
21+
}),
22+
);
23+
Toast().success(t('switch_success'));
24+
} catch (e) {
25+
console.error(e);
26+
Toast().error((e as Error).message);
27+
}
28+
};
29+
1130
interface GroupItemProps {
1231
group: string;
32+
rules: RuleWithVirtualKey[];
33+
hasToggle?: boolean;
1334
}
1435

15-
const GroupItem: FC<GroupItemProps> = ({ group }) => {
16-
const {
17-
data = [],
18-
loading,
19-
refresh,
20-
} = useRequest(
21-
async () =>
22-
(
23-
await Promise.all(
24-
TABLE_NAMES_ARR.map(t =>
25-
Api.getRules(t, {
26-
group,
27-
}),
28-
),
29-
)
30-
)
31-
.flat(1)
32-
.map(x => ({ ...x, [VIRTUAL_KEY]: getVirtualKey(x) })),
33-
{
34-
manual: false,
35-
refreshDeps: [group],
36-
},
37-
);
38-
39-
const dataRef = useLatest(data);
40-
41-
useEffect(() => {
42-
const handleRuleUpdate = (request: any) => {
43-
const rule: Rule = request.target;
44-
const key = getVirtualKey(rule);
45-
if (dataRef.current.some(x => getVirtualKey(x) === key)) {
46-
refresh();
47-
}
48-
};
49-
50-
notify.event.on(EVENTs.RULE_UPDATE, handleRuleUpdate);
51-
52-
return () => {
53-
notify.event.off(EVENTs.RULE_UPDATE, handleRuleUpdate);
54-
};
55-
}, []);
56-
57-
if (Object.keys(data).length === 0 && !loading) {
36+
const GroupItem: FC<GroupItemProps> = ({ group, rules, hasToggle = true }) => {
37+
if (group.length === 0) {
5838
return null;
5939
}
6040

6141
return (
62-
<Spin spinning={loading}>
63-
{data.map(item => (
42+
<>
43+
<div className={cx('group-title', { toggle: hasToggle })}>
44+
<div className="name">{group}</div>
45+
{hasToggle && (
46+
<ButtonGroup>
47+
<Tooltip content={t('enable')}>
48+
<Button
49+
theme="borderless"
50+
type="tertiary"
51+
onClick={() => toggleGroup(group, true)}
52+
size="small"
53+
icon={<IconUnlock />}
54+
/>
55+
</Tooltip>
56+
<Tooltip content={t('disable')}>
57+
<Button
58+
theme="borderless"
59+
type="tertiary"
60+
onClick={() => toggleGroup(group, false)}
61+
size="small"
62+
icon={<IconLock />}
63+
/>
64+
</Tooltip>
65+
</ButtonGroup>
66+
)}
67+
</div>
68+
{rules.map(item => (
6469
<RuleItem key={item[VIRTUAL_KEY]} rule={item} />
6570
))}
66-
</Spin>
71+
</>
6772
);
6873
};
6974

0 commit comments

Comments
 (0)