feat: make copy action works in media preview scene

This commit is contained in:
CMK 2021-07-06 20:02:30 +08:00
parent 66de0593df
commit 4baaf3368b
4 changed files with 45 additions and 10 deletions

View File

@ -202,14 +202,10 @@ extension StatusTableViewCellDelegate where Self: StatusProvider {
return self.context.photoLibraryService.copyImage(url: url) return self.context.photoLibraryService.copyImage(url: url)
} }
.switchToLatest() .switchToLatest()
.sink(receiveCompletion: { [weak self] completion in .sink(receiveCompletion: { completion in
guard let self = self else { return }
switch completion { switch completion {
case .failure(let error): case .failure(let error):
guard let error = error as? PhotoLibraryService.PhotoLibraryError, os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: copy photo fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
case .noPermission = error else { return }
let alertController = SettingService.openSettingsAlertController(title: L10n.Common.Alerts.SavePhotoFailure.title, message: L10n.Common.Alerts.SavePhotoFailure.message)
self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
case .finished: case .finished:
break break
} }

View File

@ -226,6 +226,24 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate {
case .local(let meta): case .local(let meta):
context.photoLibraryService.save(image: meta.image, withNotificationFeedback: true) context.photoLibraryService.save(image: meta.image, withNotificationFeedback: true)
} }
case .copyPhoto:
switch viewController.viewModel.item {
case .status(let meta):
context.photoLibraryService.copyImage(url: meta.url)
.sink { completion in
switch completion {
case .failure(let error):
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: copy photo fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
case .finished:
break
}
} receiveValue: { _ in
// do nothing
}
.store(in: &context.disposeBag)
case .local(let meta):
context.photoLibraryService.copy(image: meta.image, withNotificationFeedback: true)
}
case .share: case .share:
let applicationActivities: [UIActivity] = [ let applicationActivities: [UIActivity] = [
SafariActivity(sceneCoordinator: self.coordinator) SafariActivity(sceneCoordinator: self.coordinator)
@ -236,6 +254,7 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate {
) )
activityViewController.popoverPresentationController?.sourceView = viewController.previewImageView.imageView activityViewController.popoverPresentationController?.sourceView = viewController.previewImageView.imageView
self.present(activityViewController, animated: true, completion: nil) self.present(activityViewController, animated: true, completion: nil)
} }
} }

View File

@ -133,6 +133,14 @@ extension MediaPreviewImageViewController: UIContextMenuInteractionDelegate {
guard let self = self else { return } guard let self = self else { return }
self.delegate?.mediaPreviewImageViewController(self, contextMenuActionPerform: .savePhoto) self.delegate?.mediaPreviewImageViewController(self, contextMenuActionPerform: .savePhoto)
} }
let copyAction = UIAction(
title: L10n.Common.Controls.Actions.copyPhoto, image: UIImage(systemName: "doc.on.doc")!, identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off
) { [weak self] _ in
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: copy photo", ((#file as NSString).lastPathComponent), #line, #function)
guard let self = self else { return }
self.delegate?.mediaPreviewImageViewController(self, contextMenuActionPerform: .copyPhoto)
}
let shareAction = UIAction( let shareAction = UIAction(
title: L10n.Common.Controls.Actions.share, image: UIImage(systemName: "square.and.arrow.up")!, identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off title: L10n.Common.Controls.Actions.share, image: UIImage(systemName: "square.and.arrow.up")!, identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off
@ -145,6 +153,7 @@ extension MediaPreviewImageViewController: UIContextMenuInteractionDelegate {
let actionProvider: UIContextMenuActionProvider = { elements -> UIMenu? in let actionProvider: UIContextMenuActionProvider = { elements -> UIMenu? in
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: [ return UIMenu(title: "", image: nil, identifier: nil, options: [], children: [
saveAction, saveAction,
copyAction,
shareAction shareAction
]) ])
} }
@ -162,6 +171,7 @@ extension MediaPreviewImageViewController: UIContextMenuInteractionDelegate {
extension MediaPreviewImageViewController { extension MediaPreviewImageViewController {
enum ContextMenuAction { enum ContextMenuAction {
case savePhoto case savePhoto
case copyPhoto
case share case share
} }
} }

View File

@ -26,6 +26,10 @@ extension PhotoLibraryService {
extension PhotoLibraryService { extension PhotoLibraryService {
func saveImage(url: URL) -> AnyPublisher<UIImage, Error> { func saveImage(url: URL) -> AnyPublisher<UIImage, Error> {
guard PHPhotoLibrary.authorizationStatus(for: .addOnly) != .denied else {
return Fail(error: PhotoLibraryError.noPermission).eraseToAnyPublisher()
}
return processImage(url: url) return processImage(url: url)
.handleEvents(receiveOutput: { image in .handleEvents(receiveOutput: { image in
self.save(image: image) self.save(image: image)
@ -45,10 +49,6 @@ extension PhotoLibraryService {
let impactFeedbackGenerator = UIImpactFeedbackGenerator(style: .light) let impactFeedbackGenerator = UIImpactFeedbackGenerator(style: .light)
let notificationFeedbackGenerator = UINotificationFeedbackGenerator() let notificationFeedbackGenerator = UINotificationFeedbackGenerator()
guard PHPhotoLibrary.authorizationStatus(for: .addOnly) != .denied else {
return Fail(error: PhotoLibraryError.noPermission).eraseToAnyPublisher()
}
return ImagePipeline.shared.imagePublisher(with: url) return ImagePipeline.shared.imagePublisher(with: url)
.handleEvents(receiveSubscription: { _ in .handleEvents(receiveSubscription: { _ in
impactFeedbackGenerator.impactOccurred() impactFeedbackGenerator.impactOccurred()
@ -87,6 +87,16 @@ extension PhotoLibraryService {
notificationFeedbackGenerator.notificationOccurred(.success) notificationFeedbackGenerator.notificationOccurred(.success)
} }
} }
func copy(image: UIImage, withNotificationFeedback: Bool = false) {
UIPasteboard.general.image = image
// assert no error
if withNotificationFeedback {
let notificationFeedbackGenerator = UINotificationFeedbackGenerator()
notificationFeedbackGenerator.notificationOccurred(.success)
}
}
@objc private func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) { @objc private func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
// TODO: notify banner // TODO: notify banner