mastodon-ios/ShareActionExtension/Scene/View/StatusAttachmentViewModel+U...

132 lines
5.3 KiB
Swift

//
// StatusAttachmentViewModel+UploadState.swift
// ShareActionExtension
//
// Created by MainasuK Cirno on 2021-7-20.
//
import os.log
import Foundation
import Combine
import GameplayKit
import MastodonSDK
import MastodonCore
extension StatusAttachmentViewModel {
class UploadState: GKState {
weak var viewModel: StatusAttachmentViewModel?
init(viewModel: StatusAttachmentViewModel) {
self.viewModel = viewModel
}
override func didEnter(from previousState: GKState?) {
os_log("%{public}s[%{public}ld], %{public}s: enter %s, previous: %s", ((#file as NSString).lastPathComponent), #line, #function, self.debugDescription, previousState.debugDescription)
viewModel?.uploadStateMachineSubject.send(self)
}
}
}
extension StatusAttachmentViewModel.UploadState {
class Initial: StatusAttachmentViewModel.UploadState {
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
guard viewModel?.authentication.value != nil else { return false }
if stateClass == Initial.self {
return true
}
if viewModel?.file.value != nil {
return stateClass == Uploading.self
} else {
return stateClass == Fail.self
}
}
}
class Uploading: StatusAttachmentViewModel.UploadState {
let logger = Logger(subsystem: "StatusAttachmentViewModel.UploadState.Uploading", category: "logic")
var needsFallback = false
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return stateClass == Fail.self || stateClass == Finish.self || stateClass == Uploading.self
}
override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState)
guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
guard let authentication = viewModel.authentication.value else { return }
guard let file = viewModel.file.value else { return }
let description = viewModel.descriptionContent
let query = Mastodon.API.Media.UploadMediaQuery(
file: file,
thumbnail: nil,
description: description,
focus: nil
)
let mastodonAuthenticationBox = MastodonAuthenticationBox(
authenticationRecord: .init(objectID: authentication.objectID),
domain: authentication.domain,
userID: authentication.userID,
appAuthorization: Mastodon.API.OAuth.Authorization(accessToken: authentication.appAccessToken),
userAuthorization: Mastodon.API.OAuth.Authorization(accessToken: authentication.userAccessToken)
)
// and needs clone the `query` if needs retry
viewModel.api.uploadMedia(
domain: mastodonAuthenticationBox.domain,
query: query,
mastodonAuthenticationBox: mastodonAuthenticationBox,
needsFallback: needsFallback
)
.receive(on: DispatchQueue.main)
.sink { [weak self] completion in
guard let self = self else { return }
switch completion {
case .failure(let error):
if let apiError = error as? Mastodon.API.Error,
apiError.httpResponseStatus == .notFound,
self.needsFallback == false
{
self.needsFallback = true
stateMachine.enter(Uploading.self)
self.logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fallback to V1")
} else {
self.logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fail: \(error.localizedDescription)")
viewModel.error = error
stateMachine.enter(Fail.self)
}
case .finished:
self.logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): upload attachment success")
break
}
} receiveValue: { [weak self] response in
guard let self = self else { return }
self.logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): upload attachment \(response.value.id) success, \(response.value.url ?? "<nil>")")
viewModel.attachment.value = response.value
stateMachine.enter(Finish.self)
}
.store(in: &viewModel.disposeBag)
}
}
class Fail: StatusAttachmentViewModel.UploadState {
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
// allow discard publishing
return stateClass == Uploading.self || stateClass == Finish.self
}
}
class Finish: StatusAttachmentViewModel.UploadState {
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return false
}
}
}