diff options
| author | Haishan <[email protected]> | 2020-07-01 22:06:26 +0800 |
|---|---|---|
| committer | Haishan <[email protected]> | 2020-07-04 17:58:56 +0800 |
| commit | 32bed273c83f0593187110d2b08a0f9ec5a7efd7 (patch) | |
| tree | 0b47da752de3ee0d87945c1122b2cf9d3bf8043f /src/components/Rules.js | |
| parent | 55e928a87f561ab927774834b50e099a0758522d (diff) | |
feat: support rule provider
Diffstat (limited to 'src/components/Rules.js')
| -rw-r--r-- | src/components/Rules.js | 115 |
1 files changed, 92 insertions, 23 deletions
diff --git a/src/components/Rules.js b/src/components/Rules.js index a04116a..949e5e9 100644 --- a/src/components/Rules.js +++ b/src/components/Rules.js @@ -1,27 +1,63 @@ import React from 'react'; import { RotateCw } from 'react-feather'; -import { areEqual, FixedSizeList as List } from 'react-window'; +import { queryCache, useQuery } from 'react-query'; +import { areEqual, VariableSizeList } from 'react-window'; +import { useRecoilState } from 'recoil'; +import { fetchRuleProviders } from 'src/api/rule-provider'; +import { fetchRules } from 'src/api/rules'; +import { RuleProviderItem } from 'src/components/rules/RuleProviderItem'; +import { TextFilter } from 'src/components/rules/TextFilter'; +import { ruleFilterText } from 'src/store/rules'; import useRemainingViewPortHeight from '../hooks/useRemainingViewPortHeight'; import { getClashAPIConfig } from '../store/app'; -import { fetchRules, fetchRulesOnce, getRules } from '../store/rules'; import ContentHeader from './ContentHeader'; import Rule from './Rule'; -import RuleSearch from './RuleSearch'; +import s from './Rules.module.css'; import { Fab, position as fabPosition } from './shared/Fab'; import { connect } from './StateProvider'; -const { memo, useEffect, useMemo, useCallback } = React; +const { memo, useMemo, useCallback } = React; const paddingBottom = 30; -function itemKey(index, data) { - const item = data[index]; +function itemKey(index, { rules, provider }) { + const providerQty = provider.names.length; + + if (index < providerQty) { + return provider.names[index]; + } + const item = rules[index - providerQty]; return item.id; } +function getItemSizeFactory({ provider }) { + return function getItemSize(idx) { + const providerQty = provider.names.length; + if (idx < providerQty) { + // provider + return 90; + } + // rule + return 80; + }; +} + const Row = memo(({ index, style, data }) => { - const r = data[index]; + const { rules, provider, apiConfig } = data; + const providerQty = provider.names.length; + + if (index < providerQty) { + const name = provider.names[index]; + const item = provider.byName[name]; + return ( + <div style={style} className={s.RuleProviderItemWrapper}> + <RuleProviderItem apiConfig={apiConfig} {...item} /> + </div> + ); + } + + const r = rules[index - providerQty]; return ( <div style={style}> <Rule {...r} /> @@ -31,42 +67,75 @@ const Row = memo(({ index, style, data }) => { const mapState = (s) => ({ apiConfig: getClashAPIConfig(s), - rules: getRules(s), }); export default connect(mapState)(Rules); -function Rules({ dispatch, apiConfig, rules }) { - const fetchRulesHooked = useCallback(() => { - dispatch(fetchRules(apiConfig)); - }, [apiConfig, dispatch]); - useEffect(() => { - dispatch(fetchRulesOnce(apiConfig)); - }, [dispatch, apiConfig]); +function useRuleAndProvider(apiConfig) { + const { data: rules } = useQuery(['/rules', apiConfig], fetchRules, { + suspense: true, + }); + const { data: provider } = useQuery( + ['/providers/rules', apiConfig], + fetchRuleProviders, + { suspense: true } + ); + + const [filterText] = useRecoilState(ruleFilterText); + if (filterText === '') { + return { rules, provider }; + } else { + const f = filterText.toLowerCase(); + return { + rules: rules.filter((r) => r.payload.toLowerCase().indexOf(f) >= 0), + provider: { + byName: provider.byName, + names: provider.names.filter((t) => t.toLowerCase().indexOf(f) >= 0), + }, + }; + } +} + +function useInvalidateQueries() { + return useCallback(() => { + queryCache.invalidateQueries('/rules'); + queryCache.invalidateQueries('/providers/rules'); + }, []); +} + +function Rules({ apiConfig }) { const [refRulesContainer, containerHeight] = useRemainingViewPortHeight(); const refreshIcon = useMemo(() => <RotateCw width={16} />, []); + + const { rules, provider } = useRuleAndProvider(apiConfig); + const invalidateQueries = useInvalidateQueries(); + + const getItemSize = getItemSizeFactory({ rules, provider }); + return ( <div> - <ContentHeader title="Rules" /> - <RuleSearch /> + <div className={s.header}> + <ContentHeader title="Rules" /> + <TextFilter /> + </div> <div ref={refRulesContainer} style={{ paddingBottom }}> - <List + <VariableSizeList height={containerHeight - paddingBottom} width="100%" - itemCount={rules.length} - itemSize={80} - itemData={rules} + itemCount={rules.length + provider.names.length} + itemSize={getItemSize} + itemData={{ rules, provider, apiConfig }} itemKey={itemKey} > {Row} - </List> + </VariableSizeList> </div> <Fab icon={refreshIcon} text="Refresh" - onClick={fetchRulesHooked} position={fabPosition} + onClick={invalidateQueries} /> </div> ); |
