@@ -3,6 +3,7 @@ import UIKit
33import Yosemite
44import enum Alamofire. AFError
55import enum Networking. NetworkError
6+ import class Networking. AlamofireNetwork
67import protocol WooFoundation. Analytics
78
89/// View model for `JetpackSetupView`.
@@ -14,6 +15,7 @@ final class JetpackSetupViewModel: ObservableObject {
1415
1516 private let stores : StoresManager
1617 private let storeNavigationHandler : ( _ connectedEmail: String ? ) -> Void
18+ private let wpcomCredentials : Credentials ?
1719
1820 @Published private( set) var setupSteps : [ JetpackInstallStep ]
1921
@@ -101,12 +103,14 @@ final class JetpackSetupViewModel: ObservableObject {
101103
102104 init ( siteURL: String ,
103105 connectionOnly: Bool ,
106+ wpcomCredentials: Credentials ? ,
104107 stores: StoresManager = ServiceLocator . stores,
105108 analytics: Analytics = ServiceLocator . analytics,
106109 delayBeforeRetry: Double = Constants . delayBeforeRetry,
107110 onStoreNavigation: @escaping ( String ? ) -> Void = { _ in } ) {
108111 self . siteURL = siteURL
109112 self . connectionOnly = connectionOnly
113+ self . wpcomCredentials = wpcomCredentials
110114 self . stores = stores
111115 self . analytics = analytics
112116 self . setupSteps = connectionOnly ? [ . connection, . done] : JetpackInstallStep . allCases
@@ -138,14 +142,14 @@ final class JetpackSetupViewModel: ObservableObject {
138142
139143 func startSetup( ) {
140144 if connectionOnly {
141- fetchJetpackConnectionURL ( )
145+ checkJetpackConnection ( afterConnection : false )
142146 } else {
143147 retrieveJetpackPluginDetails ( )
144148 }
145149 }
146150
147151 func didAuthorizeJetpackConnection( ) {
148- checkJetpackConnection ( )
152+ checkJetpackConnection ( afterConnection : true )
149153 }
150154
151155 func didEncounterErrorDuringConnection( code: Int ? ) {
@@ -217,7 +221,7 @@ private extension JetpackSetupViewModel {
217221 if plugin. status == . inactive {
218222 self . activateJetpack ( )
219223 } else {
220- self . fetchJetpackConnectionURL ( )
224+ self . checkJetpackConnection ( afterConnection : false )
221225 }
222226 case . failure( let error) :
223227 DDLogError ( " ⛔️ Error retrieving Jetpack: \( error) " )
@@ -280,9 +284,10 @@ private extension JetpackSetupViewModel {
280284 stores. dispatch ( action)
281285 }
282286
287+ /// Jetpack connection flow using web view.
288+ /// Used only for sites with Jetpack plugin versions lower than 14.4.
289+ ///
283290 func fetchJetpackConnectionURL( ) {
284- currentSetupStep = . connection
285- trackSetupAfterLogin ( )
286291 let action = JetpackConnectionAction . fetchJetpackConnectionURL { [ weak self] result in
287292 guard let self else { return }
288293 switch result {
@@ -308,50 +313,6 @@ private extension JetpackSetupViewModel {
308313 stores. dispatch ( action)
309314 }
310315
311- func checkJetpackConnection( retryCount: Int = 0 ) {
312- guard retryCount <= Constants . maxRetryCount else {
313- setupFailed = true
314- if let setupError {
315- analytics. track ( . loginJetpackSetupErrorCheckingJetpackConnection, withError: setupError)
316- }
317- return
318- }
319- currentConnectionStep = . inProgress
320- let action = JetpackConnectionAction . fetchJetpackConnectionData { [ weak self] result in
321- guard let self else { return }
322- switch result {
323- case . success( let connectionData) :
324- guard let connectedEmail = connectionData. currentUser. wpcomUser? . email else {
325- DDLogWarn ( " ⚠️ Cannot find connected WPcom user " )
326- let missingWpcomUserError = NSError ( domain: Constants . errorDomain,
327- code: Constants . errorCodeNoWPComUser,
328- userInfo: [ Constants . errorUserInfoReason: Constants . errorUserInfoNoWPComUser] )
329- self . setupError = missingWpcomUserError
330- self . trackSetupDuringLogin ( . loginJetpackSetupCannotFindWPCOMUser, failure: missingWpcomUserError)
331- // Retry fetching user in case Jetpack sync takes some time.
332- DispatchQueue . main. asyncAfter ( deadline: . now( ) + delayBeforeRetry) { [ weak self] in
333- self ? . checkJetpackConnection ( retryCount: retryCount + 1 )
334- }
335- return
336- }
337-
338- self . jetpackConnectedEmail = connectedEmail
339- self . currentConnectionStep = . authorized
340- self . currentSetupStep = . done
341-
342- self . trackSetupDuringLogin ( . loginJetpackSetupAllStepsMarkedDone)
343- self . trackSetupAfterLogin ( )
344- case . failure( let error) :
345- DDLogError ( " ⛔️ Error checking Jetpack connection: \( error) " )
346- self . setupError = error
347- DispatchQueue . main. asyncAfter ( deadline: . now( ) + delayBeforeRetry) { [ weak self] in
348- self ? . checkJetpackConnection ( retryCount: retryCount + 1 )
349- }
350- }
351- }
352- stores. dispatch ( action)
353- }
354-
355316 func updateErrorMessage( ) {
356317 guard let setupErrorCode else {
357318 setupErrorDetail = . init( setupErrorMessage: Localization . genericErrorMessage,
@@ -377,6 +338,156 @@ private extension JetpackSetupViewModel {
377338 }
378339}
379340
341+ // MARK: Handle connection steps
342+ // Ref: pe5sF9-401-p2
343+ private extension JetpackSetupViewModel {
344+ func checkJetpackConnection( afterConnection: Bool , retryCount: Int = 0 ) {
345+ currentSetupStep = . connection
346+ trackSetupAfterLogin ( )
347+ guard retryCount <= Constants . maxRetryCount else {
348+ return didFailJetpackConnection ( )
349+ }
350+ currentConnectionStep = . inProgress
351+ let action = JetpackConnectionAction . fetchJetpackConnectionData { [ weak self] result in
352+ guard let self else { return }
353+ switch result {
354+ case . success( let connectionData) :
355+ if afterConnection {
356+ checkConnectedUser ( data: connectionData, retryCount: retryCount)
357+ } else {
358+ handleJetpackConnectionData ( connectionData)
359+ }
360+ case . failure( let error) :
361+ DDLogError ( " ⛔️ Error checking Jetpack connection: \( error) " )
362+ self . setupError = error
363+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + delayBeforeRetry) { [ weak self] in
364+ self ? . checkJetpackConnection ( afterConnection: afterConnection, retryCount: retryCount + 1 )
365+ }
366+ }
367+ }
368+ stores. dispatch ( action)
369+ }
370+
371+ func checkConnectedUser( data: JetpackConnectionData , retryCount: Int = 0 ) {
372+ let connectedEmail = data. currentUser. wpcomUser? . email
373+ if let connectedEmail {
374+ return didCompleteJetpackConnection ( connectedEmail: connectedEmail)
375+ }
376+
377+ DDLogWarn ( " ⚠️ Cannot find connected WPcom user " )
378+ let missingWpcomUserError = NSError ( domain: Constants . errorDomain,
379+ code: Constants . errorCodeNoWPComUser,
380+ userInfo: [ Constants . errorUserInfoReason: Constants . errorUserInfoNoWPComUser] )
381+ setupError = missingWpcomUserError
382+ trackSetupDuringLogin ( . loginJetpackSetupCannotFindWPCOMUser, failure: missingWpcomUserError)
383+ // Retry fetching user in case Jetpack sync takes some time.
384+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + delayBeforeRetry) { [ weak self] in
385+ self ? . checkJetpackConnection ( afterConnection: true , retryCount: retryCount + 1 )
386+ }
387+ }
388+
389+ func handleJetpackConnectionData( _ data: JetpackConnectionData ) {
390+ if let connectedEmail = data. currentUser. wpcomUser? . email {
391+ return didCompleteJetpackConnection ( connectedEmail: connectedEmail)
392+ }
393+
394+ if let isRegistered = data. isRegistered {
395+ return handleSiteRegisterResult ( isRegistered: isRegistered, blogID: data. blogID)
396+ }
397+
398+ /// Check site info if `isRegistered` is unavailable.
399+ stores. dispatch ( WordPressSiteAction . fetchSiteInfo ( siteURL: siteURL, completion: { [ weak self] result in
400+ guard let self else { return }
401+ switch result {
402+ case . success( let site) :
403+ if site. isJetpackThePluginInstalled {
404+ /// `isRegistered` is unavailable due to outdated Jetpack. Proceed with web flow.
405+ fetchJetpackConnectionURL ( )
406+ } else {
407+ /// For Jetpack-connected sites, `isRegistered` is not returned. Check for `connectionOwner` instead.
408+ handleSiteRegisterResult ( isRegistered: data. connectionOwner != nil , blogID: data. blogID)
409+ }
410+ case . failure( let error) :
411+ DDLogWarn ( " ⛔️ Cannot fetch site info " )
412+ setupError = error
413+ didFailJetpackConnection ( )
414+ }
415+ } ) )
416+ }
417+
418+ func handleSiteRegisterResult( isRegistered: Bool , blogID: Int64 ? ) {
419+ if let blogID, isRegistered {
420+ provisionSiteConnection ( blogID: blogID)
421+ } else {
422+ registerSiteConnection ( )
423+ }
424+ }
425+
426+ func registerSiteConnection( ) {
427+ stores. dispatch ( JetpackConnectionAction . registerSite ( completion: { [ weak self] result in
428+ guard let self else { return }
429+ switch result {
430+ case . success( let blogID) :
431+ provisionSiteConnection ( blogID: blogID)
432+ case . failure( let error) :
433+ setupError = error
434+ didFailJetpackConnection ( )
435+ }
436+ } ) )
437+ }
438+
439+ func provisionSiteConnection( blogID: Int64 ) {
440+ stores. dispatch ( JetpackConnectionAction . provisionConnection ( completion: { [ weak self] result in
441+ guard let self else { return }
442+ switch result {
443+ case . success( let response) :
444+ finalizeSiteConnection ( blogID: blogID, provisionResponse: response)
445+ case . failure( let error) :
446+ setupError = error
447+ didFailJetpackConnection ( )
448+ }
449+ } ) )
450+ }
451+
452+ func finalizeSiteConnection( blogID: Int64 , provisionResponse: JetpackConnectionProvisionResponse ) {
453+ guard let wpcomCredentials else {
454+ return // TODO: what now?
455+ }
456+ let network = AlamofireNetwork ( credentials: wpcomCredentials)
457+ stores. dispatch ( JetpackConnectionAction . finalizeConnection (
458+ siteID: blogID,
459+ siteURL: siteURL,
460+ provisionResponse: provisionResponse,
461+ network: network
462+ ) { [ weak self] result in
463+ guard let self else { return }
464+ switch result {
465+ case . success:
466+ checkJetpackConnection ( afterConnection: true )
467+ case . failure( let error) :
468+ setupError = error
469+ didFailJetpackConnection ( )
470+ }
471+ } )
472+ }
473+
474+ func didCompleteJetpackConnection( connectedEmail: String ) {
475+ jetpackConnectedEmail = connectedEmail
476+ currentConnectionStep = . authorized
477+ currentSetupStep = . done
478+
479+ trackSetupDuringLogin ( . loginJetpackSetupAllStepsMarkedDone)
480+ trackSetupAfterLogin ( )
481+ }
482+
483+ func didFailJetpackConnection( ) {
484+ setupFailed = true
485+ if let setupError {
486+ analytics. track ( . loginJetpackSetupErrorCheckingJetpackConnection, withError: setupError)
487+ }
488+ }
489+ }
490+
380491// MARK: Subtypes
381492//
382493extension JetpackSetupViewModel {
0 commit comments