Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 64 additions & 18 deletions WordPress/Classes/ViewRelated/Themes/ThemeBrowserCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public enum ThemeAction {
case tryCustomize
case view

static func activeActionsForTheme(_ theme: Theme) ->[ThemeAction] {
static func activeActionsForTheme(_ theme: Theme) -> [ThemeAction] {
if theme.custom {
if theme.hasDetailsURL() {
return [customize, details]
Expand All @@ -23,7 +23,7 @@ public enum ThemeAction {
return [customize, details, support]
}

static func inactiveActionsForTheme(_ theme: Theme) ->[ThemeAction] {
static func inactiveActionsForTheme(_ theme: Theme) -> [ThemeAction] {
if theme.custom {
if theme.hasDetailsURL() {
return [tryCustomize, activate, details]
Expand Down Expand Up @@ -100,6 +100,29 @@ open class ThemeBrowserCell: UICollectionViewCell {
@objc open var showPriceInformation: Bool = false
open weak var presenter: ThemePresenter?

/// Replaces the action button with a spinner while the theme is being activated.
var isActivating: Bool = false {
didSet {
actionButton.isHidden = isActivating
if isActivating {
activatingIndicator.startAnimating()
} else {
activatingIndicator.stopAnimating()
}
}
}

private lazy var activatingIndicator: UIActivityIndicatorView = {
let indicator = UIActivityIndicatorView(style: .medium)
indicator.hidesWhenStopped = true
indicator.accessibilityLabel = NSLocalizedString(
"themeBrowser.cell.activating",
value: "Activating",
comment: "Accessibility label for the spinner shown while a theme is being activated"
)
return indicator
}()

fileprivate var placeholderImage = UIImage(named: "theme-loading")
fileprivate var activeEllipsisImage = UIImage(named: "icon-menu-ellipsis-white")
fileprivate var inactiveEllipsisImage = UIImage(named: "icon-menu-ellipsis")
Expand All @@ -109,9 +132,12 @@ open class ThemeBrowserCell: UICollectionViewCell {
override open var isHighlighted: Bool {
didSet {
let alphaFinal: CGFloat = isHighlighted ? 0.3 : 0
UIView.animate(withDuration: 0.2, animations: { [weak self] in
self?.highlightView.alpha = alphaFinal
})
UIView.animate(
withDuration: 0.2,
animations: { [weak self] in
self?.highlightView.alpha = alphaFinal
}
)
}
}

Expand All @@ -120,6 +146,13 @@ open class ThemeBrowserCell: UICollectionViewCell {

actionButton.isExclusiveTouch = true

infoBar.addSubview(activatingIndicator)
activatingIndicator.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
activatingIndicator.centerXAnchor.constraint(equalTo: actionButton.centerXAnchor),
activatingIndicator.centerYAnchor.constraint(equalTo: actionButton.centerYAnchor)
])

layer.cornerRadius = 12
layer.cornerCurve = .continuous
layer.borderColor = UIColor.separator.cgColor
Expand All @@ -135,6 +168,7 @@ open class ThemeBrowserCell: UICollectionViewCell {
theme = nil
presenter = nil
showPriceInformation = false
isActivating = false
}

fileprivate func refreshGUI() {
Expand Down Expand Up @@ -220,20 +254,25 @@ open class ThemeBrowserCell: UICollectionViewCell {
}

fileprivate func refreshScreenshotImage(_ imageUrl: String) {
let imageUrlWithWidth = imageUrlForWidth( imageUrl: imageUrl )
let imageUrlWithWidth = imageUrlForWidth(imageUrl: imageUrl)
let screenshotUrl = URL(string: imageUrlWithWidth)

imageView.backgroundColor = Styles.placeholderColor
activityView.startAnimating()
imageView.downloadImage(from: screenshotUrl, success: { [weak self] _ in
self?.showScreenshot()
}, failure: { [weak self] error in
if let error = error as NSError?, error.domain == NSURLErrorDomain && error.code == NSURLErrorCancelled {
imageView.downloadImage(
from: screenshotUrl,
success: { [weak self] _ in
self?.showScreenshot()
},
failure: { [weak self] error in
let nsError = error as NSError?
if nsError?.domain == NSURLErrorDomain && nsError?.code == NSURLErrorCancelled {
return
}
DDLogError("Error loading theme screenshot: \(String(describing: error?.localizedDescription))")
self?.showPlaceholder()
})
}
)
}

// MARK: - Actions
Expand All @@ -245,13 +284,17 @@ open class ThemeBrowserCell: UICollectionViewCell {

let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

let themeActions = theme.isCurrentTheme() ? ThemeAction.activeActionsForTheme(theme) : ThemeAction.inactiveActionsForTheme(theme)
let themeActions =
theme.isCurrentTheme()
? ThemeAction.activeActionsForTheme(theme) : ThemeAction.inactiveActionsForTheme(theme)
themeActions.forEach { themeAction in
alertController.addActionWithTitle(themeAction.title,
style: .default,
handler: { (_: UIAlertAction) in
themeAction.present(theme, presenter)
})
alertController.addActionWithTitle(
themeAction.title,
style: .default,
handler: { (_: UIAlertAction) in
themeAction.present(theme, presenter)
}
)
}

let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel action title")
Expand Down Expand Up @@ -290,7 +333,10 @@ extension ThemeBrowserCell: Accessible {

private func prepareActionButtonForVoiceOver() {
actionButton.isAccessibilityElement = true
actionButton.accessibilityLabel = NSLocalizedString("More", comment: "Action button to display more available options")
actionButton.accessibilityLabel = NSLocalizedString(
"More",
comment: "Action button to display more available options"
)
actionButton.accessibilityTraits = .button
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,6 @@ open class ThemeBrowserViewController: UIViewController, UICollectionViewDataSou
fileprivate var activityIndicator: UIActivityIndicatorView = {
let indicatorView = UIActivityIndicatorView(style: .medium)
indicatorView.frame = themesLoaderFrame
//TODO update color with white headers
indicatorView.color = .white
indicatorView.startAnimating()
return indicatorView
}()
Expand Down Expand Up @@ -252,6 +250,9 @@ open class ThemeBrowserViewController: UIViewController, UICollectionViewDataSou

fileprivate var presentingTheme: Theme?

/// The theme whose activation request is in flight. Drives the grid cell spinner across cell reuse and reloads.
private var activatingThemeId: String?

private var noResultsViewController: NoResultsViewController?

private struct NoResultsTitles {
Expand Down Expand Up @@ -620,6 +621,7 @@ open class ThemeBrowserViewController: UIViewController, UICollectionViewDataSou

cell.presenter = self
cell.theme = themeAtIndexPath(indexPath)
cell.isActivating = activatingThemeId != nil && cell.theme?.themeId == activatingThemeId

if sections[indexPath.section] == .themes {
syncMoreThemesIfNeeded(indexPath)
Expand Down Expand Up @@ -939,10 +941,12 @@ open class ThemeBrowserViewController: UIViewController, UICollectionViewDataSou
@objc var onWebkitViewControllerClose: (() -> Void)?

@objc open func activateTheme(_ theme: Theme?) {
guard let theme, !theme.isCurrentTheme() else {
guard let theme, !theme.isCurrentTheme(), activatingThemeId == nil else {
return
}

activatingThemeId = theme.themeId
collectionView?.reloadData()
updateActivateButton(isLoading: true)

_ = themeService.activate(
Expand All @@ -955,6 +959,7 @@ open class ThemeBrowserViewController: UIViewController, UICollectionViewDataSou
blog: self?.blog
)

self?.activatingThemeId = nil
self?.collectionView?.reloadData()

let successTitle = NSLocalizedString(
Expand All @@ -966,10 +971,6 @@ open class ThemeBrowserViewController: UIViewController, UICollectionViewDataSou
comment: "Message of alert when theme activation succeeds"
)
let successMessage = String(format: successFormat, theme?.name ?? "", theme?.author ?? "")
let manageTitle = NSLocalizedString(
"Manage site",
comment: "Return to blog screen action when theme activation succeeds"
)

self?.updateActivateButton(isLoading: false)

Expand All @@ -978,13 +979,6 @@ open class ThemeBrowserViewController: UIViewController, UICollectionViewDataSou
message: successMessage,
preferredStyle: .alert
)
alertController.addActionWithTitle(
manageTitle,
style: .default,
handler: { [weak self] (_: UIAlertAction) in
_ = self?.navigationController?.popViewController(animated: true)
}
)
alertController.addDefaultActionWithTitle(SharedStrings.Button.ok, handler: nil)
alertController.presentFromRootViewController()
},
Expand All @@ -998,6 +992,8 @@ open class ThemeBrowserViewController: UIViewController, UICollectionViewDataSou
comment: "Title of alert when theme activation fails"
)

self?.activatingThemeId = nil
self?.collectionView?.reloadData()
self?.activityIndicator.stopAnimating()
self?.activateButton?.customView = nil

Expand Down