2021-03-18 10:33:07 +01:00
//
// C o m p o s e V i e w M o d e l + P u b l i s h S t a t e . s w i f t
// M a s t o d o n
//
// C r e a t e d b y M a i n a s u K C i r n o o n 2 0 2 1 - 3 - 1 8 .
//
import os . log
import Foundation
2021-03-18 12:42:26 +01:00
import Combine
2021-04-14 09:59:29 +02:00
import CoreDataStack
2021-03-18 10:33:07 +01:00
import GameplayKit
import MastodonSDK
extension ComposeViewModel {
class PublishState : GKState {
weak var viewModel : ComposeViewModel ?
init ( viewModel : ComposeViewModel ) {
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 )
2021-03-29 11:44:52 +02:00
viewModel ? . publishStateMachinePublisher . value = self
2021-03-18 10:33:07 +01:00
}
}
}
extension ComposeViewModel . PublishState {
class Initial : ComposeViewModel . PublishState {
override func isValidNextState ( _ stateClass : AnyClass ) -> Bool {
return stateClass = = Publishing . self
}
}
class Publishing : ComposeViewModel . PublishState {
2021-03-18 12:42:26 +01:00
var publishingSubscription : AnyCancellable ?
2021-03-18 10:33:07 +01:00
override func isValidNextState ( _ stateClass : AnyClass ) -> Bool {
return stateClass = = Fail . self || stateClass = = Finish . self
}
override func didEnter ( from previousState : GKState ? ) {
super . didEnter ( from : previousState )
guard let viewModel = viewModel , let stateMachine = stateMachine else { return }
2021-03-29 11:44:52 +02:00
viewModel . updatePublishDate ( )
2022-01-27 14:23:39 +01:00
let authenticationBox = viewModel . authenticationBox
let domain = authenticationBox . domain
let attachmentServices = viewModel . attachmentServices
2021-03-19 12:49:48 +01:00
let mediaIDs = attachmentServices . compactMap { attachmentService in
2021-03-18 12:42:26 +01:00
attachmentService . attachment . value ? . id
}
2021-03-24 08:08:00 +01:00
let pollOptions : [ String ] ? = {
2022-01-27 14:23:39 +01:00
guard viewModel . isPollComposing else { return nil }
return viewModel . pollOptionAttributes . map { attribute in attribute . option . value }
2021-03-24 08:08:00 +01:00
} ( )
let pollExpiresIn : Int ? = {
2022-01-27 14:23:39 +01:00
guard viewModel . isPollComposing else { return nil }
2021-03-24 08:08:00 +01:00
return viewModel . pollExpiresOptionAttribute . expiresOption . value . seconds
} ( )
2021-04-14 09:59:29 +02:00
let inReplyToID : Mastodon . Entity . Status . ID ? = {
2022-01-27 14:23:39 +01:00
guard case let . reply ( status ) = viewModel . composeKind else { return nil }
2021-04-14 09:59:29 +02:00
var id : Mastodon . Entity . Status . ID ?
viewModel . context . managedObjectContext . performAndWait {
2022-01-27 14:23:39 +01:00
guard let replyTo = status . object ( in : viewModel . context . managedObjectContext ) else { return }
2021-04-14 09:59:29 +02:00
id = replyTo . id
}
return id
} ( )
2022-01-27 14:23:39 +01:00
let sensitive : Bool = viewModel . isContentWarningComposing
2021-03-25 11:17:05 +01:00
let spoilerText : String ? = {
2022-01-27 14:23:39 +01:00
let text = viewModel . composeStatusAttribute . contentWarningContent . trimmingCharacters ( in : . whitespacesAndNewlines )
2021-03-25 11:17:05 +01:00
guard ! text . isEmpty else {
return nil
}
return text
} ( )
2022-01-27 14:23:39 +01:00
let visibility = viewModel . selectedStatusVisibility . visibility
2021-03-24 08:08:00 +01:00
2021-03-19 12:49:48 +01:00
let updateMediaQuerySubscriptions : [ AnyPublisher < Mastodon . Response . Content < Mastodon . Entity . Attachment > , Error > ] = {
var subscriptions : [ AnyPublisher < Mastodon . Response . Content < Mastodon . Entity . Attachment > , Error > ] = [ ]
for attachmentService in attachmentServices {
guard let attachmentID = attachmentService . attachment . value ? . id else { continue }
let description = attachmentService . description . value ? . trimmingCharacters ( in : . whitespacesAndNewlines ) ? ? " "
guard ! description . isEmpty else { continue }
let query = Mastodon . API . Media . UpdateMediaQuery (
file : nil ,
thumbnail : nil ,
description : description ,
focus : nil
)
let subscription = viewModel . context . apiService . updateMedia (
domain : domain ,
attachmentID : attachmentID ,
query : query ,
2022-01-27 14:23:39 +01:00
mastodonAuthenticationBox : authenticationBox
2021-03-19 12:49:48 +01:00
)
subscriptions . append ( subscription )
}
return subscriptions
} ( )
2021-08-09 11:54:11 +02:00
let idempotencyKey = viewModel . idempotencyKey . value
2021-03-19 12:49:48 +01:00
publishingSubscription = Publishers . MergeMany ( updateMediaQuerySubscriptions )
. collect ( )
2022-01-27 14:23:39 +01:00
. asyncMap { attachments -> Mastodon . Response . Content < Mastodon . Entity . Status > in
2021-03-19 12:49:48 +01:00
let query = Mastodon . API . Statuses . PublishStatusQuery (
2022-01-27 14:23:39 +01:00
status : viewModel . composeStatusAttribute . composeContent ,
2021-03-24 08:08:00 +01:00
mediaIDs : mediaIDs . isEmpty ? nil : mediaIDs ,
pollOptions : pollOptions ,
2021-03-25 11:17:05 +01:00
pollExpiresIn : pollExpiresIn ,
2021-04-14 09:59:29 +02:00
inReplyToID : inReplyToID ,
2021-03-25 11:17:05 +01:00
sensitive : sensitive ,
2021-03-25 12:34:30 +01:00
spoilerText : spoilerText ,
visibility : visibility
2021-03-19 12:49:48 +01:00
)
2022-01-27 14:23:39 +01:00
return try await viewModel . context . apiService . publishStatus (
2021-03-19 12:49:48 +01:00
domain : domain ,
2021-08-09 11:54:11 +02:00
idempotencyKey : idempotencyKey ,
2021-03-19 12:49:48 +01:00
query : query ,
2022-01-27 14:23:39 +01:00
authenticationBox : authenticationBox
2021-03-19 12:49:48 +01:00
)
}
. receive ( on : DispatchQueue . main )
. sink { completion in
switch completion {
case . failure ( let error ) :
os_log ( . info , log : . debug , " %{public}s[%{public}ld], %{public}s: publish status %s " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function , error . localizedDescription )
stateMachine . enter ( Fail . self )
case . finished :
os_log ( . info , log : . debug , " %{public}s[%{public}ld], %{public}s: publish status success " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function )
stateMachine . enter ( Finish . self )
}
} receiveValue : { response in
os_log ( . info , log : . debug , " %{public}s[%{public}ld], %{public}s: status %s published: %s " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function , response . value . id , response . value . uri )
2021-03-18 10:33:07 +01:00
}
}
}
class Fail : ComposeViewModel . PublishState {
override func isValidNextState ( _ stateClass : AnyClass ) -> Bool {
// a l l o w d i s c a r d p u b l i s h i n g
2021-03-29 11:44:52 +02:00
return stateClass = = Publishing . self || stateClass = = Discard . self
}
}
class Discard : ComposeViewModel . PublishState {
override func isValidNextState ( _ stateClass : AnyClass ) -> Bool {
return false
2021-03-18 10:33:07 +01:00
}
}
class Finish : ComposeViewModel . PublishState {
override func isValidNextState ( _ stateClass : AnyClass ) -> Bool {
return false
}
}
}