2024-10-31 22:35:22 +08:00
|
|
|
import SwiftUI
|
|
|
|
|
|
|
|
|
|
struct ContentView: View {
|
|
|
|
|
@EnvironmentObject private var networkManager: NetworkManager
|
|
|
|
|
@State private var isRefreshing = false
|
|
|
|
|
@State private var errorMessage: String?
|
|
|
|
|
@State private var showDownloadManager = false
|
|
|
|
|
@State private var searchText = ""
|
2024-11-13 13:20:25 +08:00
|
|
|
@AppStorage("apiVersion") private var apiVersion: String = "6"
|
2024-10-31 22:35:22 +08:00
|
|
|
|
2024-11-03 00:12:38 +08:00
|
|
|
private var filteredProducts: [Sap] {
|
|
|
|
|
let products = networkManager.saps.values
|
2024-10-31 22:35:22 +08:00
|
|
|
.filter { !$0.hidden && !$0.versions.isEmpty }
|
|
|
|
|
.sorted { $0.displayName < $1.displayName }
|
|
|
|
|
|
|
|
|
|
if searchText.isEmpty {
|
|
|
|
|
return Array(products)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return products.filter {
|
|
|
|
|
$0.displayName.localizedCaseInsensitiveContains(searchText) ||
|
|
|
|
|
$0.sapCode.localizedCaseInsensitiveContains(searchText)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-06 10:09:16 +08:00
|
|
|
private func openSettings() {
|
|
|
|
|
NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-31 22:35:22 +08:00
|
|
|
var body: some View {
|
|
|
|
|
VStack(spacing: 0) {
|
2024-11-05 20:30:18 +08:00
|
|
|
HStack() {
|
2024-11-01 14:34:59 +08:00
|
|
|
Text("Adobe Downloader")
|
|
|
|
|
.font(.title2)
|
|
|
|
|
.fontWeight(.bold)
|
2024-11-05 20:30:18 +08:00
|
|
|
.fixedSize()
|
|
|
|
|
|
2024-11-06 10:09:16 +08:00
|
|
|
Spacer()
|
2024-11-13 13:20:25 +08:00
|
|
|
|
|
|
|
|
HStack {
|
|
|
|
|
Text("API:")
|
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
|
Picker("", selection: $apiVersion) {
|
|
|
|
|
Text("v4").tag("4")
|
|
|
|
|
Text("v5").tag("5")
|
|
|
|
|
Text("v6").tag("6")
|
|
|
|
|
}
|
|
|
|
|
.pickerStyle(.segmented)
|
|
|
|
|
.frame(width: 150)
|
|
|
|
|
.onChange(of: apiVersion) { newValue in
|
|
|
|
|
refreshData()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.padding(.horizontal, 10)
|
|
|
|
|
|
2024-10-31 22:35:22 +08:00
|
|
|
HStack(spacing: 8) {
|
|
|
|
|
SearchField(text: $searchText)
|
2024-11-06 10:09:16 +08:00
|
|
|
.frame(maxWidth: 200)
|
|
|
|
|
|
2024-11-06 21:57:17 +08:00
|
|
|
if #available(macOS 14.0, *) {
|
|
|
|
|
SettingsLink {
|
|
|
|
|
Image(systemName: "gearshape")
|
|
|
|
|
.imageScale(.medium)
|
|
|
|
|
}
|
|
|
|
|
.buttonStyle(.borderless)
|
|
|
|
|
} else {
|
|
|
|
|
Button(action: openSettings) {
|
|
|
|
|
Image(systemName: "gearshape")
|
|
|
|
|
.imageScale(.medium)
|
|
|
|
|
}
|
|
|
|
|
.buttonStyle(.borderless)
|
2024-11-06 10:09:16 +08:00
|
|
|
}
|
2024-10-31 22:35:22 +08:00
|
|
|
|
|
|
|
|
Button(action: refreshData) {
|
|
|
|
|
Image(systemName: "arrow.clockwise")
|
2024-11-01 14:34:59 +08:00
|
|
|
.imageScale(.medium)
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|
|
|
|
|
.disabled(isRefreshing)
|
|
|
|
|
.buttonStyle(.borderless)
|
|
|
|
|
|
|
|
|
|
Button(action: { showDownloadManager.toggle() }) {
|
|
|
|
|
Image(systemName: "arrow.down.circle")
|
2024-11-01 14:34:59 +08:00
|
|
|
.imageScale(.medium)
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|
2024-11-09 23:15:50 +08:00
|
|
|
.disabled(isRefreshing)
|
2024-10-31 22:35:22 +08:00
|
|
|
.buttonStyle(.borderless)
|
|
|
|
|
.overlay(
|
|
|
|
|
Group {
|
|
|
|
|
if !networkManager.downloadTasks.isEmpty {
|
|
|
|
|
Text("\(networkManager.downloadTasks.count)")
|
|
|
|
|
.font(.caption2)
|
2024-11-01 14:34:59 +08:00
|
|
|
.padding(3)
|
2024-10-31 22:35:22 +08:00
|
|
|
.background(Color.blue)
|
|
|
|
|
.clipShape(Circle())
|
|
|
|
|
.foregroundColor(.white)
|
2024-11-01 14:34:59 +08:00
|
|
|
.offset(x: 8, y: -8)
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-06 10:09:16 +08:00
|
|
|
.padding(.horizontal)
|
|
|
|
|
.padding(.vertical, 8)
|
2024-10-31 22:35:22 +08:00
|
|
|
.background(Color(NSColor.windowBackgroundColor))
|
|
|
|
|
|
2024-11-11 19:16:56 +08:00
|
|
|
HStack() {
|
|
|
|
|
Image(systemName: "exclamationmark.triangle.fill")
|
|
|
|
|
.foregroundColor(.orange)
|
|
|
|
|
Text("Adobe Downloader 完全开源免费: https://github.com/X1a0He/Adobe-Downloader")
|
|
|
|
|
}
|
|
|
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
|
|
|
.padding(.horizontal)
|
|
|
|
|
.padding(.bottom, 5)
|
|
|
|
|
.background(Color(NSColor.windowBackgroundColor))
|
|
|
|
|
|
2024-10-31 22:35:22 +08:00
|
|
|
ZStack {
|
|
|
|
|
Color(NSColor.windowBackgroundColor)
|
|
|
|
|
.ignoresSafeArea()
|
|
|
|
|
|
|
|
|
|
switch networkManager.loadingState {
|
|
|
|
|
case .idle, .loading:
|
|
|
|
|
ProgressView("正在加载...")
|
|
|
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
|
|
|
|
|
|
|
|
case .failed(let error):
|
|
|
|
|
VStack(spacing: 20) {
|
|
|
|
|
Image(systemName: "exclamationmark.triangle")
|
|
|
|
|
.font(.system(size: 48))
|
|
|
|
|
.foregroundColor(.red)
|
|
|
|
|
|
|
|
|
|
Text("加载失败")
|
|
|
|
|
.font(.title2)
|
|
|
|
|
.fontWeight(.medium)
|
|
|
|
|
|
|
|
|
|
Text(error.localizedDescription)
|
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
|
.multilineTextAlignment(.center)
|
|
|
|
|
.frame(maxWidth: 300)
|
|
|
|
|
.padding(.bottom, 10)
|
|
|
|
|
|
|
|
|
|
Button(action: {
|
|
|
|
|
networkManager.retryFetchData()
|
|
|
|
|
}) {
|
2024-11-05 20:30:18 +08:00
|
|
|
HStack() {
|
2024-10-31 22:35:22 +08:00
|
|
|
Image(systemName: "arrow.clockwise")
|
|
|
|
|
Text("重试")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.buttonStyle(.borderedProminent)
|
|
|
|
|
.controlSize(.large)
|
|
|
|
|
}
|
|
|
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
|
|
|
|
|
|
|
|
case .success:
|
|
|
|
|
if filteredProducts.isEmpty {
|
2024-11-06 21:57:17 +08:00
|
|
|
VStack {
|
|
|
|
|
Image(systemName: "magnifyingglass")
|
|
|
|
|
.font(.system(size: 36))
|
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
|
Text("没有找到产品")
|
|
|
|
|
.font(.headline)
|
|
|
|
|
.padding(.top)
|
|
|
|
|
Text("尝试使用不同的搜索关键词")
|
|
|
|
|
.font(.subheadline)
|
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
|
}
|
|
|
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
2024-10-31 22:35:22 +08:00
|
|
|
} else {
|
2024-11-04 17:40:01 +08:00
|
|
|
ScrollView(showsIndicators: false) {
|
2024-10-31 22:35:22 +08:00
|
|
|
LazyVGrid(
|
|
|
|
|
columns: [GridItem(.adaptive(minimum: 250))],
|
|
|
|
|
spacing: 20
|
|
|
|
|
) {
|
2024-11-03 00:12:38 +08:00
|
|
|
ForEach(filteredProducts, id: \.sapCode) { sap in
|
|
|
|
|
AppCardView(sap: sap)
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.padding()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.sheet(isPresented: $showDownloadManager) {
|
|
|
|
|
DownloadManagerView()
|
|
|
|
|
.environmentObject(networkManager)
|
|
|
|
|
}
|
|
|
|
|
.onAppear {
|
2024-11-03 00:12:38 +08:00
|
|
|
if networkManager.saps.isEmpty {
|
2024-10-31 22:35:22 +08:00
|
|
|
refreshData()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func refreshData() {
|
|
|
|
|
isRefreshing = true
|
|
|
|
|
errorMessage = nil
|
|
|
|
|
|
|
|
|
|
Task {
|
|
|
|
|
await networkManager.fetchProducts()
|
|
|
|
|
await MainActor.run {
|
|
|
|
|
isRefreshing = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct SearchField: View {
|
|
|
|
|
@Binding var text: String
|
|
|
|
|
|
|
|
|
|
var body: some View {
|
|
|
|
|
HStack {
|
|
|
|
|
Image(systemName: "magnifyingglass")
|
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
|
TextField("搜索应用", text: $text)
|
|
|
|
|
.textFieldStyle(PlainTextFieldStyle())
|
|
|
|
|
if !text.isEmpty {
|
|
|
|
|
Button(action: { text = "" }) {
|
|
|
|
|
Image(systemName: "xmark.circle.fill")
|
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
|
}
|
|
|
|
|
.buttonStyle(PlainButtonStyle())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.padding(8)
|
|
|
|
|
.background(Color(NSColor.controlBackgroundColor))
|
|
|
|
|
.cornerRadius(8)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#Preview {
|
|
|
|
|
let networkManager = NetworkManager()
|
|
|
|
|
|
|
|
|
|
return ContentView()
|
|
|
|
|
.environmentObject(networkManager)
|
|
|
|
|
.frame(width: 850, height: 700)
|
|
|
|
|
}
|
|
|
|
|
|