Add OSS upload and Kingfisher image support

This commit is contained in:
2026-06-22 16:30:05 +08:00
parent 484566d27e
commit 0b83a73509
20 changed files with 1399 additions and 134 deletions

View File

@ -18,6 +18,9 @@ final class ProfileViewModel {
var isSaving = false
var isEditingProfile = false
var editingNickname = ""
private(set) var pendingAvatarData: Data?
private(set) var pendingAvatarFileName: String?
private(set) var avatarUploadProgress: Int?
var displayNickname: String {
nonEmpty(userInfo?.nickname) ?? "未设置昵称"
@ -75,29 +78,51 @@ final class ProfileViewModel {
func cancelEditing() {
editingNickname = ""
isEditingProfile = false
clearPendingAvatar()
}
/// userInfo
func saveProfile(api: ProfileAPI) async throws {
///
func prepareAvatarImage(data: Data, timestamp: TimeInterval = Date().timeIntervalSince1970) throws {
let processed = try AvatarImageProcessor.process(data: data, timestamp: timestamp)
pendingAvatarData = processed.data
pendingAvatarFileName = processed.fileName
if !isEditingProfile {
beginEditing()
}
}
/// userInfo
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 != displayNickname
guard nicknameChanged else {
let avatarChanged = pendingAvatarData != nil
guard nicknameChanged || avatarChanged else {
cancelEditing()
return
}
guard !isSaving else { return }
isSaving = true
defer { isSaving = false }
avatarUploadProgress = avatarChanged ? 1 : nil
defer {
isSaving = false
avatarUploadProgress = nil
}
try await api.updateUserInfo(nickname: nextNickname)
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)
}
var nextUser = userInfo ?? UserInfoResponse()
nextUser = UserInfoResponse(
avatar: nextUser.avatar,
avatar: uploadedAvatarURL ?? nextUser.avatar,
realName: nextUser.realName,
phone: nextUser.phone,
nickname: nextNickname,
@ -106,6 +131,7 @@ final class ProfileViewModel {
statusName: nextUser.statusName
)
userInfo = nextUser
clearPendingAvatar()
cancelEditing()
}
@ -139,6 +165,24 @@ final class ProfileViewModel {
let text = value?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
return text.isEmpty ? nil : text
}
/// 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
Task { @MainActor in
self?.avatarUploadProgress = progress
}
}
}
///
private func clearPendingAvatar() {
pendingAvatarData = nil
pendingAvatarFileName = nil
avatarUploadProgress = nil
}
}
///