订阅列表可以调顺序
每行末尾两个箭头,鼠标划过那一行才显现。proxy-providers 是映射,顺序只 影响界面呈现,内核不看;交换的是两个用户订阅在映射里的位置,中间夹着的 链式副本留在原地——它们不在列表上,跟着挪只会把 diff 撑大。 顺序写在配置文件里,所以走保存链路,不是运行时动作。
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
addFileSubscription,
|
||||
addSubscription,
|
||||
listSubscriptions,
|
||||
moveSubscription,
|
||||
providerNames,
|
||||
removeSubscription,
|
||||
renameSubscription,
|
||||
@@ -280,6 +281,48 @@ describe('删除前的引用检查', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('调整订阅顺序', () => {
|
||||
/** 三个用户订阅,外加一份接在中间的订阅副本。 */
|
||||
function three(): ConfigObject {
|
||||
let data: ConfigObject = {}
|
||||
for (const name of ['A', 'B', 'C']) data = addSubscription(data, name, `https://${name}.example`)
|
||||
return data
|
||||
}
|
||||
|
||||
it('上移和下移交换相邻两个订阅', () => {
|
||||
expect(listSubscriptions(moveSubscription(three(), 'B', -1)).map((s) => s.name)).toEqual([
|
||||
'B',
|
||||
'A',
|
||||
'C',
|
||||
])
|
||||
expect(listSubscriptions(moveSubscription(three(), 'B', 1)).map((s) => s.name)).toEqual([
|
||||
'A',
|
||||
'C',
|
||||
'B',
|
||||
])
|
||||
})
|
||||
|
||||
it('到头了原样返回,配置对象不变', () => {
|
||||
const data = three()
|
||||
expect(moveSubscription(data, 'A', -1)).toBe(data)
|
||||
expect(moveSubscription(data, 'C', 1)).toBe(data)
|
||||
expect(moveSubscription(data, '不存在', 1)).toBe(data)
|
||||
})
|
||||
|
||||
it('夹在中间的订阅副本留在原地', () => {
|
||||
const withCopy = applySetup(
|
||||
{ ...three(), listeners: [{ name: 'test1', type: 'mixed', port: 7778 }] },
|
||||
'test1',
|
||||
{ proxy: { sources: ['B'], filter: null }, chain: { sources: ['B'], filter: null } },
|
||||
)
|
||||
const copy = chainProviderName('test1', 'B')
|
||||
const before = Object.keys(withCopy['proxy-providers'] as ConfigObject).indexOf(copy)
|
||||
const after = moveSubscription(withCopy, 'A', 1)
|
||||
expect(listSubscriptions(after).map((s) => s.name)).toEqual(['B', 'A', 'C'])
|
||||
expect(Object.keys(after['proxy-providers'] as ConfigObject).indexOf(copy)).toBe(before)
|
||||
})
|
||||
})
|
||||
|
||||
describe('缓存路径撞名', () => {
|
||||
it('名字只差符号时净化成同一个路径,能认出来', () => {
|
||||
const data = addSubscription({}, '订阅 A', 'https://a.example/sub')
|
||||
|
||||
@@ -238,6 +238,36 @@ export function renameSubscription(
|
||||
return next
|
||||
}
|
||||
|
||||
/**
|
||||
* 把一个订阅在列表里上移或下移一位。
|
||||
*
|
||||
* 顺序只是呈现顺序,内核不看 `proxy-providers` 里条目的先后。交换的是两个用户订阅在
|
||||
* 映射里的位置,中间夹着的副本留在原地——它们不在列表上,跟着挪只会把 diff 撑大。
|
||||
* 已经在头(尾)还要上(下)移时原样返回,调用方据此不发起保存。
|
||||
*/
|
||||
export function moveSubscription(
|
||||
data: ConfigObject,
|
||||
name: string,
|
||||
delta: -1 | 1,
|
||||
): ConfigObject {
|
||||
const entries = Object.entries(providersOf(data))
|
||||
const keys = entries.map(([key]) => key)
|
||||
const mine = keys.filter((key) => !isGenerated(key))
|
||||
const at = mine.indexOf(name)
|
||||
const target = mine[at + delta]
|
||||
if (at < 0 || target === undefined) return data
|
||||
|
||||
const from = keys.indexOf(name)
|
||||
const onto = keys.indexOf(target)
|
||||
const order = [...entries]
|
||||
order[from] = entries[onto]!
|
||||
order[onto] = entries[from]!
|
||||
|
||||
const next = { ...data }
|
||||
next['proxy-providers'] = Object.fromEntries(order)
|
||||
return next
|
||||
}
|
||||
|
||||
/** 删除一个订阅。删到最后一个时把整个键去掉,不在文件里留空映射。 */
|
||||
export function removeSubscription(data: ConfigObject, name: string): ConfigObject {
|
||||
const providers = { ...providersOf(data) }
|
||||
|
||||
Reference in New Issue
Block a user