Skip to content

Commit 2d53260

Browse files
authored
Merge pull request #17429 from wordpress-mobile/feature/land-in-the-editor
A/B test landing in the editor
2 parents 80bc0a1 + bbcdd44 commit 2d53260

16 files changed

+285
-33
lines changed

Podfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ workspace 'WordPress.xcworkspace'
2020
## ===================================
2121
##
2222
def wordpress_shared
23-
pod 'WordPressShared', '~> 1.17.0'
23+
pod 'WordPressShared', '~> 1.17.1-beta.1'
2424
#pod 'WordPressShared', :git => 'https://github.com/wordpress-mobile/WordPress-iOS-Shared.git', :tag => ''
2525
#pod 'WordPressShared', :git => 'https://github.com/wordpress-mobile/WordPress-iOS-Shared.git', :branch => ''
2626
#pod 'WordPressShared', :git => 'https://github.com/wordpress-mobile/WordPress-iOS-Shared.git', :commit => ''

Podfile.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ PODS:
471471
- WordPressShared (~> 1.15-beta)
472472
- wpxmlrpc (~> 0.9)
473473
- WordPressMocks (0.0.15)
474-
- WordPressShared (1.17.0):
474+
- WordPressShared (1.17.1-beta.1):
475475
- CocoaLumberjack (~> 3.4)
476476
- FormatterKit/TimeIntervalFormatter (~> 1.8)
477477
- WordPressUI (1.12.3)
@@ -569,7 +569,7 @@ DEPENDENCIES:
569569
- WordPressAuthenticator (~> 1.43.0)
570570
- WordPressKit (~> 4.47.0-beta.1)
571571
- WordPressMocks (~> 0.0.15)
572-
- WordPressShared (~> 1.17.0)
572+
- WordPressShared (~> 1.17.1-beta.1)
573573
- WordPressUI (~> 1.12.3)
574574
- WPMediaPicker (~> 1.8.2)
575575
- Yoga (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.70.0/third-party-podspecs/Yoga.podspec.json`)
@@ -579,6 +579,7 @@ DEPENDENCIES:
579579
SPEC REPOS:
580580
https://github.com/wordpress-mobile/cocoapods-specs.git:
581581
- WordPressAuthenticator
582+
- WordPressShared
582583
trunk:
583584
- Alamofire
584585
- AlamofireImage
@@ -619,7 +620,6 @@ SPEC REPOS:
619620
- WordPress-Editor-iOS
620621
- WordPressKit
621622
- WordPressMocks
622-
- WordPressShared
623623
- WordPressUI
624624
- WPMediaPicker
625625
- wpxmlrpc
@@ -833,7 +833,7 @@ SPEC CHECKSUMS:
833833
WordPressAuthenticator: e4a43745a647e4dc4b35d21b1353bc4f07f9f3c9
834834
WordPressKit: 375f54b3b27bf37f8fc5ed444afd0507fe48a2c7
835835
WordPressMocks: 6b52b0764d9939408151367dd9c6e8a910877f4d
836-
WordPressShared: a4b0308a6345d4dda20c8f7ad9317df4246b4a00
836+
WordPressShared: bc50e38a31e05134a7c643cef9cf6a7e28e9cf34
837837
WordPressUI: 45591178e843ecb82e65e868ec766148befe9f9f
838838
WPMediaPicker: a34b708a7b995a0889bd5098e3b79f4a50a7a9bd
839839
wpxmlrpc: bf55a43a7e710bd2a4fb8c02dfe83b1246f14f13
@@ -847,6 +847,6 @@ SPEC CHECKSUMS:
847847
ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba
848848
ZIPFoundation: e27423c004a5a1410c15933407747374e7c6cb6e
849849

850-
PODFILE CHECKSUM: b10963dbcbffa2add515936104b784dcc64f8ad3
850+
PODFILE CHECKSUM: c9667ea89c8f053063df53439705577c83f22e17
851851

852852
COCOAPODS: 1.11.2

WordPress/Classes/Models/Blog+HomepageSettings.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,15 @@ extension Blog {
9999
setValue(number as Any, forOption: OptionsKeys.homepageID)
100100
}
101101
}
102+
103+
/// Getter which returns the current homepage (or nil)
104+
/// Note: It seems to be necessary to first sync pages (otherwise the `findPost` result fails to cast to `Page`)
105+
var homepage: Page? {
106+
guard let pageID = homepageType == .page ? homepagePageID
107+
: homepageType == .posts ? homepagePostsPageID
108+
: nil else { return nil }
109+
let context = ContextManager.sharedInstance().mainContext
110+
let postService = PostService(managedObjectContext: context)
111+
return postService.findPost(withID: NSNumber(value: pageID), in: self) as? Page
112+
}
102113
}

WordPress/Classes/Utility/AB Testing/ABTest.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import AutomatticTracks
22

33
enum ABTest: String, CaseIterable {
44
case unknown = "unknown"
5+
case landInTheEditorPhase1 = "wpios_land_in_the_editor_phase1_v3"
56

67
/// Returns a variation for the given experiment
78
var variation: Variation {

WordPress/Classes/Utility/Analytics/WPAnalyticsTrackerAutomatticTracks.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,9 @@ + (TracksEventPair *)eventPairForStat:(WPAnalyticsStat)stat
790790
case WPAnalyticsStatInstallJetpackWebviewFailed:
791791
eventName = @"connect_jetpack_failed";
792792
break;
793+
case WPAnalyticsStatLandingEditorShown:
794+
eventName = @"landing_editor_shown";
795+
break;
793796
case WPAnalyticsStatLayoutPickerPreviewErrorShown:
794797
eventName = @"layout_picker_preview_error_shown";
795798
break;

WordPress/Classes/Utility/Editor/EditorFactory.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class EditorFactory {
2020
}
2121
}
2222

23-
private func createGutenbergVC(with post: AbstractPost, loadAutosaveRevision: Bool, replaceEditor: @escaping ReplaceEditorBlock) -> GutenbergViewController {
23+
func createGutenbergVC(with post: AbstractPost, loadAutosaveRevision: Bool, replaceEditor: @escaping ReplaceEditorBlock) -> GutenbergViewController {
2424
let gutenbergVC = GutenbergViewController(post: post, loadAutosaveRevision: loadAutosaveRevision, replaceEditor: replaceEditor)
2525

2626
if gutenbergSettings.shouldAutoenableGutenberg(for: post) {
@@ -33,6 +33,19 @@ class EditorFactory {
3333
return gutenbergVC
3434
}
3535

36+
// TODO: DRY this up
37+
func createHomepageGutenbergVC(with post: AbstractPost, loadAutosaveRevision: Bool, replaceEditor: @escaping ReplaceEditorBlock) -> EditHomepageViewController {
38+
let gutenbergVC = EditHomepageViewController(post: post, loadAutosaveRevision: loadAutosaveRevision, replaceEditor: replaceEditor)
39+
40+
if gutenbergSettings.shouldAutoenableGutenberg(for: post) {
41+
gutenbergSettings.setGutenbergEnabled(true, for: post.blog, source: .onBlockPostOpening)
42+
gutenbergSettings.postSettingsToRemote(for: post.blog)
43+
gutenbergVC.shouldPresentInformativeDialog = true
44+
gutenbergSettings.willShowDialog(for: post.blog)
45+
}
46+
47+
return gutenbergVC
48+
}
3649
func switchToGutenberg(from source: EditorViewController) {
3750
let replacement = GutenbergViewController(post: source.post, replaceEditor: source.replaceEditor, editorSession: source.editorSession)
3851
source.replaceEditor(source, replacement)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import Foundation
2+
3+
class EditHomepageViewController: GutenbergViewController {
4+
required init(
5+
post: AbstractPost,
6+
loadAutosaveRevision: Bool = false,
7+
replaceEditor: @escaping ReplaceEditorCallback,
8+
editorSession: PostEditorAnalyticsSession? = nil,
9+
navigationBarManager: PostEditorNavigationBarManager? = nil
10+
) {
11+
let navigationBarManager = navigationBarManager ?? HomepageEditorNavigationBarManager()
12+
super.init(post: post, loadAutosaveRevision: loadAutosaveRevision, replaceEditor: replaceEditor, editorSession: editorSession, navigationBarManager: navigationBarManager)
13+
}
14+
15+
required init?(coder aDecoder: NSCoder) {
16+
fatalError("init(coder:) has not been implemented")
17+
}
18+
19+
private(set) lazy var homepageEditorStateContext: PostEditorStateContext = {
20+
return PostEditorStateContext(post: post, delegate: self, action: .continueFromHomepageEditing)
21+
}()
22+
23+
override var postEditorStateContext: PostEditorStateContext {
24+
return homepageEditorStateContext
25+
}
26+
27+
// If there are changes, offer to save them, otherwise continue will dismiss the editor with no changes.
28+
override func continueFromHomepageEditing() {
29+
if editorHasChanges {
30+
handlePublishButtonTap()
31+
} else {
32+
cancelEditing()
33+
}
34+
}
35+
}
36+
37+
extension EditHomepageViewController: HomepageEditorNavigationBarManagerDelegate {
38+
var continueButtonText: String {
39+
return postEditorStateContext.publishButtonText
40+
}
41+
42+
func navigationBarManager(_ manager: HomepageEditorNavigationBarManager, continueWasPressed sender: UIButton) {
43+
requestHTML(for: .continueFromHomepageEditing)
44+
}
45+
}

WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import WordPressFlux
66
import Kanvas
77

88
class GutenbergViewController: UIViewController, PostEditor, FeaturedImageDelegate {
9-
109
let errorDomain: String = "GutenbergViewController.errorDomain"
1110

1211
enum RequestHTMLReason {
@@ -15,6 +14,7 @@ class GutenbergViewController: UIViewController, PostEditor, FeaturedImageDelega
1514
case more
1615
case switchBlog
1716
case autoSave
17+
case continueFromHomepageEditing
1818
}
1919

2020
private lazy var stockPhotos: GutenbergStockPhotos = {
@@ -223,7 +223,7 @@ class GutenbergViewController: UIViewController, PostEditor, FeaturedImageDelega
223223
///
224224
var loadAutosaveRevision: Bool
225225

226-
let navigationBarManager = PostEditorNavigationBarManager()
226+
let navigationBarManager: PostEditorNavigationBarManager
227227

228228
lazy var attachmentDelegate = AztecAttachmentDelegate(post: post)
229229

@@ -299,25 +299,32 @@ class GutenbergViewController: UIViewController, PostEditor, FeaturedImageDelega
299299
}()
300300

301301
// MARK: - Initializers
302+
required convenience init(post: AbstractPost, loadAutosaveRevision: Bool, replaceEditor: @escaping ReplaceEditorCallback, editorSession: PostEditorAnalyticsSession?) {
303+
self.init(post: post, loadAutosaveRevision: loadAutosaveRevision, replaceEditor: replaceEditor, editorSession: editorSession)
304+
}
305+
302306
required init(
303307
post: AbstractPost,
304308
loadAutosaveRevision: Bool = false,
305-
replaceEditor: @escaping (EditorViewController, EditorViewController) -> (),
306-
editorSession: PostEditorAnalyticsSession? = nil) {
309+
replaceEditor: @escaping ReplaceEditorCallback,
310+
editorSession: PostEditorAnalyticsSession? = nil,
311+
navigationBarManager: PostEditorNavigationBarManager? = nil
312+
) {
307313

308314
self.post = post
309315
self.loadAutosaveRevision = loadAutosaveRevision
310316

311317
self.replaceEditor = replaceEditor
312318
verificationPromptHelper = AztecVerificationPromptHelper(account: self.post.blog.account)
313319
self.editorSession = PostEditorAnalyticsSession(editor: .gutenberg, post: post)
320+
self.navigationBarManager = navigationBarManager ?? PostEditorNavigationBarManager()
314321

315322
super.init(nibName: nil, bundle: nil)
316323

317324
addObservers(toPost: post)
318325

319326
PostCoordinator.shared.cancelAnyPendingSaveOf(post: post)
320-
navigationBarManager.delegate = self
327+
self.navigationBarManager.delegate = self
321328
}
322329

323330
required init?(coder aDecoder: NSCoder) {
@@ -882,10 +889,19 @@ extension GutenbergViewController: GutenbergBridgeDelegate {
882889
blogPickerWasPressed()
883890
case .autoSave:
884891
break
892+
// Inelegant :(
893+
case .continueFromHomepageEditing:
894+
continueFromHomepageEditing()
895+
break
885896
}
886897
}
887898
}
888899

900+
// Not ideal, but seems the least bad of the alternatives
901+
@objc func continueFromHomepageEditing() {
902+
fatalError("This method must be overriden by the extending class")
903+
}
904+
889905
func gutenbergDidLayout() {
890906
defer {
891907
isFirstGutenbergLayout = false

WordPress/Classes/ViewRelated/Pages/EditPageViewController.swift

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,19 @@ class EditPageViewController: UIViewController {
66
fileprivate var postTitle: String?
77
fileprivate var content: String?
88
fileprivate var hasShownEditor = false
9+
fileprivate var isHomePageEditor = false
10+
private var homepageEditorCompletion: HomepageEditorCompletion?
911

1012
convenience init(page: Page) {
1113
self.init(page: page, blog: page.blog, postTitle: nil, content: nil, appliedTemplate: nil)
1214
}
1315

16+
convenience init(homepage: Page, completion: @escaping HomepageEditorCompletion) {
17+
self.init(page: homepage)
18+
isHomePageEditor = true
19+
homepageEditorCompletion = completion
20+
}
21+
1422
convenience init(blog: Blog, postTitle: String?, content: String?, appliedTemplate: String?) {
1523
self.init(page: nil, blog: blog, postTitle: postTitle, content: content, appliedTemplate: appliedTemplate)
1624
}
@@ -35,7 +43,11 @@ class EditPageViewController: UIViewController {
3543
override func viewDidAppear(_ animated: Bool) {
3644
super.viewDidAppear(animated)
3745
if !hasShownEditor {
38-
showEditor()
46+
if isHomePageEditor {
47+
showHomepageEditor()
48+
} else {
49+
showEditor()
50+
}
3951
hasShownEditor = true
4052
}
4153
}
@@ -58,6 +70,15 @@ class EditPageViewController: UIViewController {
5870
}
5971
}
6072

73+
fileprivate func showHomepageEditor() {
74+
let editorFactory = EditorFactory()
75+
let gutenbergVC = editorFactory.createHomepageGutenbergVC(with: self.pageToEdit(), loadAutosaveRevision: false, replaceEditor: { [weak self] (editor, replacement) in
76+
self?.replaceEditor(editor: editor, replacement: replacement)
77+
})
78+
79+
show(gutenbergVC)
80+
}
81+
6182
fileprivate func showEditor() {
6283
let editorFactory = EditorFactory()
6384

@@ -75,7 +96,10 @@ class EditPageViewController: UIViewController {
7596
// Dismiss navigation controller
7697
self?.dismiss(animated: true) {
7798
// Dismiss self
78-
self?.dismiss(animated: false)
99+
self?.dismiss(animated: false) {
100+
// Invoke completion
101+
self?.homepageEditorCompletion?()
102+
}
79103
}
80104
}
81105

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import Foundation
2+
3+
protocol HomepageEditorNavigationBarManagerDelegate: PostEditorNavigationBarManagerDelegate {
4+
var continueButtonText: String { get }
5+
6+
func navigationBarManager(_ manager: HomepageEditorNavigationBarManager, continueWasPressed sender: UIButton)
7+
}
8+
9+
class HomepageEditorNavigationBarManager: PostEditorNavigationBarManager {
10+
weak var homepageEditorNavigationBarManagerDelegate: HomepageEditorNavigationBarManagerDelegate?
11+
12+
override weak var delegate: PostEditorNavigationBarManagerDelegate? {
13+
get {
14+
return homepageEditorNavigationBarManagerDelegate
15+
}
16+
set {
17+
if let newDelegate = newValue {
18+
if let newHomepageDelegate = newDelegate as? HomepageEditorNavigationBarManagerDelegate {
19+
homepageEditorNavigationBarManagerDelegate = newHomepageDelegate
20+
} else {
21+
// This should not happen, but fail fast in case
22+
fatalError("homepageEditorNavigationBarManagerDelegate must be of type HomepageEditorNavigationBarManagerDelegate?")
23+
}
24+
} else {
25+
homepageEditorNavigationBarManagerDelegate = nil
26+
}
27+
}
28+
}
29+
30+
/// Continue Button
31+
private(set) lazy var continueButton: UIButton = {
32+
let button = UIButton(type: .system)
33+
button.addTarget(self, action: #selector(continueButtonTapped(sender:)), for: .touchUpInside)
34+
button.setTitle(homepageEditorNavigationBarManagerDelegate?.continueButtonText ?? "", for: .normal)
35+
button.sizeToFit()
36+
button.isEnabled = true
37+
button.setContentHuggingPriority(.required, for: .horizontal)
38+
return button
39+
}()
40+
41+
/// Continue Button
42+
private(set) lazy var continueBarButtonItem: UIBarButtonItem = {
43+
let button = UIBarButtonItem(customView: self.continueButton)
44+
return button
45+
}()
46+
47+
@objc private func continueButtonTapped(sender: UIButton) {
48+
homepageEditorNavigationBarManagerDelegate?.navigationBarManager(self, continueWasPressed: sender)
49+
}
50+
51+
override var leftBarButtonItems: [UIBarButtonItem] {
52+
return []
53+
}
54+
55+
override var rightBarButtonItems: [UIBarButtonItem] {
56+
return [moreBarButtonItem, continueBarButtonItem, separatorButtonItem]
57+
}
58+
}

0 commit comments

Comments
 (0)