Skip to content

Commit

Permalink
Switch to UIMenu in site icon picker flow
Browse files Browse the repository at this point in the history
  • Loading branch information
kean committed Aug 18, 2023
1 parent 2b59edc commit 3a9cafc
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Gridicons
import UIKit

@objc protocol BlogDetailHeaderViewDelegate {
func siteIconTapped()
func makeSiteIconMenu() -> UIMenu?
func siteIconReceivedDroppedImage(_ image: UIImage?)
func siteIconShouldAllowDroppedImages() -> Bool
func siteTitleTapped()
Expand Down Expand Up @@ -107,11 +107,12 @@ class BlogDetailHeaderView: UIView {

// MARK: - Initializers

required init(items: [ActionRow.Item]) {
required init(items: [ActionRow.Item], delegate: BlogDetailHeaderViewDelegate) {
titleView = TitleView(frame: .zero)

super.init(frame: .zero)

self.delegate = delegate
setupChildViews(items: items)
}

Expand All @@ -122,11 +123,14 @@ class BlogDetailHeaderView: UIView {
// MARK: - Child View Initialization

private func setupChildViews(items: [ActionRow.Item]) {
titleView.siteIconView.tapped = { [weak self] in
QuickStartTourGuide.shared.visited(.siteIcon)
self?.titleView.siteIconView.spotlightIsShown = false
assert(delegate != nil)

self?.delegate?.siteIconTapped()
if let menu = delegate?.makeSiteIconMenu() {
titleView.siteIconView.setMenu(menu) { [weak self] in
QuickStartTourGuide.shared.visited(.siteIcon)
WPAnalytics.track(.siteSettingsSiteIconTapped)
self?.titleView.siteIconView.spotlightIsShown = false
}
}

titleView.siteIconView.dropped = { [weak self] images in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ class SiteIconView: UIView {

private var dropInteraction: UIDropInteraction?

/// Set the menu to be displayed when the button is tapped. The menu replaces
/// teh default on tap action.
func setMenu(_ menu: UIMenu, onMenuTriggerd: @escaping () -> Void) {
button.menu = menu
button.showsMenuAsPrimaryAction = true
button.addAction(UIAction { _ in onMenuTriggerd() }, for: .menuActionTriggered)
}

private let button: UIButton = {
let button = UIButton(frame: .zero)
button.backgroundColor = UIColor.secondaryButtonBackground
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,71 +3,68 @@ import WordPressFlux
import WordPressShared
import SwiftUI
import SVProgressHUD
import Gridicons

extension SitePickerViewController {

func showSiteIconSelectionAlert() {
let alert = UIAlertController(title: SiteIconAlertStrings.title,
message: nil,
preferredStyle: .actionSheet)

alert.popoverPresentationController?.sourceView = blogDetailHeaderView.blavatarImageView.superview
alert.popoverPresentationController?.sourceRect = blogDetailHeaderView.blavatarImageView.frame
alert.popoverPresentationController?.permittedArrowDirections = .any

alert.addDefaultActionWithTitle(SiteIconAlertStrings.Actions.chooseImage) { [weak self] _ in
NoticesDispatch.unlock()
self?.updateSiteIcon()
}

alert.addDefaultActionWithTitle(SiteIconAlertStrings.Actions.createWithEmoji) { [weak self] _ in
NoticesDispatch.unlock()
self?.showEmojiPicker()
}

alert.addDestructiveActionWithTitle(SiteIconAlertStrings.Actions.removeSiteIcon) { [weak self] _ in
NoticesDispatch.unlock()
self?.removeSiteIcon()
func makeSiteIconMenu() -> UIMenu? {
guard siteIconShouldAllowDroppedImages() else {
return nil
}

alert.addCancelActionWithTitle(SiteIconAlertStrings.Actions.cancel) { [weak self] _ in
NoticesDispatch.unlock()
self?.startAlertTimer()
}

present(alert, animated: true)
return UIMenu(children: [
UIDeferredMenuElement.uncached { [weak self] in
$0(self?.makeUpdateSiteIconActions() ?? [])
}
])
}

func showUpdateSiteIconAlert() {
let alert = UIAlertController(title: nil,
message: nil,
preferredStyle: .actionSheet)

alert.popoverPresentationController?.sourceView = blogDetailHeaderView.blavatarImageView.superview
alert.popoverPresentationController?.sourceRect = blogDetailHeaderView.blavatarImageView.frame
alert.popoverPresentationController?.permittedArrowDirections = .any

alert.addDefaultActionWithTitle(SiteIconAlertStrings.Actions.changeSiteIcon) { [weak self] _ in
NoticesDispatch.unlock()
self?.updateSiteIcon()
private func makeUpdateSiteIconActions() -> [UIAction] {
var actions: [UIAction] = []
if FeatureFlag.nativePhotoPicker.enabled {
actions += [
MediaPickerMenu.makePickFromPhotosAction { [weak self] in
self?.updateSiteIcon(source: .photosLibrary)
},
MediaPickerMenu.makeTakePhotoAction { [weak self] in
self?.updateSiteIcon(source: .camera)
},
MediaPickerMenu.makePickFromMediaAction { [weak self] in
self?.updateSiteIcon(source: .mediaLibrary)
}
]
} else {
actions.append(UIAction(
title: SiteIconAlertStrings.Actions.changeSiteIcon,
image: UIImage(systemName: "photo.on.rectangle"),
handler: { [weak self] _ in self?.updateSiteIcon(source: .combined) }
))
}
if FeatureFlag.siteIconCreator.enabled {
actions.append(UIAction(
title: SiteIconAlertStrings.Actions.createWithEmoji,
image: UIImage(systemName: "face.smiling"),
handler: { [weak self] _ in self?.showEmojiPicker() }
))
}

if blog.hasIcon {
alert.addDestructiveActionWithTitle(SiteIconAlertStrings.Actions.removeSiteIcon) { [weak self] _ in
NoticesDispatch.unlock()
self?.removeSiteIcon()
}
}

alert.addCancelActionWithTitle(SiteIconAlertStrings.Actions.cancel) { [weak self] _ in
NoticesDispatch.unlock()
self?.startAlertTimer()
}
actions.append(UIAction(
title: SiteIconAlertStrings.Actions.removeSiteIcon,
image: UIImage(systemName: "trash"),
attributes: [.destructive],
handler: { [weak self] _ in self?.removeSiteIcon() }
))
}
return actions
}

present(alert, animated: true)
enum SiteIconSource {
case photosLibrary
case camera
case mediaLibrary
case combined // legacy option
}

func updateSiteIcon() {
func updateSiteIcon(source: SiteIconSource = .combined) {
siteIconPickerPresenter = SiteIconPickerPresenter(blog: blog)
siteIconPickerPresenter?.onCompletion = { [ weak self] media, error in
if error != nil {
Expand All @@ -88,7 +85,16 @@ extension SitePickerViewController {
self?.dismiss(animated: true)
}

siteIconPickerPresenter?.presentPickerFrom(self)
switch source {
case .photosLibrary:
siteIconPickerPresenter?.presentPhotosPicker(from: self)
case .camera:
siteIconPickerPresenter?.presentCamera(from: self)
case .mediaLibrary:
siteIconPickerPresenter?.presentMediaLibraryPicker(from: self)
case .combined:
siteIconPickerPresenter?.presentPickerFrom(self)
}
}

func showEmojiPicker() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ final class SitePickerViewController: UIViewController {
let mediaService: MediaService

private(set) lazy var blogDetailHeaderView: BlogDetailHeaderView = {
let headerView = BlogDetailHeaderView(items: [])
let headerView = BlogDetailHeaderView(items: [], delegate: self)
headerView.translatesAutoresizingMaskIntoConstraints = false
return headerView
}()
Expand Down Expand Up @@ -51,7 +51,6 @@ final class SitePickerViewController: UIViewController {

private func setupHeaderView() {
blogDetailHeaderView.blog = blog
blogDetailHeaderView.delegate = self
view.addSubview(blogDetailHeaderView)
view.pinSubviewToAllEdges(blogDetailHeaderView)
}
Expand All @@ -71,25 +70,6 @@ final class SitePickerViewController: UIViewController {

extension SitePickerViewController: BlogDetailHeaderViewDelegate {

func siteIconTapped() {
guard siteIconShouldAllowDroppedImages() else {
// Gracefully ignore the tap for users that can not upload files or
// blogs that do not have capabilities since those will not support the REST API icon update
return
}

WPAnalytics.track(.siteSettingsSiteIconTapped)

NoticesDispatch.lock()

guard FeatureFlag.siteIconCreator.enabled else {
showUpdateSiteIconAlert()
return
}

showSiteIconSelectionAlert()
}

func siteIconReceivedDroppedImage(_ image: UIImage?) {
if !siteIconShouldAllowDroppedImages() {
// Gracefully ignore the drop for users that can not upload files or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,49 @@ final class SiteIconPickerPresenter: NSObject {
unregisterChangeObserver()
}

func presentPhotosPicker(from presentingViewController: UIViewController) {
var configuration = PHPickerConfiguration()
configuration.filter = .images

let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
presentingViewController.present(picker, animated: true)
}

func presentCamera(from presentingViewController: UIViewController) {
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .camera
picker.allowsEditing = false
presentingViewController.present(picker, animated: true, completion: nil)
}

func presentMediaLibraryPicker(from presentingViewController: UIViewController) {
let options = WPMediaPickerOptions()
options.showMostRecentFirst = true
options.filter = [.image]
options.allowMultipleSelection = false
options.showSearchBar = true
options.badgedUTTypes = [UTType.gif.identifier]
options.preferredStatusBarStyle = WPStyleGuide.preferredStatusBarStyle

let pickerViewController = WPNavigationMediaPickerViewController(options: options)

let dataSource = MediaLibraryPickerDataSource(blog: blog)
dataSource.ignoreSyncErrors = true

pickerViewController.dataSource = dataSource
pickerViewController.delegate = self
pickerViewController.modalPresentationStyle = .formSheet

presentingViewController.present(mediaPickerViewController, animated: true)
}

/// Presents a new WPMediaPickerViewController instance.
///
@objc func presentPickerFrom(_ viewController: UIViewController) {
if FeatureFlag.nativePhotoPicker.enabled {
var configuration = PHPickerConfiguration()
configuration.filter = .images

let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
viewController.present(picker, animated: true)
} else {
viewController.present(mediaPickerViewController, animated: true)
registerChangeObserver(forPicker: mediaPickerViewController.mediaPicker)
}
viewController.present(mediaPickerViewController, animated: true)
registerChangeObserver(forPicker: mediaPickerViewController.mediaPicker)
}

// MARK: - Private Methods
Expand Down Expand Up @@ -212,6 +241,19 @@ extension SiteIconPickerPresenter: PHPickerViewControllerDelegate {
}
}

extension SiteIconPickerPresenter: UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
picker.dismiss(animated: true) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
#warning("Fix this")
self.showImageCropViewController(image)
}
}
}
}

extension SiteIconPickerPresenter: UINavigationControllerDelegate {}

extension SiteIconPickerPresenter: WPMediaPickerViewControllerDelegate {

func mediaPickerControllerWillBeginLoadingData(_ picker: WPMediaPickerViewController) {
Expand Down
37 changes: 37 additions & 0 deletions WordPress/Classes/ViewRelated/Media/MediaPicker.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import UIKit
import Gridicons

struct MediaPickerMenu {
static func makePickFromPhotosAction(_ handler: @escaping () -> Void) -> UIAction {
UIAction(
title: Strings.pickFromPhotosLibrary,
image: UIImage(systemName: "photo.on.rectangle.angled"),
attributes: [],
handler: { _ in handler() }
)
}

static func makeTakePhotoAction(_ handler: @escaping () -> Void) -> UIAction {
UIAction(
title: Strings.takePhoto,
image: UIImage(systemName: "camera"),
attributes: [],
handler: { _ in handler() }
)
}

static func makePickFromMediaAction(_ handler: @escaping () -> Void) -> UIAction {
UIAction(
title: Strings.pickFromMedia,
image: UIImage(systemName: "photo.stack"),
attributes: [],
handler: { _ in handler() }
)
}
}

private enum Strings {
static let pickFromPhotosLibrary = NSLocalizedString("mediaPicker.pickFromPhotosLibrary", value: "Choose from Device", comment: "The name of the action in the context menu")
static let takePhoto = NSLocalizedString("mediaPicker.takePhoto", value: "Take Photo", comment: "The name of the action in the context menu")
static let pickFromMedia = NSLocalizedString("mediaPicker.pickFromMediaLibrary", value: "Choose from Media", comment: "The name of the action in the context menu (user's WordPress Media Library")
}
Loading

0 comments on commit 3a9cafc

Please sign in to comment.