Extraction from extension context
This commit is contained in:
parent
358aebf0f0
commit
d127eb51f2
|
@ -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>
|
||||||
|
|
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue