Extraction from extension context

This commit is contained in:
Justin Mazzocchi 2021-01-16 23:14:17 -08:00
parent 358aebf0f0
commit d127eb51f2
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
6 changed files with 94 additions and 53 deletions

View file

@ -27,13 +27,11 @@
<key>NSExtensionActivationRule</key> <key>NSExtensionActivationRule</key>
<dict> <dict>
<key>NSExtensionActivationSupportsImageWithMaxCount</key> <key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>4</integer> <integer>1</integer>
<key>NSExtensionActivationSupportsText</key> <key>NSExtensionActivationSupportsText</key>
<true/> <true/>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key> <key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>1</integer> <integer>1</integer>
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key> <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer> <integer>1</integer>
</dict> </dict>

View file

@ -11,13 +11,14 @@ class ShareExtensionNavigationViewController: UINavigationController {
environment: .live( environment: .live(
userNotificationCenter: .current(), userNotificationCenter: .current(),
reduceMotion: { UIAccessibility.isReduceMotionEnabled })) reduceMotion: { UIAccessibility.isReduceMotionEnabled }))
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) override func viewDidLoad() {
super.viewDidLoad()
let newStatusViewModel: NewStatusViewModel let newStatusViewModel: NewStatusViewModel
do { do {
newStatusViewModel = try viewModel.newStatusViewModel() newStatusViewModel = try viewModel.newStatusViewModel(extensionContext: extensionContext)
} catch { } catch {
setViewControllers([ShareErrorViewController(error: error)], animated: false) setViewControllers([ShareErrorViewController(error: error)], animated: false)
@ -28,9 +29,4 @@ class ShareExtensionNavigationViewController: UINavigationController {
[UIHostingController(rootView: NewStatusView { newStatusViewModel })], [UIHostingController(rootView: NewStatusView { newStatusViewModel })],
animated: false) animated: false)
} }
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
} }

View file

@ -4,19 +4,20 @@ import Combine
import Foundation import Foundation
import Mastodon import Mastodon
import ServiceLayer import ServiceLayer
import UniformTypeIdentifiers
public final class CompositionViewModel: AttachmentsRenderingViewModel, ObservableObject, Identifiable { public final class CompositionViewModel: AttachmentsRenderingViewModel, ObservableObject, Identifiable {
public let id = Id() public let id = Id()
public var isPosted = false public var isPosted = false
@Published public var text: String @Published public var text = ""
@Published public var contentWarning: String @Published public var contentWarning = ""
@Published public var displayContentWarning: Bool @Published public var displayContentWarning = false
@Published public var sensitive: Bool @Published public var sensitive = false
@Published public var displayPoll: Bool @Published public var displayPoll = false
@Published public var pollMultipleChoice: Bool @Published public var pollMultipleChoice = false
@Published public var pollExpiresIn = PollExpiry.oneDay @Published public var pollExpiresIn = PollExpiry.oneDay
@Published public private(set) var pollOptions: [PollOption] @Published public private(set) var pollOptions = [PollOption(text: ""), PollOption(text: "")]
@Published public private(set) var attachmentViewModels: [AttachmentViewModel] @Published public private(set) var attachmentViewModels = [AttachmentViewModel]()
@Published public private(set) var attachmentUpload: AttachmentUpload? @Published public private(set) var attachmentUpload: AttachmentUpload?
@Published public private(set) var isPostable = false @Published public private(set) var isPostable = false
@Published public private(set) var canAddAttachment = true @Published public private(set) var canAddAttachment = true
@ -27,24 +28,8 @@ public final class CompositionViewModel: AttachmentsRenderingViewModel, Observab
private let eventsSubject: PassthroughSubject<Event, Never> private let eventsSubject: PassthroughSubject<Event, Never>
private var attachmentUploadCancellable: AnyCancellable? private var attachmentUploadCancellable: AnyCancellable?
init(eventsSubject: PassthroughSubject<Event, Never>, init(eventsSubject: PassthroughSubject<Event, Never>) {
redraft: (status: Status, identification: Identification)? = nil) {
self.eventsSubject = eventsSubject self.eventsSubject = eventsSubject
text = redraft?.status.text ?? ""
contentWarning = redraft?.status.spoilerText ?? ""
displayContentWarning = !(redraft?.status.spoilerText.isEmpty ?? true)
sensitive = redraft?.status.sensitive ?? false
displayPoll = redraft?.status.poll != nil
pollMultipleChoice = redraft?.status.poll?.multiple ?? false
pollOptions = redraft?.status.poll?.options.map { PollOption(text: $0.title) }
?? [PollOption(text: ""), PollOption(text: "")]
if let redraft = redraft {
attachmentViewModels = redraft.status.mediaAttachments.map {
AttachmentViewModel(attachment: $0, identification: redraft.identification)
}
} else {
attachmentViewModels = [AttachmentViewModel]()
}
$text.map { !$0.isEmpty } $text.map { !$0.isEmpty }
.removeDuplicates() .removeDuplicates()
@ -111,6 +96,58 @@ public extension CompositionViewModel {
typealias Id = UUID typealias Id = UUID
convenience init(eventsSubject: PassthroughSubject<Event, Never>, redraft: Status, identification: Identification) {
self.init(eventsSubject: eventsSubject)
if let text = redraft.text {
self.text = text
}
contentWarning = redraft.spoilerText
displayContentWarning = redraft.spoilerText.isEmpty
sensitive = redraft.sensitive
displayPoll = redraft.poll != nil
attachmentViewModels = redraft.mediaAttachments.map {
AttachmentViewModel(attachment: $0, identification: identification)
}
if let poll = redraft.poll {
pollMultipleChoice = poll.multiple
pollOptions = poll.options.map { PollOption(text: $0.title) }
}
}
convenience init(eventsSubject: PassthroughSubject<Event, Never>,
extensionContext: NSExtensionContext,
parentViewModel: NewStatusViewModel) {
self.init(eventsSubject: eventsSubject)
guard let inputItem = extensionContext.inputItems.first as? NSExtensionItem,
let itemProvider = inputItem.attachments?.first
else { return }
if itemProvider.hasItemConformingToTypeIdentifier(UTType.plainText.identifier) {
itemProvider.loadItem(forTypeIdentifier: UTType.plainText.identifier, options: nil) { result, _ in
guard let text = result as? String else { return }
self.text = text
}
} else if itemProvider.hasItemConformingToTypeIdentifier(UTType.url.identifier) {
itemProvider.loadItem(forTypeIdentifier: UTType.url.identifier, options: nil) { result, _ in
guard let url = result as? URL else { return }
if let contentText = inputItem.attributedContentText?.string {
self.text.append(contentText)
self.text.append("\n\n")
}
self.text.append(url.absoluteString)
}
} else {
attach(itemProvider: itemProvider, parentViewModel: parentViewModel)
}
}
func components(inReplyToId: Status.Id?, visibility: Status.Visibility) -> StatusComponents { func components(inReplyToId: Status.Id?, visibility: Status.Visibility) -> StatusComponents {
StatusComponents( StatusComponents(
inReplyToId: inReplyToId, inReplyToId: inReplyToId,

View file

@ -7,7 +7,7 @@ import ServiceLayer
public final class NewStatusViewModel: ObservableObject { public final class NewStatusViewModel: ObservableObject {
@Published public var visibility: Status.Visibility @Published public var visibility: Status.Visibility
@Published public private(set) var compositionViewModels: [CompositionViewModel] @Published public private(set) var compositionViewModels = [CompositionViewModel]()
@Published public private(set) var identification: Identification @Published public private(set) var identification: Identification
@Published public private(set) var authenticatedIdentities = [Identity]() @Published public private(set) var authenticatedIdentities = [Identity]()
@Published public var canPost = false @Published public var canPost = false
@ -27,25 +27,33 @@ public final class NewStatusViewModel: ObservableObject {
identification: Identification, identification: Identification,
environment: AppEnvironment, environment: AppEnvironment,
inReplyTo: StatusViewModel?, inReplyTo: StatusViewModel?,
redraft: Status?) { redraft: Status?,
extensionContext: NSExtensionContext?) {
self.allIdentitiesService = allIdentitiesService self.allIdentitiesService = allIdentitiesService
self.identification = identification self.identification = identification
self.environment = environment self.environment = environment
inReplyToViewModel = inReplyTo inReplyToViewModel = inReplyTo
let redraftAndIdentification: (status: Status, identification: Identification)?
if let redraft = redraft {
redraftAndIdentification = (status: redraft, identification: identification)
} else {
redraftAndIdentification = nil
}
compositionViewModels = [CompositionViewModel(
eventsSubject: compositionEventsSubject,
redraft: redraftAndIdentification)]
events = eventsSubject.eraseToAnyPublisher() events = eventsSubject.eraseToAnyPublisher()
visibility = identification.identity.preferences.postingDefaultVisibility visibility = identification.identity.preferences.postingDefaultVisibility
let compositionViewModel: CompositionViewModel
if let redraft = redraft {
compositionViewModel = CompositionViewModel(
eventsSubject: compositionEventsSubject,
redraft: redraft,
identification: identification)
} else if let extensionContext = extensionContext {
compositionViewModel = CompositionViewModel(
eventsSubject: compositionEventsSubject,
extensionContext: extensionContext,
parentViewModel: self)
} else {
compositionViewModel = CompositionViewModel(eventsSubject: compositionEventsSubject)
}
compositionViewModels = [compositionViewModel]
allIdentitiesService.authenticatedIdentitiesPublisher() allIdentitiesService.authenticatedIdentitiesPublisher()
.assignErrorsToAlertItem(to: \.alertItem, on: self) .assignErrorsToAlertItem(to: \.alertItem, on: self)
.assign(to: &$authenticatedIdentities) .assign(to: &$authenticatedIdentities)

View file

@ -68,7 +68,8 @@ public extension RootViewModel {
identification: identification, identification: identification,
environment: environment, environment: environment,
inReplyTo: inReplyTo, inReplyTo: inReplyTo,
redraft: redraft) redraft: redraft,
extensionContext: nil)
} }
} }

View file

@ -19,7 +19,7 @@ public final class ShareExtensionNavigationViewModel: ObservableObject {
} }
public extension ShareExtensionNavigationViewModel { public extension ShareExtensionNavigationViewModel {
func newStatusViewModel() throws -> NewStatusViewModel { func newStatusViewModel(extensionContext: NSExtensionContext?) throws -> NewStatusViewModel {
let allIdentitiesService = try AllIdentitiesService(environment: environment) let allIdentitiesService = try AllIdentitiesService(environment: environment)
guard let identity = try allIdentitiesService.mostRecentAuthenticatedIdentity() guard let identity = try allIdentitiesService.mostRecentAuthenticatedIdentity()
@ -38,6 +38,7 @@ public extension ShareExtensionNavigationViewModel {
identification: identification, identification: identification,
environment: environment, environment: environment,
inReplyTo: nil, inReplyTo: nil,
redraft: nil) redraft: nil,
extensionContext: extensionContext)
} }
} }