119 lines
3.8 KiB
Swift
119 lines
3.8 KiB
Swift
//
|
|
// ProfileHeaderViewModel.swift
|
|
// Mastodon
|
|
//
|
|
// Created by MainasuK Cirno on 2021-4-9.
|
|
//
|
|
|
|
import os.log
|
|
import UIKit
|
|
import Combine
|
|
import Kanna
|
|
import MastodonSDK
|
|
import MastodonMeta
|
|
|
|
final class ProfileHeaderViewModel {
|
|
|
|
static let avatarImageMaxSizeInPixel = CGSize(width: 400, height: 400)
|
|
static let maxProfileFieldCount = 4
|
|
|
|
var disposeBag = Set<AnyCancellable>()
|
|
|
|
// input
|
|
let context: AppContext
|
|
@Published var isEditing = false
|
|
@Published var accountForEdit: Mastodon.Entity.Account?
|
|
@Published var emojiMeta: MastodonContent.Emojis = [:]
|
|
|
|
let viewDidAppear = CurrentValueSubject<Bool, Never>(false)
|
|
let needsSetupBottomShadow = CurrentValueSubject<Bool, Never>(true)
|
|
let needsFiledCollectionViewHidden = CurrentValueSubject<Bool, Never>(false)
|
|
let isTitleViewContentOffsetSet = CurrentValueSubject<Bool, Never>(false)
|
|
|
|
// output
|
|
let isTitleViewDisplaying = CurrentValueSubject<Bool, Never>(false)
|
|
let displayProfileInfo = ProfileInfo()
|
|
let editProfileInfo = ProfileInfo()
|
|
let editProfileInfoDidInitialized = CurrentValueSubject<Void, Never>(Void()) // needs trigger initial event
|
|
|
|
init(context: AppContext) {
|
|
self.context = context
|
|
|
|
Publishers.CombineLatest(
|
|
$isEditing.removeDuplicates(), // only trigger when value toggle
|
|
$accountForEdit
|
|
)
|
|
.receive(on: DispatchQueue.main)
|
|
.sink { [weak self] isEditing, account in
|
|
guard let self = self else { return }
|
|
guard isEditing else { return }
|
|
// setup editing value when toggle to editing
|
|
self.editProfileInfo.name = self.displayProfileInfo.name // set to name
|
|
self.editProfileInfo.avatarImage = nil // set to empty
|
|
self.editProfileInfo.note = ProfileHeaderViewModel.normalize(note: self.displayProfileInfo.note)
|
|
self.editProfileInfoDidInitialized.send()
|
|
}
|
|
.store(in: &disposeBag)
|
|
}
|
|
|
|
}
|
|
|
|
extension ProfileHeaderViewModel {
|
|
class ProfileInfo {
|
|
// input
|
|
@Published var name: String?
|
|
@Published var avatarImageURL: URL?
|
|
@Published var avatarImage: UIImage?
|
|
@Published var note: String?
|
|
|
|
// output
|
|
@Published var avatarImageResource = ImageResource(url: nil, image: nil)
|
|
|
|
struct ImageResource {
|
|
let url: URL?
|
|
let image: UIImage?
|
|
}
|
|
|
|
init() {
|
|
Publishers.CombineLatest(
|
|
$avatarImageURL,
|
|
$avatarImage
|
|
)
|
|
.map { url, image in
|
|
ImageResource(url: url, image: image)
|
|
}
|
|
.assign(to: &$avatarImageResource)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension ProfileHeaderViewModel {
|
|
|
|
static func normalize(note: String?) -> String? {
|
|
let _note = note?.replacingOccurrences(of: "<br>|<br />", with: "\u{2028}", options: .regularExpression, range: nil)
|
|
.replacingOccurrences(of: "</p>", with: "</p>\u{2029}", range: nil)
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
guard let note = _note, !note.isEmpty else {
|
|
return nil
|
|
}
|
|
|
|
let html = try? HTML(html: note, encoding: .utf8)
|
|
return html?.text
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// MARK: - ProfileViewModelEditable
|
|
extension ProfileHeaderViewModel: ProfileViewModelEditable {
|
|
func isEdited() -> Bool {
|
|
guard isEditing else { return false }
|
|
|
|
guard editProfileInfo.name == displayProfileInfo.name else { return true }
|
|
guard editProfileInfo.avatarImage == nil else { return true }
|
|
guard editProfileInfo.note == ProfileHeaderViewModel.normalize(note: displayProfileInfo.note) else { return true }
|
|
|
|
return false
|
|
}
|
|
}
|