2024-10-31 22:35:22 +08:00
|
|
|
|
//
|
2024-11-05 20:30:18 +08:00
|
|
|
|
// Adobe Downloader
|
2024-10-31 22:35:22 +08:00
|
|
|
|
//
|
|
|
|
|
|
// 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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-31 22:35:22 +08:00
|
|
|
|
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
|
2024-11-03 00:12:38 +08:00
|
|
|
|
@State private var expandedVersions: Set<String> = []
|
2024-10-31 22:35:22 +08:00
|
|
|
|
|
2025-02-27 23:02:40 +08:00
|
|
|
|
private let product: Product
|
2024-11-07 21:31:29 +08:00
|
|
|
|
private let onSelect: (String) -> Void
|
|
|
|
|
|
|
2025-02-27 23:02:40 +08:00
|
|
|
|
init(product: Product, onSelect: @escaping (String) -> Void) {
|
|
|
|
|
|
self.product = product
|
2024-11-07 21:31:29 +08:00
|
|
|
|
self.onSelect = onSelect
|
|
|
|
|
|
}
|
2024-10-31 22:35:22 +08:00
|
|
|
|
|
|
|
|
|
|
var body: some View {
|
|
|
|
|
|
VStack(spacing: 0) {
|
2025-03-05 21:09:14 +08:00
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-05 21:09:14 +08:00
|
|
|
|
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-04 00:29:08 +08:00
|
|
|
|
}
|
2024-11-03 00:12:38 +08:00
|
|
|
|
}
|
2024-11-15 17:47:15 +08:00
|
|
|
|
.padding(.bottom, VersionPickerConstants.headerPadding)
|
2024-11-04 00:29:08 +08:00
|
|
|
|
|
2024-11-15 17:47:15 +08:00
|
|
|
|
Text("🔔 即将下载 \(downloadAppleSilicon ? "Apple Silicon" : "Intel") (\(platformText)) 版本 🔔")
|
|
|
|
|
|
.font(.caption)
|
|
|
|
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
2024-11-18 22:42:21 +08:00
|
|
|
|
.padding(.bottom, 10)
|
2024-11-15 17:47:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
.padding(.horizontal)
|
|
|
|
|
|
.padding(.top)
|
|
|
|
|
|
.background(Color(NSColor.windowBackgroundColor))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private var platformText: String {
|
2024-11-18 20:33:45 +08:00
|
|
|
|
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-10-31 22:35:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
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(
|
2025-03-05 21:09:14 +08:00
|
|
|
|
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-03 00:12:38 +08:00
|
|
|
|
}
|
2024-11-15 17:47:15 +08:00
|
|
|
|
.buttonStyle(.plain)
|
2024-11-01 14:34:59 +08:00
|
|
|
|
}
|
2024-10-31 22:35:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-15 17:47:15 +08:00
|
|
|
|
private struct VersionInfo: View {
|
|
|
|
|
|
let version: String
|
|
|
|
|
|
let platform: String
|
2024-11-04 00:29:08 +08:00
|
|
|
|
|
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 {
|
2025-01-13 16:40:52 +08:00
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|