From d127eb51f2453fb28f4ba7015ed828007dde921c Mon Sep 17 00:00:00 2001
From: Justin Mazzocchi <2831158+jzzocc@users.noreply.github.com>
Date: Sat, 16 Jan 2021 23:14:17 -0800
Subject: [PATCH] Extraction from extension context
---
Share Extension/Info.plist | 4 +-
...areExtensionNavigationViewController.swift | 12 +--
.../ViewModels/CompositionViewModel.swift | 87 +++++++++++++------
.../ViewModels/NewStatusViewModel.swift | 36 +++++---
.../Sources/ViewModels/RootViewModel.swift | 3 +-
.../ShareExtensionNavigationViewModel.swift | 5 +-
6 files changed, 94 insertions(+), 53 deletions(-)
diff --git a/Share Extension/Info.plist b/Share Extension/Info.plist
index 40936e6..7ed487b 100644
--- a/Share Extension/Info.plist
+++ b/Share Extension/Info.plist
@@ -27,13 +27,11 @@
NSExtensionActivationRule
NSExtensionActivationSupportsImageWithMaxCount
- 4
+ 1
NSExtensionActivationSupportsText
NSExtensionActivationSupportsMovieWithMaxCount
1
- NSExtensionActivationSupportsWebPageWithMaxCount
- 1
NSExtensionActivationSupportsWebURLWithMaxCount
1
diff --git a/Share Extension/ShareExtensionNavigationViewController.swift b/Share Extension/ShareExtensionNavigationViewController.swift
index e8f093b..46b6041 100644
--- a/Share Extension/ShareExtensionNavigationViewController.swift
+++ b/Share Extension/ShareExtensionNavigationViewController.swift
@@ -11,13 +11,14 @@ class ShareExtensionNavigationViewController: UINavigationController {
environment: .live(
userNotificationCenter: .current(),
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
do {
- newStatusViewModel = try viewModel.newStatusViewModel()
+ newStatusViewModel = try viewModel.newStatusViewModel(extensionContext: extensionContext)
} catch {
setViewControllers([ShareErrorViewController(error: error)], animated: false)
@@ -28,9 +29,4 @@ class ShareExtensionNavigationViewController: UINavigationController {
[UIHostingController(rootView: NewStatusView { newStatusViewModel })],
animated: false)
}
-
- @available(*, unavailable)
- required init?(coder aDecoder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
}
diff --git a/ViewModels/Sources/ViewModels/CompositionViewModel.swift b/ViewModels/Sources/ViewModels/CompositionViewModel.swift
index 61c9fa4..2c0c074 100644
--- a/ViewModels/Sources/ViewModels/CompositionViewModel.swift
+++ b/ViewModels/Sources/ViewModels/CompositionViewModel.swift
@@ -4,19 +4,20 @@ import Combine
import Foundation
import Mastodon
import ServiceLayer
+import UniformTypeIdentifiers
public final class CompositionViewModel: AttachmentsRenderingViewModel, ObservableObject, Identifiable {
public let id = Id()
public var isPosted = false
- @Published public var text: String
- @Published public var contentWarning: String
- @Published public var displayContentWarning: Bool
- @Published public var sensitive: Bool
- @Published public var displayPoll: Bool
- @Published public var pollMultipleChoice: Bool
+ @Published public var text = ""
+ @Published public var contentWarning = ""
+ @Published public var displayContentWarning = false
+ @Published public var sensitive = false
+ @Published public var displayPoll = false
+ @Published public var pollMultipleChoice = false
@Published public var pollExpiresIn = PollExpiry.oneDay
- @Published public private(set) var pollOptions: [PollOption]
- @Published public private(set) var attachmentViewModels: [AttachmentViewModel]
+ @Published public private(set) var pollOptions = [PollOption(text: ""), PollOption(text: "")]
+ @Published public private(set) var attachmentViewModels = [AttachmentViewModel]()
@Published public private(set) var attachmentUpload: AttachmentUpload?
@Published public private(set) var isPostable = false
@Published public private(set) var canAddAttachment = true
@@ -27,24 +28,8 @@ public final class CompositionViewModel: AttachmentsRenderingViewModel, Observab
private let eventsSubject: PassthroughSubject
private var attachmentUploadCancellable: AnyCancellable?
- init(eventsSubject: PassthroughSubject,
- redraft: (status: Status, identification: Identification)? = nil) {
+ init(eventsSubject: PassthroughSubject) {
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 }
.removeDuplicates()
@@ -111,6 +96,58 @@ public extension CompositionViewModel {
typealias Id = UUID
+ convenience init(eventsSubject: PassthroughSubject, 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,
+ 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 {
StatusComponents(
inReplyToId: inReplyToId,
diff --git a/ViewModels/Sources/ViewModels/NewStatusViewModel.swift b/ViewModels/Sources/ViewModels/NewStatusViewModel.swift
index 09aeb47..cc51400 100644
--- a/ViewModels/Sources/ViewModels/NewStatusViewModel.swift
+++ b/ViewModels/Sources/ViewModels/NewStatusViewModel.swift
@@ -7,7 +7,7 @@ import ServiceLayer
public final class NewStatusViewModel: ObservableObject {
@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 authenticatedIdentities = [Identity]()
@Published public var canPost = false
@@ -27,25 +27,33 @@ public final class NewStatusViewModel: ObservableObject {
identification: Identification,
environment: AppEnvironment,
inReplyTo: StatusViewModel?,
- redraft: Status?) {
+ redraft: Status?,
+ extensionContext: NSExtensionContext?) {
self.allIdentitiesService = allIdentitiesService
self.identification = identification
self.environment = environment
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()
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()
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.assign(to: &$authenticatedIdentities)
diff --git a/ViewModels/Sources/ViewModels/RootViewModel.swift b/ViewModels/Sources/ViewModels/RootViewModel.swift
index a02f40d..f85dad4 100644
--- a/ViewModels/Sources/ViewModels/RootViewModel.swift
+++ b/ViewModels/Sources/ViewModels/RootViewModel.swift
@@ -68,7 +68,8 @@ public extension RootViewModel {
identification: identification,
environment: environment,
inReplyTo: inReplyTo,
- redraft: redraft)
+ redraft: redraft,
+ extensionContext: nil)
}
}
diff --git a/ViewModels/Sources/ViewModels/ShareExtensionNavigationViewModel.swift b/ViewModels/Sources/ViewModels/ShareExtensionNavigationViewModel.swift
index 6238ef0..450deb5 100644
--- a/ViewModels/Sources/ViewModels/ShareExtensionNavigationViewModel.swift
+++ b/ViewModels/Sources/ViewModels/ShareExtensionNavigationViewModel.swift
@@ -19,7 +19,7 @@ public final class ShareExtensionNavigationViewModel: ObservableObject {
}
public extension ShareExtensionNavigationViewModel {
- func newStatusViewModel() throws -> NewStatusViewModel {
+ func newStatusViewModel(extensionContext: NSExtensionContext?) throws -> NewStatusViewModel {
let allIdentitiesService = try AllIdentitiesService(environment: environment)
guard let identity = try allIdentitiesService.mostRecentAuthenticatedIdentity()
@@ -38,6 +38,7 @@ public extension ShareExtensionNavigationViewModel {
identification: identification,
environment: environment,
inReplyTo: nil,
- redraft: nil)
+ redraft: nil,
+ extensionContext: extensionContext)
}
}