Skip to content
Merged
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
9 changes: 8 additions & 1 deletion WordPress/Classes/Stores/RemoteConfigStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ class RemoteConfigStore {

// MARK: Public Functions

/// Looks up the value for a remote config parameter.
/// - Parameters:
/// - key: The key associated with a remote config parameter
public func value(for key: String) -> Any? {
return cache[key]
}

/// Fetches remote config values from the server.
/// - Parameter callback: An optional callback that can be used to update UI following the fetch. It is not called on the UI thread.
public func update(then callback: FetchCallback? = nil) {
Expand All @@ -49,7 +56,7 @@ extension RemoteConfigStore {
typealias FetchCallback = () -> Void

/// The local cache stores remote config values between runs so that the most recently fetched set are ready to go as soon as this object is instantiated.
private(set) var cache: [String: Any] {
private var cache: [String: Any] {
get {
// Read from the cache in a thread-safe way
queue.sync {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ struct RemoteConfigParameter<T> {
private let store: RemoteConfigStore

private var serverValue: T? {
return store.cache[key] as? T
return store.value(for: key) as? T
}

// MARK: Initializer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ - (void)setRestorableSelectedIndexPath:(NSIndexPath *)restorableSelectedIndexPat
switch (section.category) {
case BlogDetailsSectionCategoryQuickAction:
case BlogDetailsSectionCategoryQuickStart:
case BlogDetailsSectionCategoryJetpackBrandingCard:
case BlogDetailsSectionCategoryDomainCredit: {
_restorableSelectedIndexPath = nil;
}
Expand Down Expand Up @@ -700,6 +701,7 @@ - (void)reloadTableViewPreservingSelection
switch (section.category) {
case BlogDetailsSectionCategoryQuickAction:
case BlogDetailsSectionCategoryQuickStart:
case BlogDetailsSectionCategoryJetpackBrandingCard:
case BlogDetailsSectionCategoryDomainCredit: {
BlogDetailsSubsection subsection = [self shouldShowDashboard] ? BlogDetailsSubsectionHome : BlogDetailsSubsectionStats;
BlogDetailsSectionCategory category = [self sectionCategoryWithSubsection:subsection blog: self.blog];
Expand Down Expand Up @@ -742,7 +744,7 @@ - (void)configureTableViewData
if (MigrationSuccessCardView.shouldShowMigrationSuccessCard == YES) {
[marr addObject:[self migrationSuccessSectionViewModel]];
}
if (JetpackBrandingMenuCardCoordinator.shouldShowCard == YES) {
if (self.shouldShowJetpackBrandingMenuCard == YES) {
[marr addObject:[self jetpackCardSectionViewModel]];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ struct JetpackFullscreenOverlayGeneralViewModel: JetpackFullscreenOverlayViewMod
return Strings.PhaseTwoAndThree.notificationsTitle
case (.three, .reader):
return Strings.PhaseTwoAndThree.readerTitle
case (.three, _):
return Strings.PhaseThree.generalTitle
default:
return ""
}
Expand Down Expand Up @@ -110,7 +112,7 @@ struct JetpackFullscreenOverlayGeneralViewModel: JetpackFullscreenOverlayViewMod
case .login:
fallthrough
case .appOpen:
return "" // TODO: Add new animation when ready
return Constants.allFeaturesLogosAnimationLtr
}
}

Expand All @@ -127,7 +129,7 @@ struct JetpackFullscreenOverlayGeneralViewModel: JetpackFullscreenOverlayViewMod
case .login:
fallthrough
case .appOpen:
return "" // TODO: Add new animation when ready
return Constants.allFeaturesLogosAnimationRtl
}
}

Expand Down Expand Up @@ -175,13 +177,15 @@ struct JetpackFullscreenOverlayGeneralViewModel: JetpackFullscreenOverlayViewMod
}

var continueButtonText: String? {
switch source {
case .stats:
switch (source, phase) {
case (.stats, _):
return Strings.General.statsContinueButtonTitle
case .notifications:
case (.notifications, _):
return Strings.General.notificationsContinueButtonTitle
case .reader:
case (.reader, _):
return Strings.General.readerContinueButtonTitle
case (_, .three):
return Strings.PhaseThree.generalContinueButtonTitle
default:
return nil
}
Expand Down Expand Up @@ -242,6 +246,8 @@ private extension JetpackFullscreenOverlayGeneralViewModel {
static let readerLogoAnimationRtl = "JetpackReaderLogoAnimation_rtl"
static let notificationsLogoAnimationLtr = "JetpackNotificationsLogoAnimation_ltr"
static let notificationsLogoAnimationRtl = "JetpackNotificationsLogoAnimation_rtl"
static let allFeaturesLogosAnimationLtr = "JetpackAllFeaturesLogosAnimation_ltr"
static let allFeaturesLogosAnimationRtl = "JetpackAllFeaturesLogosAnimation_rtl"
}

enum Strings {
Expand Down Expand Up @@ -314,5 +320,15 @@ private extension JetpackFullscreenOverlayGeneralViewModel {
value: "Switching is free and only takes a minute.",
comment: "A footnote in a screen displayed when the user accesses a Jetpack powered feature from the WordPress app. The screen showcases the Jetpack app.")
}

enum PhaseThree {
static let generalTitle = NSLocalizedString("jetpack.fullscreen.overlay.phaseThree.general.title",
value: "Jetpack features are moving soon.",
comment: "Title of a screen that showcases the Jetpack app.")

static let generalContinueButtonTitle = NSLocalizedString("jetpack.fullscreen.overlay.phaseThree.general.continue.title",
value: "Continue without Jetpack",
comment: "Title of a button that dismisses an overlay that showcases the Jetpack app.")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,26 @@ import Foundation

extension BlogDetailsViewController {

@objc var shouldShowJetpackBrandingMenuCard: Bool {
let presenter = JetpackBrandingMenuCardPresenter()
return presenter.shouldShowCard()
}

@objc func jetpackCardSectionViewModel() -> BlogDetailsSection {
let row = BlogDetailsRow()
row.callback = {}
row.callback = {
JetpackFeaturesRemovalCoordinator.presentOverlayIfNeeded(from: .card, in: self)
}

let section = BlogDetailsSection(title: nil,
rows: [row],
footerTitle: nil,
category: .jetpackBrandingCard)
return section
}

func reloadTableView() {
configureTableViewData()
reloadTableViewPreservingSelection()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ class JetpackBrandingMenuCardCell: UITableViewCell {

// MARK: Private Variables

private weak var viewController: UIViewController?
private weak var viewController: BlogDetailsViewController?
private var presenter: JetpackBrandingMenuCardPresenter

/// Sets the animation based on the language orientation
private var animation: Animation? {
Expand Down Expand Up @@ -119,11 +120,13 @@ class JetpackBrandingMenuCardCell: UITableViewCell {
// MARK: Initializers

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
presenter = JetpackBrandingMenuCardPresenter()
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}

required init?(coder: NSCoder) {
presenter = JetpackBrandingMenuCardPresenter()
super.init(coder: coder)
commonInit()
}
Expand All @@ -144,15 +147,15 @@ class JetpackBrandingMenuCardCell: UITableViewCell {

private func setupContent() {
logosAnimationView.play()
let config = JetpackBrandingMenuCardCoordinator.cardConfig
let config = presenter.cardConfig()
descriptionLabel.text = config?.description
learnMoreSuperview.isHidden = config?.learnMoreButtonURL == nil
}

// MARK: Actions

@objc private func learnMoreButtonTapped() {
guard let config = JetpackBrandingMenuCardCoordinator.cardConfig,
guard let config = presenter.cardConfig(),
let urlString = config.learnMoreButtonURL,
let url = URL(string: urlString) else {
return
Expand Down Expand Up @@ -186,11 +189,15 @@ private extension JetpackBrandingMenuCardCell {
// MARK: Actions

private func remindMeLaterTapped() {
// TODO: Implement this
presenter.remindLaterTapped()
viewController?.reloadTableView()
// TODO: Track button tapped
}

private func hideThisTapped() {
// TODO: Implement this
presenter.hideThisTapped()
viewController?.reloadTableView()
// TODO: Track button tapped
}
}

Expand Down Expand Up @@ -276,7 +283,7 @@ private extension JetpackBrandingMenuCardCell {
extension JetpackBrandingMenuCardCell {

@objc(configureWithViewController:)
func configure(with viewController: UIViewController) {
func configure(with viewController: BlogDetailsViewController) {
self.viewController = viewController
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import Foundation

class JetpackBrandingMenuCardPresenter {

struct Config {
let description: String
let learnMoreButtonURL: String?
}

// MARK: Private Variables

private let remoteConfigStore: RemoteConfigStore
private let persistenceStore: UserPersistentRepository
private let featureFlagStore: RemoteFeatureFlagStore
private let currentDateProvider: CurrentDateProvider

// MARK: Initializers

init(remoteConfigStore: RemoteConfigStore = RemoteConfigStore(),
featureFlagStore: RemoteFeatureFlagStore = RemoteFeatureFlagStore(),
persistenceStore: UserPersistentRepository = UserDefaults.standard,
currentDateProvider: CurrentDateProvider = DefaultCurrentDateProvider()) {
self.remoteConfigStore = remoteConfigStore
self.featureFlagStore = featureFlagStore
self.persistenceStore = persistenceStore
self.currentDateProvider = currentDateProvider
}

// MARK: Public Functions

func cardConfig() -> Config? {
let phase = JetpackFeaturesRemovalCoordinator.generalPhase(featureFlagStore: featureFlagStore)
switch phase {
case .three:
let description = Strings.phaseThreeDescription
let url = RemoteConfig(store: remoteConfigStore).phaseThreeBlogPostUrl.value
return .init(description: description, learnMoreButtonURL: url)
default:
return nil
}
}

func shouldShowCard() -> Bool {
let showCardOnDate = showCardOnDate ?? .distantPast // If not set, then return distant past so that the condition below always succeeds
guard shouldHideCard == false, // Card not hidden
showCardOnDate < currentDateProvider.date(), // Interval has passed if temporarily hidden
let _ = cardConfig() else { // Card is enabled in the current phase
return false
}
return true
}

func remindLaterTapped() {
let now = currentDateProvider.date()
let duration = Constants.remindLaterDurationInDays * Constants.secondsInDay
let newDate = now.addingTimeInterval(TimeInterval(duration))
showCardOnDate = newDate
}

func hideThisTapped() {
shouldHideCard = true
}
}

private extension JetpackBrandingMenuCardPresenter {
var shouldHideCard: Bool {
get {
persistenceStore.bool(forKey: Constants.shouldHideCardKey)
}

set {
persistenceStore.set(newValue, forKey: Constants.shouldHideCardKey)
}
}

var showCardOnDate: Date? {
get {
persistenceStore.object(forKey: Constants.showCardOnDateKey) as? Date
}

set {
persistenceStore.set(newValue, forKey: Constants.showCardOnDateKey)
}
}
}

private extension JetpackBrandingMenuCardPresenter {
enum Constants {
static let secondsInDay = 86_400
static let remindLaterDurationInDays = 4
static let shouldHideCardKey = "JetpackBrandingShouldHideCardKey"
static let showCardOnDateKey = "JetpackBrandingShowCardOnDateKey"
}

enum Strings {
static let phaseThreeDescription = NSLocalizedString("jetpack.menuCard.description",
value: "Stats, Reader, Notifications and other features will move to the Jetpack mobile app soon.",
comment: "Description inside a menu card communicating that features are moving to the Jetpack app.")
}
}
Loading