Files
ShadowsocksX-NG/ShadowsocksX-NG2/App/UI/About/ThirdPartyComponentsView.swift

120 lines
3.6 KiB
Swift

import SwiftUI
struct ThirdPartyComponentsView: View {
static let sceneID = "third-party-components"
@State private var contents: ThirdPartyComponents?
@State private var failed = false
var body: some View {
Group {
if let contents {
ThirdPartyComponentsBrowser(contents: contents)
} else if failed {
ContentUnavailableView("第三方组件信息不可用", systemImage: "exclamationmark.triangle")
} else {
ProgressView()
}
}
.frame(minWidth: 760, minHeight: 460)
.task {
contents = nil
failed = false
do {
contents = try ThirdPartyComponents()
} catch {
failed = true
}
}
}
}
private struct ThirdPartyComponentsBrowser: View {
let contents: ThirdPartyComponents
@State private var selection: String?
var body: some View {
HSplitView {
List(contents.components, selection: $selection) { component in
VStack(alignment: .leading, spacing: 4) {
Text(verbatim: component.metadata.name)
.font(.headline)
Text(verbatim: component.metadata.version)
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
.padding(.vertical, 4)
.tag(component.id)
}
.accessibilityLabel("组件")
.frame(minWidth: 200, idealWidth: 240, maxWidth: 320)
if let component = contents.components.first(where: { $0.id == selection }) {
ThirdPartyComponentDetail(component: component)
.id(component.id)
.frame(minWidth: 440, maxWidth: .infinity, maxHeight: .infinity)
}
}
.onAppear {
if selection == nil { selection = contents.components.first?.id }
}
}
}
private struct ThirdPartyComponentDetail: View {
let component: ThirdPartyComponents.Component
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 24) {
Text(verbatim: component.metadata.name)
.font(.title.bold())
VStack(alignment: .leading, spacing: 12) {
LabeledContent("版本") {
Text(verbatim: component.metadata.version)
}
sourceLink("项目", address: component.metadata.projectURL)
sourceLink("源码来源", address: component.metadata.sourceURL)
sourceLink("分发来源", address: component.metadata.distributionURL)
}
documents("许可证", component.licenses)
documents("附加声明", component.notices)
documents("引用的公共许可证", component.sharedLicenses)
}
.textSelection(.enabled)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(24)
}
}
private func sourceLink(_ title: LocalizedStringKey, address: String) -> some View {
VStack(alignment: .leading, spacing: 4) {
Text(title).foregroundStyle(.secondary)
if let url = URL(string: address) {
Link(destination: url) {
Text(verbatim: address)
.multilineTextAlignment(.leading)
}
}
}
}
@ViewBuilder
private func documents(
_ title: LocalizedStringKey, _ documents: [ThirdPartyComponents.Document]
) -> some View {
if !documents.isEmpty {
VStack(alignment: .leading, spacing: 16) {
Text(title).font(.title2.bold())
ForEach(documents) { document in
VStack(alignment: .leading, spacing: 8) {
Text(verbatim: document.name).font(.headline)
Text(verbatim: document.text)
.font(.system(.body, design: .monospaced))
.fixedSize(horizontal: false, vertical: true)
}
}
}
}
}
}