Files
JetsonMediaIOS/Jetson Media/ui/ProfileView.swift
2025-08-17 22:08:25 +08:00

292 lines
10 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
import Foundation
// UserDefaults
extension UserDefaults {
// [album_id: 访]
private var historyRecordsKey: String { "history_records" }
var historyRecords: [String: TimeInterval] {
get {
(object(forKey: historyRecordsKey) as? [String: TimeInterval]) ?? [:]
}
set {
set(newValue, forKey: historyRecordsKey)
}
}
//
func addHistory(albumId: String) {
var records = historyRecords
records[albumId] = Date().timeIntervalSince1970 //
historyRecords = records
}
//
func removeHistory(albumId: String) {
var records = historyRecords
records.removeValue(forKey: albumId)
historyRecords = records
}
//
func clearAllHistory() {
historyRecords = [:]
}
}
struct ProfileView: View {
//
let username = "Jetson User"
//
@State private var favoriteAlbums: [AlbumItem] = []
@State private var isLoadingFavorites = false
//
@State private var historyAlbums: [AlbumItem] = []
@State private var isLoadingHistory = false
@State private var showHistoryEmpty = false
//
@State private var showError = false
@State private var errorMessage = ""
var body: some View {
NavigationStack {
List {
//
Section {
HStack(alignment: .center, spacing: 12) {
//
Image(systemName: "person.circle.fill")
.resizable()
.frame(width: 80, height: 80)
.foregroundColor(.blue)
//
VStack(spacing: 4) {
Text(username)
.font(.headline)
}
}
.padding(.vertical, 20)
}
//
Section(header: Text("我的收藏")
.font(.headline)) {
if isLoadingFavorites {
HStack {
Spacer()
ProgressView("加载收藏中...")
Spacer()
}
.padding(.vertical, 20)
} else if favoriteAlbums.isEmpty {
Text("暂无收藏,快去收藏喜欢的漫画吧~")
.foregroundColor(.gray)
.padding(.vertical, 20)
} else {
ForEach(favoriteAlbums) { item in
NavigationLink(destination: PhotoView(albumId: item.album_id)) {
VStack(alignment: .leading, spacing: 4) {
Text(item.title)
.font(.subheadline)
.lineLimit(2)
Text("ID: \(item.album_id)")
.font(.caption)
.foregroundColor(.gray)
}
}
}
.onDelete(perform: removeFromFavorites)
}
}
//
Section(header: HStack {
Text("浏览历史")
.font(.headline)
Spacer()
Button("清除全部") {
clearAllHistory()
}
.foregroundColor(.red)
.font(.subheadline)
}) {
if isLoadingHistory {
HStack {
Spacer()
ProgressView("加载历史中...")
Spacer()
}
.padding(.vertical, 20)
} else if historyAlbums.isEmpty {
Text("暂无浏览记录")
.foregroundColor(.gray)
.padding(.vertical, 20)
} else {
ForEach(historyAlbums) { item in
NavigationLink(destination: PhotoView(albumId: item.album_id)) {
VStack(alignment: .leading, spacing: 4) {
Text(item.title)
.font(.subheadline)
.lineLimit(2)
Text("ID: \(item.album_id)")
.font(.caption)
.foregroundColor(.gray)
}
}
}
.onDelete(perform: removeFromHistory)
}
}
//
Section(header: Text("设置")) {
Button(action: {}) {
HStack {
Image(systemName: "gear")
.foregroundColor(.gray)
Text("设置")
}
}
}
}
.navigationTitle("我的")
.toolbar {
EditButton()
}
.alert(isPresented: $showError) {
Alert(title: Text("加载失败"), message: Text(errorMessage), dismissButton: .default(Text("确定")))
}
.onAppear {
loadFavoriteAlbums()
loadHistoryAlbums() //
}
}
}
// MARK: -
private func loadFavoriteAlbums() {
let favoriteIds = UserDefaults.standard.favoriteAlbumIds
guard !favoriteIds.isEmpty else {
favoriteAlbums = []
return
}
isLoadingFavorites = true
favoriteAlbums = []
let group = DispatchGroup()
for id in favoriteIds {
group.enter()
fetchAlbumInfo(albumId: id) { albumItem in
if let item = albumItem {
favoriteAlbums.append(item)
}
group.leave()
}
}
group.notify(queue: .main) {
favoriteAlbums.sort { $0.album_id > $1.album_id }
isLoadingFavorites = false
}
}
private func removeFromFavorites(at offsets: IndexSet) {
guard let index = offsets.first else { return }
let removedItem = favoriteAlbums[index]
favoriteAlbums.remove(at: index)
var favorites = UserDefaults.standard.favoriteAlbumIds
favorites.removeAll { $0 == removedItem.album_id }
UserDefaults.standard.favoriteAlbumIds = favorites
}
// MARK: -
private func loadHistoryAlbums() {
let historyRecords = UserDefaults.standard.historyRecords
//
let sortedIds = historyRecords.keys.sorted {
historyRecords[$0] ?? 0 > historyRecords[$1] ?? 0
}
guard !sortedIds.isEmpty else {
historyAlbums = []
return
}
isLoadingHistory = true
historyAlbums = []
let group = DispatchGroup()
for id in sortedIds {
group.enter()
fetchAlbumInfo(albumId: id) { albumItem in
if let item = albumItem {
historyAlbums.append(item)
}
group.leave()
}
}
group.notify(queue: .main) {
isLoadingHistory = false
}
}
private func removeFromHistory(at offsets: IndexSet) {
guard let index = offsets.first else { return }
let removedItem = historyAlbums[index]
historyAlbums.remove(at: index)
UserDefaults.standard.removeHistory(albumId: removedItem.album_id)
}
private func clearAllHistory() {
UserDefaults.standard.clearAllHistory()
historyAlbums = []
}
// MARK: -
private func fetchAlbumInfo(albumId: String, completion: @escaping (AlbumItem?) -> Void) {
let urlString = "http://jm.rbq.college/album/\(albumId)/"
guard let url = URL(string: urlString) else {
completion(nil)
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
DispatchQueue.main.async {
showError(message: "加载失败: \(error.localizedDescription)")
}
completion(nil)
return
}
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200,
let data = data else {
completion(nil)
return
}
do {
let album = try JSONDecoder().decode(Album.self, from: data)
let albumItem = AlbumItem(album_id: album.album_id, title: album.name)
completion(albumItem)
} catch {
print("解析数据失败: \(error)")
completion(nil)
}
}.resume()
}
private func showError(message: String) {
errorMessage = message
showError = true
}
}