mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-10-28 13:13:47 +00:00
feat(ui): 边栏菜单改「导入」,添加订阅移回订阅页工具栏
- 边栏「添加」菜单改名「导入」,图标换 square.and.arrow.down,与 分享按钮 square.and.arrow.up 成对;菜单收敛为三项导入动作 - 「添加订阅」移回订阅分区自挂工具栏,表单由订阅页自持;窗口壳删 .addSubscription workspace sheet 与 destination 映射 - 空态文案同步:订阅页指向本页按钮,服务器页改称「导入」菜单并删 去从未存在的「创建分组」表述
This commit is contained in:
@@ -1,21 +1,13 @@
|
||||
import SwiftUI
|
||||
|
||||
/// 服务器导入表单清单:由边栏「导入」菜单打开,全部属服务器域,窗口壳统一
|
||||
/// 持有呈现;添加订阅表单由订阅分区自持(SubscriptionsView)。
|
||||
private enum WorkspaceSheet: String, Identifiable {
|
||||
case importURL
|
||||
case qrImport
|
||||
case legacyImport
|
||||
case addSubscription
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
var destination: WorkspaceDestination {
|
||||
switch self {
|
||||
case .importURL, .qrImport, .legacyImport:
|
||||
.servers
|
||||
case .addSubscription:
|
||||
.subscriptions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 主窗口外壳(地图 #52,票 #53):NavigationSplitView 侧栏承载五项导航与
|
||||
@@ -53,11 +45,11 @@ struct MainWindowView: View {
|
||||
/// selection(选择再同步,且可能携带缓存的旧值),回写必须落在 SwiftUI 自管
|
||||
/// 的 @State 上,再经 onChange 单向驱动 route;直接 publish 到 route 会触发
|
||||
/// view-update 期间发布警告,且缓存的旧值会把外部导航弹回。route 的外部
|
||||
/// 导航(添加菜单、规则编辑器返回等)经 sidebar 上的 onChange 回填选中项。
|
||||
/// 导航(导入菜单、规则编辑器返回等)经 sidebar 上的 onChange 回填选中项。
|
||||
/// 初值须与 WorkspaceRoute 的初始 destination 对齐,否则首帧侧栏无高亮行。
|
||||
@State private var sidebarSelection: WorkspaceDestination? = WorkspaceRoute.initialDestination
|
||||
@State private var selection: NodeID?
|
||||
/// 全局添加菜单打开的表单由窗口壳持有。
|
||||
/// 全局导入菜单打开的表单由窗口壳持有。
|
||||
@State private var presentedWorkspaceSheet: WorkspaceSheet?
|
||||
@StateObject private var shellActionErrors = ErrorAlertPresenter()
|
||||
|
||||
@@ -88,14 +80,12 @@ struct MainWindowView: View {
|
||||
QRImportSheet(workflow: workflow, errors: shellActionErrors, selection: $selection)
|
||||
case .legacyImport:
|
||||
LegacyImportSheet(workflow: workflow)
|
||||
case .addSubscription:
|
||||
AddSubscriptionSheet(workflow: workflow, errors: shellActionErrors)
|
||||
}
|
||||
}
|
||||
.presentingErrors(shellActionErrors)
|
||||
}
|
||||
|
||||
// MARK: - 全局添加菜单
|
||||
// MARK: - 全局导入菜单
|
||||
|
||||
private var addMenu: some View {
|
||||
Menu {
|
||||
@@ -104,13 +94,11 @@ struct MainWindowView: View {
|
||||
if workflow.legacyImportState.snapshotFound {
|
||||
Button(legacyImportActionTitle) { presentWorkspaceSheet(.legacyImport) }
|
||||
}
|
||||
Divider()
|
||||
Button("添加订阅…") { presentWorkspaceSheet(.addSubscription) }
|
||||
} label: {
|
||||
Label("添加", systemImage: "plus")
|
||||
Label("导入", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.labelStyle(.iconOnly)
|
||||
.help("添加")
|
||||
.help("导入")
|
||||
}
|
||||
|
||||
private var legacyImportActionTitle: String {
|
||||
@@ -118,7 +106,7 @@ struct MainWindowView: View {
|
||||
}
|
||||
|
||||
private func presentWorkspaceSheet(_ sheet: WorkspaceSheet) {
|
||||
route.navigate(to: sheet.destination)
|
||||
route.navigate(to: .servers)
|
||||
presentedWorkspaceSheet = sheet
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ struct ServersView: View {
|
||||
if workflow.tree.isEmpty {
|
||||
ContentUnavailableView(
|
||||
"暂无服务器", systemImage: "server.rack",
|
||||
description: Text("用工具栏的「新建服务器」按钮手动录入,或用「添加」菜单导入 ss:// 链接、创建分组")
|
||||
description: Text("用工具栏的「新建服务器」按钮手动录入,或用「导入」菜单导入 ss:// 链接")
|
||||
)
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@ import SwiftUI
|
||||
|
||||
/// 订阅分区(spec #21 D11,issue #35/#41,地图 #52 票 #56):每订阅一张卡片
|
||||
/// (图标 + 名称 + 「HTTPS 订阅地址已隐藏」+ 状态徽标 + 更新/重试 + 「…」菜单 +
|
||||
/// 三列元数据 + 脱敏说明行),失败卡警告色描边。添加订阅通过窗口壳侧栏的
|
||||
/// 「添加」菜单进入;「更新全部」由本页自挂工具栏(呈现于窗口工具栏右端,
|
||||
/// 与服务器分区同法)。卡片数据来自订阅 projection(非敏感);结构化刷新
|
||||
/// 状态在呈现层本地化(story 42)。
|
||||
/// 三列元数据 + 脱敏说明行),失败卡警告色描边。「添加订阅」与「更新全部」
|
||||
/// 均由本页自挂工具栏(呈现于窗口工具栏右端,与服务器分区同法)。卡片数据
|
||||
/// 来自订阅 projection(非敏感);结构化刷新状态在呈现层本地化(story 42)。
|
||||
struct SubscriptionsView: View {
|
||||
@ObservedObject var workflow: CatalogWorkflow
|
||||
/// 共享错误弹窗呈现(UI 持有;typed error → 本地化文案的呈现边缘)。
|
||||
@@ -15,6 +14,8 @@ struct SubscriptionsView: View {
|
||||
|
||||
@State private var editTarget: SubscriptionSummary?
|
||||
@State private var deleteTarget: SubscriptionSummary?
|
||||
/// 添加订阅表单:本页自持(2026-10-04 自窗口壳「添加」菜单移回本页工具栏)。
|
||||
@State private var isPresentingAddSubscription = false
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
@@ -40,7 +41,7 @@ struct SubscriptionsView: View {
|
||||
if workflow.subscriptions.isEmpty {
|
||||
ContentUnavailableView(
|
||||
"暂无订阅", systemImage: "arrow.triangle.2.circlepath",
|
||||
description: Text("用窗口左上方的「添加」菜单添加 HTTPS 订阅地址(SIP-008 JSON)")
|
||||
description: Text("用工具栏的「添加订阅」按钮添加 HTTPS 订阅地址(SIP-008 JSON)")
|
||||
)
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
@@ -48,6 +49,9 @@ struct SubscriptionsView: View {
|
||||
.sheet(item: $editTarget) { summary in
|
||||
EditSubscriptionURLSheet(workflow: workflow, errors: errors, summary: summary)
|
||||
}
|
||||
.sheet(isPresented: $isPresentingAddSubscription) {
|
||||
AddSubscriptionSheet(workflow: workflow, errors: errors)
|
||||
}
|
||||
.confirmationDialog(
|
||||
"删除订阅「\(deleteTarget?.name ?? "")」及其整棵子树?",
|
||||
isPresented: Binding(
|
||||
@@ -64,6 +68,11 @@ struct SubscriptionsView: View {
|
||||
}
|
||||
.presentingErrors(errors)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button("添加订阅", systemImage: "plus") {
|
||||
isPresentingAddSubscription = true
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
HStack(spacing: 8) {
|
||||
if !workflow.refreshingSubscriptionIDs.isEmpty {
|
||||
|
||||
Reference in New Issue
Block a user