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 20, 2023
1 parent 2b59edc commit b93ece7
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import Gridicons
import UIKit

@objc protocol BlogDetailHeaderViewDelegate {
func siteIconTapped()
func makeSiteIconMenu() -> UIMenu?
func didShowSiteIconMenu()
func siteIconReceivedDroppedImage(_ image: UIImage?)
func siteIconShouldAllowDroppedImages() -> Bool
func siteTitleTapped()
Expand Down Expand Up @@ -107,11 +108,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 +124,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
self?.delegate?.didShowSiteIconMenu()
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 @@ -40,7 +40,7 @@ extension SitePickerViewController {
blogDetailHeaderView.toggleSpotlightOnSiteIcon()
}

private func showNoticeAsNeeded() {
func showNoticeAsNeeded() {
if let tourToSuggest = QuickStartTourGuide.shared.tourToSuggest(for: blog) {
QuickStartTourGuide.shared.suggest(tourToSuggest, for: blog)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,71 +3,84 @@ 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()
}

if blog.hasIcon {
alert.addDestructiveActionWithTitle(SiteIconAlertStrings.Actions.removeSiteIcon) { [weak self] _ in
NoticesDispatch.unlock()
self?.removeSiteIcon()
func didShowSiteIconMenu() {
if QuickStartTourGuide.shared.isCurrentElement(.siteIcon) {
// There is no good way to determine when `UIMenu` is cancelled,
// so we wait until nothing is presented by the site picker.
NoticesDispatch.lock()
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
if self.presentedViewController == nil {
NoticesDispatch.unlock()
self.showNoticeAsNeeded()
timer.invalidate()
}
}
}
QuickStartTourGuide.shared.visited(.siteIcon)
}

alert.addCancelActionWithTitle(SiteIconAlertStrings.Actions.cancel) { [weak self] _ in
NoticesDispatch.unlock()
self?.startAlertTimer()
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 {
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 +101,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
Loading

0 comments on commit b93ece7

Please sign in to comment.