Browse files for attachments

This commit is contained in:
Justin Mazzocchi 2021-01-09 23:08:45 -08:00
parent 487e8a766c
commit 377bf6aecc
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
4 changed files with 80 additions and 35 deletions

View file

@ -46,6 +46,9 @@
"compose.attachment.uploading" = "Uploading"; "compose.attachment.uploading" = "Uploading";
"compose.prompt" = "What's on your mind?"; "compose.prompt" = "What's on your mind?";
"compose.mark-media-sensitive" = "Mark media as sensitive"; "compose.mark-media-sensitive" = "Mark media as sensitive";
"compose.photo-library" = "Photo Library";
"compose.take-photo-or-video" = "Take Photo or Video";
"compose.browse" = "Browse";
"error" = "Error"; "error" = "Error";
"favorites" = "Favorites"; "favorites" = "Favorites";
"registration.review-terms-of-use-and-privacy-policy-%@" = "Please review %@'s Terms of Use and Privacy Policy to continue"; "registration.review-terms-of-use-and-privacy-policy-%@" = "Please review %@'s Terms of Use and Privacy Policy to continue";

View file

@ -20,6 +20,7 @@ final class NewStatusViewController: UIViewController {
action: nil) action: nil)
private let mediaSelections = PassthroughSubject<[PHPickerResult], Never>() private let mediaSelections = PassthroughSubject<[PHPickerResult], Never>()
private let imagePickerResults = PassthroughSubject<[UIImagePickerController.InfoKey: Any]?, Never>() private let imagePickerResults = PassthroughSubject<[UIImagePickerController.InfoKey: Any]?, Never>()
private let documentPickerResuls = PassthroughSubject<[URL]?, Never>()
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
init(viewModel: NewStatusViewModel) { init(viewModel: NewStatusViewModel) {
@ -110,6 +111,16 @@ extension NewStatusViewController: UIImagePickerControllerDelegate {
} }
} }
extension NewStatusViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
documentPickerResuls.send(urls)
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
documentPickerResuls.send(nil)
}
}
// Required by UIImagePickerController // Required by UIImagePickerController
extension NewStatusViewController: UINavigationControllerDelegate {} extension NewStatusViewController: UINavigationControllerDelegate {}
@ -122,6 +133,8 @@ private extension NewStatusViewController {
#if !IS_SHARE_EXTENSION #if !IS_SHARE_EXTENSION
presentCamera(compositionViewModel: compositionViewModel) presentCamera(compositionViewModel: compositionViewModel)
#endif #endif
case let .presentDocumentPicker(compositionViewModel):
presentDocumentPicker(compositionViewModel: compositionViewModel)
case let .editAttachment(attachmentViewModel, compositionViewModel): case let .editAttachment(attachmentViewModel, compositionViewModel):
presentAttachmentEditor( presentAttachmentEditor(
attachmentViewModel: attachmentViewModel, attachmentViewModel: attachmentViewModel,
@ -301,6 +314,28 @@ private extension NewStatusViewController {
} }
#endif #endif
func presentDocumentPicker(compositionViewModel: CompositionViewModel) {
documentPickerResuls.first().sink { [weak self] in
guard let self = self,
let result = $0?.first,
result.startAccessingSecurityScopedResource(),
let itemProvider = NSItemProvider(contentsOf: result)
else { return }
self.viewModel.attach(itemProvider: itemProvider, to: compositionViewModel)
result.stopAccessingSecurityScopedResource()
}
.store(in: &cancellables)
let documentPickerController = UIDocumentPickerViewController(forOpeningContentTypes: [.image, .movie, .audio])
documentPickerController.delegate = self
documentPickerController.allowsMultipleSelection = false
documentPickerController.modalPresentationStyle = .overFullScreen
present(documentPickerController, animated: true)
}
func presentAttachmentEditor(attachmentViewModel: AttachmentViewModel, compositionViewModel: CompositionViewModel) { func presentAttachmentEditor(attachmentViewModel: AttachmentViewModel, compositionViewModel: CompositionViewModel) {
let editAttachmentsView = EditAttachmentView { (attachmentViewModel, compositionViewModel) } let editAttachmentsView = EditAttachmentView { (attachmentViewModel, compositionViewModel) }
let editAttachmentViewController = UIHostingController(rootView: editAttachmentsView) let editAttachmentViewController = UIHostingController(rootView: editAttachmentsView)

View file

@ -53,6 +53,7 @@ public extension NewStatusViewModel {
enum Event { enum Event {
case presentMediaPicker(CompositionViewModel) case presentMediaPicker(CompositionViewModel)
case presentCamera(CompositionViewModel) case presentCamera(CompositionViewModel)
case presentDocumentPicker(CompositionViewModel)
case editAttachment(AttachmentViewModel, CompositionViewModel) case editAttachment(AttachmentViewModel, CompositionViewModel)
} }
@ -89,6 +90,10 @@ public extension NewStatusViewModel {
eventsSubject.send(.presentCamera(viewModel)) eventsSubject.send(.presentCamera(viewModel))
} }
func presentDocumentPicker(viewModel: CompositionViewModel) {
eventsSubject.send(.presentDocumentPicker(viewModel))
}
func remove(viewModel: CompositionViewModel) { func remove(viewModel: CompositionViewModel) {
compositionViewModels.removeAll { $0 === viewModel } compositionViewModels.removeAll { $0 === viewModel }
} }

View file

@ -45,40 +45,44 @@ private extension CompositionInputAccessoryView {
stackView.translatesAutoresizingMaskIntoConstraints = false stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.spacing = .defaultSpacing stackView.spacing = .defaultSpacing
let mediaButton = UIButton() var attachmentActions = [
UIAction(
title: NSLocalizedString("compose.browse", comment: ""),
image: UIImage(systemName: "ellipsis")) { [weak self] _ in
guard let self = self else { return }
stackView.addArrangedSubview(mediaButton) self.parentViewModel.presentDocumentPicker(viewModel: self.viewModel)
mediaButton.setImage( },
UIImage( UIAction(
systemName: "photo", title: NSLocalizedString("compose.photo-library", comment: ""),
withConfiguration: UIImage.SymbolConfiguration(scale: .medium)), image: UIImage(systemName: "rectangle.on.rectangle")) { [weak self] _ in
for: .normal)
mediaButton.addAction(UIAction { [weak self] _ in
guard let self = self else { return } guard let self = self else { return }
self.parentViewModel.presentMediaPicker(viewModel: self.viewModel) self.parentViewModel.presentMediaPicker(viewModel: self.viewModel)
}, }
for: .touchUpInside) ]
let cameraButton = UIButton()
#if !IS_SHARE_EXTENSION #if !IS_SHARE_EXTENSION
if AVCaptureDevice.authorizationStatus(for: .video) != .restricted { attachmentActions.insert(UIAction(
stackView.addArrangedSubview(cameraButton) title: NSLocalizedString("compose.take-photo-or-video", comment: ""),
cameraButton.setImage( image: UIImage(systemName: "camera.fill")) { [weak self] _ in
UIImage(
systemName: "camera",
withConfiguration: UIImage.SymbolConfiguration(scale: .medium)),
for: .normal)
cameraButton.addAction(UIAction { [weak self] _ in
guard let self = self else { return } guard let self = self else { return }
self.parentViewModel.presentCamera(viewModel: self.viewModel) self.parentViewModel.presentCamera(viewModel: self.viewModel)
}, },
for: .touchUpInside) at: 1)
}
#endif #endif
let attachmentButton = UIButton()
stackView.addArrangedSubview(attachmentButton)
attachmentButton.setImage(
UIImage(
systemName: "paperclip",
withConfiguration: UIImage.SymbolConfiguration(scale: .medium)),
for: .normal)
attachmentButton.showsMenuAsPrimaryAction = true
attachmentButton.menu = UIMenu(children: attachmentActions)
let pollButton = UIButton() let pollButton = UIButton()
stackView.addArrangedSubview(pollButton) stackView.addArrangedSubview(pollButton)
@ -126,10 +130,8 @@ private extension CompositionInputAccessoryView {
self.parentViewModel.insert(after: self.viewModel) self.parentViewModel.insert(after: self.viewModel)
}, for: .touchUpInside) }, for: .touchUpInside)
viewModel.$canAddAttachment.sink { viewModel.$canAddAttachment
mediaButton.isEnabled = $0 .sink { attachmentButton.isEnabled = $0 }
cameraButton.isEnabled = $0
}
.store(in: &cancellables) .store(in: &cancellables)
viewModel.$remainingCharacters.sink { viewModel.$remainingCharacters.sink {
@ -148,7 +150,7 @@ private extension CompositionInputAccessoryView {
} }
.store(in: &cancellables) .store(in: &cancellables)
for button in [mediaButton, pollButton, visibilityButton, contentWarningButton, addButton] { for button in [attachmentButton, pollButton, visibilityButton, contentWarningButton, addButton] {
button.heightAnchor.constraint(greaterThanOrEqualToConstant: .minimumButtonDimension).isActive = true button.heightAnchor.constraint(greaterThanOrEqualToConstant: .minimumButtonDimension).isActive = true
button.widthAnchor.constraint(greaterThanOrEqualToConstant: .minimumButtonDimension).isActive = true button.widthAnchor.constraint(greaterThanOrEqualToConstant: .minimumButtonDimension).isActive = true
} }