Files
Adobe-Downloader/Adobe Downloader/Views/VersionPickerView.swift

333 lines
9.8 KiB
Swift
Raw Normal View History

//
// Adobe Downloader
//
// Created by X1a0He on 2024/10/30.
//
import SwiftUI
2024-11-15 17:47:15 +08:00
private enum VersionPickerConstants {
static let headerPadding: CGFloat = 5
static let viewWidth: CGFloat = 400
static let viewHeight: CGFloat = 500
static let iconSize: CGFloat = 32
static let verticalSpacing: CGFloat = 8
static let horizontalSpacing: CGFloat = 12
static let cornerRadius: CGFloat = 8
static let buttonPadding: CGFloat = 8
static let titleFontSize: CGFloat = 14
static let captionFontSize: CGFloat = 12
}
struct VersionPickerView: View {
@Environment(\.dismiss) private var dismiss
2024-11-15 17:47:15 +08:00
@StorageValue(\.defaultLanguage) private var defaultLanguage
@StorageValue(\.downloadAppleSilicon) private var downloadAppleSilicon
@State private var expandedVersions: Set<String> = []
2025-02-27 23:02:40 +08:00
private let product: Product
private let onSelect: (String) -> Void
2025-02-27 23:02:40 +08:00
init(product: Product, onSelect: @escaping (String) -> Void) {
self.product = product
self.onSelect = onSelect
}
var body: some View {
VStack(spacing: 0) {
VersionPickerHeaderView(product: product, downloadAppleSilicon: downloadAppleSilicon)
2024-11-15 17:47:15 +08:00
VersionListView(
2025-02-27 23:02:40 +08:00
product: product,
2024-11-15 17:47:15 +08:00
expandedVersions: $expandedVersions,
onSelect: onSelect,
dismiss: dismiss
)
}
.frame(width: VersionPickerConstants.viewWidth, height: VersionPickerConstants.viewHeight)
}
}
private struct VersionPickerHeaderView: View {
2025-02-27 23:02:40 +08:00
let product: Product
2024-11-15 17:47:15 +08:00
let downloadAppleSilicon: Bool
@Environment(\.dismiss) private var dismiss
@EnvironmentObject private var networkManager: NetworkManager
var body: some View {
VStack {
HStack {
2025-02-27 23:02:40 +08:00
Text("\(product.displayName)")
2024-11-15 17:47:15 +08:00
.font(.headline)
Text("选择版本")
.foregroundColor(.secondary)
Spacer()
Button("取消") {
dismiss()
}
}
2024-11-15 17:47:15 +08:00
.padding(.bottom, VersionPickerConstants.headerPadding)
2024-11-15 17:47:15 +08:00
Text("🔔 即将下载 \(downloadAppleSilicon ? "Apple Silicon" : "Intel") (\(platformText)) 版本 🔔")
.font(.caption)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.bottom, 10)
2024-11-15 17:47:15 +08:00
}
.padding(.horizontal)
.padding(.top)
.background(Color(NSColor.windowBackgroundColor))
}
private var platformText: String {
StorageData.shared.allowedPlatform.joined(separator: ", ")
2024-11-15 17:47:15 +08:00
}
}
private struct VersionListView: View {
@EnvironmentObject private var networkManager: NetworkManager
2025-02-27 23:02:40 +08:00
let product: Product
2024-11-15 17:47:15 +08:00
@Binding var expandedVersions: Set<String>
let onSelect: (String) -> Void
let dismiss: DismissAction
var body: some View {
ScrollView(showsIndicators: false) {
LazyVStack(spacing: VersionPickerConstants.verticalSpacing) {
ForEach(filteredVersions, id: \.key) { version, info in
VersionRow(
2025-02-27 23:02:40 +08:00
product: product,
2024-11-15 17:47:15 +08:00
version: version,
info: info,
isExpanded: expandedVersions.contains(version),
onSelect: handleVersionSelect,
onToggle: handleVersionToggle
)
}
}
2024-11-15 17:47:15 +08:00
.padding()
}
.background(Color(NSColor.windowBackgroundColor))
}
2025-02-27 23:02:40 +08:00
private var filteredVersions: [(key: String, value: Product.Platform)] {
//
let platforms = product.platforms.filter { platform in
StorageData.shared.allowedPlatform.contains(platform.id) &&
platform.languageSet.first != nil
}
//
if platforms.isEmpty {
return []
}
//
return platforms.map { platform in
// 使 productVersion
(key: platform.languageSet.first?.productVersion ?? "", value: platform)
}.sorted { pair1, pair2 in
//
AppStatics.compareVersions(pair1.key, pair2.key) > 0
}
2024-11-15 17:47:15 +08:00
}
private func handleVersionSelect(_ version: String) {
onSelect(version)
dismiss()
}
private func handleVersionToggle(_ version: String) {
withAnimation {
if expandedVersions.contains(version) {
expandedVersions.remove(version)
} else {
expandedVersions.insert(version)
}
}
}
}
private struct VersionRow: View {
@StorageValue(\.defaultLanguage) private var defaultLanguage
2025-02-27 23:02:40 +08:00
let product: Product
2024-11-15 17:47:15 +08:00
let version: String
2025-02-27 23:02:40 +08:00
let info: Product.Platform
2024-11-15 17:47:15 +08:00
let isExpanded: Bool
let onSelect: (String) -> Void
let onToggle: (String) -> Void
private var existingPath: URL? {
2025-02-27 23:02:40 +08:00
globalNetworkManager.isVersionDownloaded(
productId: product.id,
2024-11-15 17:47:15 +08:00
version: version,
language: defaultLanguage
)
}
var body: some View {
VStack(spacing: 0) {
VersionHeader(
version: version,
info: info,
isExpanded: isExpanded,
hasExistingPath: existingPath != nil,
onSelect: handleSelect,
onToggle: { onToggle(version) }
)
if isExpanded {
VersionDetails(
info: info,
version: version,
onSelect: onSelect
)
}
}
.padding(.horizontal)
.background(Color(NSColor.controlBackgroundColor))
.cornerRadius(VersionPickerConstants.cornerRadius)
}
private func handleSelect() {
2025-02-27 23:02:40 +08:00
let dependencies = info.languageSet.first?.dependencies ?? []
if dependencies.isEmpty {
2024-11-15 17:47:15 +08:00
onSelect(version)
} else {
onToggle(version)
}
}
}
private struct VersionHeader: View {
let version: String
2025-02-27 23:02:40 +08:00
let info: Product.Platform
2024-11-15 17:47:15 +08:00
let isExpanded: Bool
let hasExistingPath: Bool
let onSelect: () -> Void
let onToggle: () -> Void
var body: some View {
Button(action: onSelect) {
HStack {
2025-02-27 23:02:40 +08:00
VersionInfo(version: version, platform: info.id)
2024-11-15 17:47:15 +08:00
Spacer()
ExistingPathButton(isVisible: hasExistingPath)
ExpandButton(
isExpanded: isExpanded,
2025-02-27 23:02:40 +08:00
onToggle: onToggle,
hasDependencies: !(info.languageSet.first?.dependencies.isEmpty ?? true)
2024-11-15 17:47:15 +08:00
)
}
.padding(.vertical, VersionPickerConstants.buttonPadding)
.contentShape(Rectangle())
}
2024-11-15 17:47:15 +08:00
.buttonStyle(.plain)
}
}
2024-11-15 17:47:15 +08:00
private struct VersionInfo: View {
let version: String
let platform: String
2024-11-15 17:47:15 +08:00
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Text(version)
.font(.headline)
Text(platform)
.font(.caption)
.foregroundColor(.secondary)
}
}
}
private struct ExistingPathButton: View {
let isVisible: Bool
var body: some View {
if isVisible {
Text("可能已存在目录")
2024-11-15 17:47:15 +08:00
.font(.caption)
.foregroundColor(.white)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(Color.blue)
.cornerRadius(4)
}
}
}
private struct ExpandButton: View {
let isExpanded: Bool
2025-02-27 23:02:40 +08:00
let onToggle: () -> Void
2024-11-15 17:47:15 +08:00
let hasDependencies: Bool
var body: some View {
2025-02-27 23:02:40 +08:00
Button(action: onToggle) {
Image(systemName: iconName)
.foregroundColor(.secondary)
}
2024-11-15 17:47:15 +08:00
}
private var iconName: String {
if !hasDependencies {
return "chevron.right"
}
return isExpanded ? "chevron.down" : "chevron.right"
}
}
private struct VersionDetails: View {
2025-02-27 23:02:40 +08:00
let info: Product.Platform
2024-11-15 17:47:15 +08:00
let version: String
let onSelect: (String) -> Void
var body: some View {
VStack(alignment: .leading, spacing: VersionPickerConstants.verticalSpacing) {
Text("依赖包:")
.font(.caption)
.foregroundColor(.secondary)
.padding(.top, 8)
.padding(.leading, 16)
2025-02-27 23:02:40 +08:00
DependenciesList(dependencies: info.languageSet.first?.dependencies ?? [])
2024-11-15 17:47:15 +08:00
DownloadButton(version: version, onSelect: onSelect)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.bottom, 8)
}
}
private struct DependenciesList: View {
2025-02-27 23:02:40 +08:00
let dependencies: [Product.Platform.LanguageSet.Dependency]
2024-11-15 17:47:15 +08:00
var body: some View {
ForEach(dependencies, id: \.sapCode) { dependency in
HStack(spacing: 8) {
Image(systemName: "cube.box")
.foregroundColor(.blue)
.frame(width: 16)
2025-02-27 23:02:40 +08:00
Text("\(dependency.sapCode) (\(dependency.baseVersion))")
2024-11-15 17:47:15 +08:00
.font(.caption)
Spacer()
}
.padding(.leading, 24)
}
}
}
private struct DownloadButton: View {
let version: String
let onSelect: (String) -> Void
var body: some View {
Button("下载此版本") {
onSelect(version)
}
.buttonStyle(.borderedProminent)
.padding(.top, 8)
.padding(.leading, 16)
}
}