Skip to content

Commit

Permalink
feat: grab proxy-groups names from config
Browse files Browse the repository at this point in the history
  • Loading branch information
magicdawn committed Dec 15, 2023
1 parent 09bafa2 commit be21188
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 54 deletions.
9 changes: 4 additions & 5 deletions packages/ui/src/page/current-config/ConfigDND.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ConfigItem } from '$ui/define'
import { cx } from '$ui/libs'
import { rootState } from '$ui/store'
import { limitLines } from '$ui/util/text-util'
import { InfoCircleOutlined, QuestionCircleFilled } from '@ant-design/icons'
import { useMemoizedFn } from 'ahooks'
Expand All @@ -9,16 +8,16 @@ import debugFactory from 'debug'
import { useEffect, useMemo, useState } from 'react'
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'
import { useSnapshot } from 'valtio'
import { state as libraryRuleListState } from '../library-rule-list/model'
import { state as librarySubscribeState } from '../library-subscribe/model'
import styles from './ConfigDND.module.less'
import { state } from './model'

const dndDebug = debugFactory('app:page:current-config:ConfigDND')

export function ConfigDND() {
// subscribe
const rootStateSnap = useSnapshot(rootState)

const subscribeList = rootStateSnap.librarySubscribe.list
const subscribeList = useSnapshot(librarySubscribeState.list)
const subscribeSourceList = useMemo(() => {
return subscribeList.map((item) => {
return {
Expand All @@ -31,7 +30,7 @@ export function ConfigDND() {
}, [subscribeList])

// rule
const ruleList = rootStateSnap.libraryRuleList.list
const ruleList = useSnapshot(libraryRuleListState.list)
const ruleSourceList = useMemo(() => {
return ruleList.map((item) => {
return {
Expand Down
124 changes: 81 additions & 43 deletions packages/ui/src/page/library-rule-list/AddRuleModal.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { message } from '$ui/store'
import { genConfig } from '$ui/util/gen'
import { css } from '@emotion/react'
import { useMemoizedFn, useUpdateEffect } from 'ahooks'
import { AutoComplete, Button, Col, Input, Modal, Row, Select, Space } from 'antd'
import AppleScript from 'applescript'
import { clipboard } from 'electron'
import Yaml from 'js-yaml'
import { uniq } from 'lodash'
import pify from 'promise.ify'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { tldExists } from 'tldjs'
Expand All @@ -18,10 +20,10 @@ const { Option } = Select
// from-global: 从主页中直接打开
export type Mode = 'from-rule' | 'from-global'

type ClashRuleType = 'DOMAIN-SUFFIX' | 'DOMAIN-KEYWORD'
const TYPES: ClashRuleType[] = ['DOMAIN-SUFFIX', 'DOMAIN-KEYWORD']
type ClashRuleType = 'DOMAIN-SUFFIX' | 'DOMAIN-KEYWORD' | 'DOMAIN'
const TYPES: ClashRuleType[] = ['DOMAIN-SUFFIX', 'DOMAIN-KEYWORD', 'DOMAIN']

const TARGETS = ['Proxy', 'DIRECT', 'REJECT']
const DEFAULT_TARGETS = ['Proxy', 'DIRECT', 'REJECT']

interface IProps {
visible: boolean
Expand Down Expand Up @@ -70,6 +72,22 @@ export default function AddRuleModal(props: IProps) {
setVisible(false)
})

/**
* rule detail
*/

const [type, setType] = useState<ClashRuleType>(TYPES[0])
const [url, setUrl] = useState('')
const [target, setTarget] = useState(DEFAULT_TARGETS[0])

const [extraTargets, setExtraTargets] = useState<string[]>([])
const [targetAutoCompleteList, setTargetAutoCompleteList] = useState<string[]>([])

const allTargets = useMemo(
() => uniq([target, ...DEFAULT_TARGETS, ...extraTargets].filter(Boolean)),
[target, extraTargets],
)

/**
* source url
*/
Expand All @@ -78,6 +96,7 @@ export default function AddRuleModal(props: IProps) {
const [autoCompletes, setAutoCompletes] = useState<Record<ClashRuleType, string[]>>(() => ({
'DOMAIN-KEYWORD': [],
'DOMAIN-SUFFIX': [],
'DOMAIN': [],
}))

const changeProcessUrl = useMemoizedFn((url: string) => {
Expand All @@ -93,35 +112,65 @@ export default function AddRuleModal(props: IProps) {
setAutoCompletes(data)
})

const readClipboardUrl = useCallback(() => {
const useClipboardUrl = useCallback(() => {
const url = clipboard.readText()
if (url) {
changeProcessUrl(url)
}
}, [])

const readChromeUrl = useCallback(async () => {
const useChromeUrl = useCallback(async () => {
const url = await getChromeUrl()
if (url) {
message.success('获取 chrome url 成功')
changeProcessUrl(url)
}
}, [])

const updateExtraTargets = useMemoizedFn(async () => {
const config = await genConfig()
const proxyGroupNames = (config['proxy-groups'] || []).map((x) => x.name)
setExtraTargets(proxyGroupNames)
await new Promise<void>((resolve) => setTimeout(resolve))
resetTargetAutoCompleteList()
})

const resetTargetAutoCompleteList = useMemoizedFn(() => {
setTargetAutoCompleteList(allTargets)
})

// default use chrome url
useUpdateEffect(() => {
if (visible) {
readChromeUrl()
useChromeUrl()
updateExtraTargets()
}
}, [visible])

/**
* rule detail
*/
const onSearchTargets = useMemoizedFn((text: string) => {
// TODO: add fuzzy search

const [type, setType] = useState<ClashRuleType>(TYPES[0])
const [url, setUrl] = useState('')
const [target, setTarget] = useState(TARGETS[0])
if (!text) {
setTargetAutoCompleteList([])
return
}

const _text = text
text = text.toLowerCase()
const searchFrom = uniq(extraTargets)
const filtered = uniq([
_text,
...DEFAULT_TARGETS,
...searchFrom.filter((name) => {
return name.toLowerCase().startsWith(text)
}),
...searchFrom.filter((name) => {
return name.toLowerCase().includes(text)
}),
])

setTargetAutoCompleteList(filtered)
})

const curAutoCompletes = useMemo(() => {
return autoCompletes[type] || []
Expand All @@ -137,7 +186,7 @@ export default function AddRuleModal(props: IProps) {
* ui style
*/

const layout = [{ span: 7 }, { flex: 1 }, { span: 4 }]
const layout = [{ span: 6 }, { flex: 1 }, { span: 6 }]

const okButtonProps = useMemo(() => {
const disabled = !(type && target && url)
Expand Down Expand Up @@ -168,8 +217,8 @@ export default function AddRuleModal(props: IProps) {
/>
<div style={{ marginTop: 10 }}>
<Space direction='horizontal'>
<Button onClick={readClipboardUrl}>从剪贴板读取</Button>
<Button type='primary' onClick={readChromeUrl}>
<Button onClick={useClipboardUrl}>从剪贴板读取</Button>
<Button type='primary' onClick={useChromeUrl}>
从 Google Chrome 读取
</Button>
</Space>
Expand All @@ -193,33 +242,24 @@ export default function AddRuleModal(props: IProps) {
</Col>

<Col {...layout[1]}>
<AutoComplete value={url} onChange={setUrl} style={{ width: '100%' }}>
{url && !curAutoCompletes.includes(url) && (
<Option key={`${type}-custom`} value={url}>
{url}
</Option>
)}
{curAutoCompletes.map((t) => (
<Option key={`${type}-${t}`} value={t}>
{t}
</Option>
))}
</AutoComplete>
<AutoComplete
value={url}
onChange={setUrl}
style={{ width: '100%' }}
options={curAutoCompletes.map((value) => ({ value }))}
/>
</Col>

<Col {...layout[2]}>
<AutoComplete value={target} onChange={setTarget} style={{ width: '100%' }}>
{!TARGETS.includes(target) && (
<Option key='custom' value={target}>
{target}
</Option>
)}
{TARGETS.map((t) => (
<Option key={t} value={t}>
{t}
</Option>
))}
</AutoComplete>
<AutoComplete
value={target}
onChange={setTarget}
style={{ width: '100%' }}
options={(!target ? allTargets : targetAutoCompleteList).map((value) => ({ value }))}
onSearch={onSearchTargets}
onSelect={resetTargetAutoCompleteList}
allowClear
/>
</Col>
</Row>

Expand Down Expand Up @@ -330,12 +370,9 @@ async function getChromeUrl() {
return url
}

// FIXME
// global.URI = URI

function getAutoCompletes(url: string) {
const u = new URI(url)
// const fullDomain = u.domain() // full domain, e.g www.githug.com
// const fullDomain = u.domain() // full domain, e.g www.github.com
// const shortDomain = u.domain(true) // without subdomain, e.g github.com
// const keyword = shortDomain.split('.')[0] // e.g github

Expand All @@ -360,5 +397,6 @@ function getAutoCompletes(url: string) {
return {
'DOMAIN-KEYWORD': keywords,
'DOMAIN-SUFFIX': suffixes,
'DOMAIN': [hostname],
}
}
4 changes: 2 additions & 2 deletions packages/ui/src/page/library-rule-list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,9 @@ export function PartialConfigItem({ item, index }: { item: RuleItem; index: numb
>
<path
fill='#888888'
fill-rule='evenodd'
fillRule='evenodd'
d='M2.5 4.1a.4.4 0 1 0 0 .8h10a.4.4 0 0 0 0-.8h-10Zm0 2a.4.4 0 1 0 0 .8h10a.4.4 0 0 0 0-.8h-10Zm-.4 2.4c0-.22.18-.4.4-.4h10a.4.4 0 0 1 0 .8h-10a.4.4 0 0 1-.4-.4Zm.4 1.6a.4.4 0 0 0 0 .8h10a.4.4 0 0 0 0-.8h-10Z'
clip-rule='evenodd'
clipRule='evenodd'
/>
</svg>
</div>
Expand Down
15 changes: 11 additions & 4 deletions packages/ui/src/util/gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,12 @@ export function getUsingItems() {
return { subscribeItems, ruleItems }
}

export default async function genConfig({ forceUpdate = false }: { forceUpdate?: boolean } = {}) {
export async function genConfig({ forceUpdate = false }: { forceUpdate?: boolean } = {}) {
const { name, clashMeta, generateAllProxyGroup, generateSubNameProxyGroup } =
rootState.currentConfig
const { subscribeItems, ruleItems } = getUsingItems()

/**
* config merge
*/
// the config
let config: Partial<ClashConfig> = {}

// 值为 array 的 key 集合
Expand Down Expand Up @@ -301,6 +299,15 @@ export default async function genConfig({ forceUpdate = false }: { forceUpdate?:
}
/* #endregion */

return config
}

export default async function genConfigThenWrite({
forceUpdate = false,
}: { forceUpdate?: boolean } = {}) {
const config = await genConfig({ forceUpdate })

const { name, clashMeta } = rootState.currentConfig
const configYaml = YAML.dump(config)
const file = getConfigFile(name, clashMeta)

Expand Down

0 comments on commit be21188

Please sign in to comment.