feat: make copy action works in media preview scene
This commit is contained in:
parent
66de0593df
commit
4baaf3368b
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue