Files
suixinkan_uikit/suixinkan/Features/Profile/ViewModels/ProfileEditViewModel.swift
汉秋 005349f8e6 模块化 AppStore 并完善素材管理与个人空间设置。
将会话、权限、位置、收款与排队存储拆分为独立模块,同步更新各 ViewModel 与单元测试,并补充素材管理 UI 与个人空间设置交互。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-13 11:01:08 +08:00

103 lines
3.5 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.

//
// ProfileEditViewModel.swift
// suixinkan
//
import Foundation
/// ViewModel
final class ProfileEditViewModel {
private let initialNickname: String
private let initialAvatarURL: String
private(set) var editingNickname = ""
private(set) var pendingAvatarData: Data?
private(set) var pendingAvatarFileName: String?
private(set) var isSaving = false
private(set) var avatarUploadProgress: Int?
var onStateChange: (() -> Void)?
/// URL使
var displayAvatarURL: String {
initialAvatarURL
}
/// 使
init(nickname: String, avatarURL: String) {
initialNickname = nickname
initialAvatarURL = avatarURL
editingNickname = nickname == "未设置昵称" ? "" : nickname
}
func updateEditingNickname(_ value: String) {
editingNickname = value
notifyStateChange()
}
func prepareAvatarImage(data: Data, timestamp: TimeInterval = Date().timeIntervalSince1970) throws {
let processed = try AvatarImageProcessor.process(data: data, timestamp: timestamp)
pendingAvatarData = processed.data
pendingAvatarFileName = processed.fileName
notifyStateChange()
}
///
func saveProfile(api: ProfileAPI, uploader: any OSSUploadServing, scenicId: Int) async throws {
let nextNickname = editingNickname.trimmingCharacters(in: .whitespacesAndNewlines)
guard !nextNickname.isEmpty else {
throw ProfileValidationError.emptyNickname
}
let nicknameChanged = nextNickname != initialNickname
let avatarChanged = pendingAvatarData != nil
guard nicknameChanged || avatarChanged else { return }
guard !isSaving else { return }
isSaving = true
avatarUploadProgress = avatarChanged ? 1 : nil
notifyStateChange()
defer {
isSaving = false
avatarUploadProgress = nil
notifyStateChange()
}
let uploadedAvatarURL = try await uploadPendingAvatarIfNeeded(uploader: uploader, scenicId: scenicId)
if let uploadedAvatarURL {
try await api.updateUserAvatarURL(uploadedAvatarURL)
}
if nicknameChanged {
try await api.updateUserInfo(nickname: nextNickname)
}
AppStore.shared.session.userName = nextNickname
if let uploadedAvatarURL {
AppStore.shared.session.avatar = uploadedAvatarURL
}
clearPendingAvatar()
NotificationCenter.default.post(name: NotificationName.userProfileDidUpdate, object: nil)
}
private func uploadPendingAvatarIfNeeded(uploader: any OSSUploadServing, scenicId: Int) async throws -> String? {
guard let pendingAvatarData else { return nil }
let fileName = pendingAvatarFileName ?? "avatar_\(Int(Date().timeIntervalSince1970)).jpg"
return try await uploader.uploadUserAvatar(data: pendingAvatarData, fileName: fileName, scenicId: scenicId) { [weak self] progress in
self?.avatarUploadProgress = progress
self?.notifyStateChange()
}
}
private func clearPendingAvatar() {
pendingAvatarData = nil
pendingAvatarFileName = nil
avatarUploadProgress = nil
}
private func notifyStateChange() {
onStateChange?()
}
}