Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
e94ec28
Add WordPress to queryable schemes.
twstokes Nov 24, 2022
127cd61
Add migration helper utility class.
twstokes Nov 24, 2022
7d6ddd0
Add import logic to `JetpackWindowManager`
alpavanoglu Nov 28, 2022
494d4c6
Remove obsolete comment
alpavanoglu Nov 28, 2022
dcb0b72
Merge remote-tracking branch 'origin/trunk' into task/jp-migration-de…
twstokes Nov 28, 2022
05aec45
Move app detection to an extension of UIApplication.
twstokes Nov 28, 2022
11c14d7
Move app availability extension to global classes.
twstokes Nov 28, 2022
2aa95bb
Add detection for migratable WordPress version.
twstokes Nov 28, 2022
c37b765
Add function to detect state of WordPress app installation.
twstokes Nov 29, 2022
dff5e1e
Add `showAppUI` call for the else case
alpavanoglu Nov 29, 2022
312cf8d
Move local drafts check to ContentMigrationCoordinator
dvdchr Nov 29, 2022
28719bf
Add return call to guard
alpavanoglu Nov 29, 2022
674b23c
Add AppDelegate import & migrate logic
alpavanoglu Nov 29, 2022
1968534
Update `showAppUI` call as `showSignInUI` when user is not logged in
alpavanoglu Nov 29, 2022
7bdefb8
Merge pull request #19681 from wordpress-mobile/feature/19631-preflig…
dvdchr Nov 29, 2022
08c0b0d
Add if-jetpack directive to AppDelegate
alpavanoglu Nov 29, 2022
e8a8281
Merge pull request #19655 from wordpress-mobile/task/jp-migration-det…
twstokes Nov 29, 2022
1f237b3
Merge pull request #19680 from wordpress-mobile/task/jp-migration-det…
twstokes Nov 29, 2022
6731d38
Check all posts remote status instead of just drafts
wargcm Nov 29, 2022
794a496
Merge pull request #19676 from wordpress-mobile/task/jp-unsync-user-d…
alpavanoglu Nov 30, 2022
3379a29
Release script: Update gutenberg-mobile ref
SiobhyB Nov 30, 2022
716f973
Update reference to Gutenberg Mobile
SiobhyB Nov 30, 2022
7029733
Merge pull request #19684 from wordpress-mobile/task/19631-check-loca…
wargcm Nov 30, 2022
eb59482
Update MigrationAppDetection, add tracks for migratable and not migra…
Nov 30, 2022
8928cb6
Update MigratableStateTracker, add private access to eventName
Nov 30, 2022
b3462ed
Merge pull request #19692 from wordpress-mobile/task/19682-jp-migrati…
Gio2018 Nov 30, 2022
aa88d43
[Jetpack Content Migration] Send Migration Email (#19690)
salimbraksa Nov 30, 2022
5018286
Update Gutenberg Mobile to v1.85.1
jhnstn Nov 30, 2022
b5dfbd5
Merge pull request #19689 from wordpress-mobile/gutenberg/integrate_r…
derekblank Dec 1, 2022
a3ad006
Bump version number
oguzkocer Dec 1, 2022
0c76437
Merge remote-tracking branch 'origin/release/21.3' into merge/21.2.0.…
oguzkocer Dec 1, 2022
07f0356
Merge pull request #19695 from wordpress-mobile/merge/21.2.0.4-to-rel…
oguzkocer Dec 1, 2022
57e992c
Avoid turning off WordPress notifications when turning app during the…
staskus Dec 1, 2022
50ccd96
Add editorialized release notes for version 21.3
oguzkocer Dec 1, 2022
abba7d1
Update metadata strings
oguzkocer Dec 1, 2022
6e73a33
Update metadata strings
oguzkocer Dec 1, 2022
b38bd88
Fix a bug that was causing the log out not to work when in the migrat…
salimbraksa Dec 1, 2022
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
2 changes: 1 addition & 1 deletion Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ abstract_target 'Apps' do
## Gutenberg (React Native)
## =====================
##
gutenberg tag: 'v1.85.0'
gutenberg tag: 'v1.85.1'

## Third party libraries
## =====================
Expand Down
202 changes: 101 additions & 101 deletions Podfile.lock

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions WordPress/Classes/Extensions/UIApplication+AppAvailability.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation

enum AppScheme: String {
case wordpress = "wordpress://"
case wordpressMigrationV1 = "wordpressmigration+v1://"
}

extension UIApplication {
func canOpen(app: AppScheme) -> Bool {
guard let url = URL(string: app.rawValue) else {
return false
}
return canOpenURL(url)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ protocol JetpackNotificationMigrationServiceProtocol {
final class JetpackNotificationMigrationService: JetpackNotificationMigrationServiceProtocol {
private let remoteNotificationRegister: RemoteNotificationRegister
private let featureFlagStore: RemoteFeatureFlagStore
private let userDefaults: UserDefaults
private let isWordPress: Bool

static let shared = JetpackNotificationMigrationService()

static let wordPressScheme = "wordpressnotificationmigration"
static let jetpackScheme = "jetpacknotificationmigration"
private let wordPressNotificationsToggledDefaultsKey = "wordPressNotificationsToggledDefaultsKey"
private let jetpackNotificationMigrationDefaultsKey = "jetpackNotificationMigrationDefaultsKey"

private var jetpackMigrationPreventDuplicateNotifications: Bool {
Expand All @@ -38,6 +40,8 @@ final class JetpackNotificationMigrationService: JetpackNotificationMigrationSer
}

set {
userDefaults.set(true, forKey: wordPressNotificationsToggledDefaultsKey)

if newValue, isWordPress {
remoteNotificationRegister.registerForRemoteNotifications()
rescheduleLocalNotifications()
Expand All @@ -63,18 +67,20 @@ final class JetpackNotificationMigrationService: JetpackNotificationMigrationSer
/// disableWordPressNotificationsFromJetpack may get triggered multiple times from Jetpack app but it only needs to be executed the first time
private var isMigrationDone: Bool {
get {
return UserDefaults.standard.bool(forKey: jetpackNotificationMigrationDefaultsKey)
return userDefaults.bool(forKey: jetpackNotificationMigrationDefaultsKey)
}
set {
UserDefaults.standard.setValue(newValue, forKey: jetpackNotificationMigrationDefaultsKey)
userDefaults.setValue(newValue, forKey: jetpackNotificationMigrationDefaultsKey)
}
}

init(remoteNotificationRegister: RemoteNotificationRegister = UIApplication.shared,
featureFlagStore: RemoteFeatureFlagStore = RemoteFeatureFlagStore(),
userDefaults: UserDefaults = .standard,
isWordPress: Bool = AppConfiguration.isWordPress) {
self.remoteNotificationRegister = remoteNotificationRegister
self.featureFlagStore = featureFlagStore
self.userDefaults = userDefaults
self.isWordPress = isWordPress
}

Expand All @@ -85,6 +91,7 @@ final class JetpackNotificationMigrationService: JetpackNotificationMigrationSer
func shouldPresentNotifications() -> Bool {
let disableNotifications = jetpackMigrationPreventDuplicateNotifications
&& isWordPress
&& userDefaults.bool(forKey: wordPressNotificationsToggledDefaultsKey)
&& !wordPressNotificationsEnabled

if disableNotifications {
Expand Down
10 changes: 10 additions & 0 deletions WordPress/Classes/Stores/UserPersistentRepositoryUtility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ private enum UPRUConstants {
static let currentAnnouncementsKey = "currentAnnouncements"
static let currentAnnouncementsDateKey = "currentAnnouncementsDate"
static let announcementsVersionDisplayedKey = "announcementsVersionDisplayed"
static let isJPContentImportCompleteKey = "jetpackContentImportComplete"
}

protocol UserPersistentRepositoryUtility: AnyObject {
Expand Down Expand Up @@ -164,4 +165,13 @@ extension UserPersistentRepositoryUtility {
UserPersistentStoreFactory.instance().set(newValue, forKey: UPRUConstants.announcementsVersionDisplayedKey)
}
}

var isJPContentImportComplete: Bool {
get {
return UserPersistentStoreFactory.instance().bool(forKey: UPRUConstants.isJPContentImportCompleteKey)
}
set {
UserPersistentStoreFactory.instance().set(newValue, forKey: UPRUConstants.isJPContentImportCompleteKey)
}
}
}
7 changes: 7 additions & 0 deletions WordPress/Classes/System/WordPressAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,13 @@ class WordPressAppDelegate: UIResponder, UIApplicationDelegate {
uploadsManager.resume()
updateFeatureFlags()
updateRemoteConfig()

#if JETPACK
if let windowManager = windowManager as? JetpackWindowManager,
windowManager.shouldImportMigrationData {
windowManager.importAndShowMigrationContent(nil, failureCompletion: nil)
}
#endif
}

func applicationWillResignActive(_ application: UIApplication) {
Expand Down
14 changes: 14 additions & 0 deletions WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,11 @@ import Foundation
case jetpackSiteCreationOverlayButtonTapped
case jetpackSiteCreationOverlayDismissed

// WordPress to Jetpack Migration
case migrationEmailTriggered
case migrationEmailSent
case migrationEmailFailed

/// A String that represents the event
var value: String {
switch self {
Expand Down Expand Up @@ -1123,6 +1128,15 @@ import Foundation
return "remove_site_creation_overlay_button_tapped"
case .jetpackSiteCreationOverlayDismissed:
return "remove_site_creation_overlay_dismissed"

// WordPress to Jetpack Migration
case .migrationEmailTriggered:
return "migration_email_triggered"
case .migrationEmailSent:
return "migration_email_sent"
case .migrationEmailFailed:
return "migration_email_failed"

} // END OF SWITCH
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ class ContentMigrationCoordinator {

// MARK: Dependencies

private let coreDataStack: CoreDataStack
private let dataMigrator: ContentDataMigrating
private let userPersistentRepository: UserPersistentRepository
private let eligibilityProvider: ContentMigrationEligibilityProvider

init(dataMigrator: ContentDataMigrating = DataMigrator(),
init(coreDataStack: CoreDataStack = ContextManager.shared,
dataMigrator: ContentDataMigrating = DataMigrator(),
userPersistentRepository: UserPersistentRepository = UserDefaults.standard,
eligibilityProvider: ContentMigrationEligibilityProvider = AppConfiguration()) {
self.coreDataStack = coreDataStack
self.dataMigrator = dataMigrator
self.userPersistentRepository = userPersistentRepository
self.eligibilityProvider = eligibilityProvider
Expand All @@ -23,6 +26,7 @@ class ContentMigrationCoordinator {
enum ContentMigrationCoordinatorError: Error {
case ineligible
case exportFailure
case localDraftsNotSynced
}

// MARK: Methods
Expand All @@ -41,7 +45,10 @@ class ContentMigrationCoordinator {
return
}

// TODO: Sync local post drafts here.
guard isLocalPostsSynced() else {
completion?(.failure(.localDraftsNotSynced))
return
}

dataMigrator.exportData { result in
switch result {
Expand Down Expand Up @@ -78,6 +85,26 @@ class ContentMigrationCoordinator {
}
}

// MARK: - Preflights Local Draft Check

private extension ContentMigrationCoordinator {

func isLocalPostsSynced() -> Bool {
let fetchRequest = NSFetchRequest<Post>(entityName: String(describing: Post.self))
fetchRequest.predicate = NSPredicate(format: "remoteStatusNumber = %@ || remoteStatusNumber = %@ || remoteStatusNumber = %@ || remoteStatusNumber = %@",
NSNumber(value: AbstractPostRemoteStatus.pushing.rawValue),
NSNumber(value: AbstractPostRemoteStatus.failed.rawValue),
NSNumber(value: AbstractPostRemoteStatus.local.rawValue),
NSNumber(value: AbstractPostRemoteStatus.pushingMedia.rawValue))
guard let count = try? coreDataStack.mainContext.count(for: fetchRequest) else {
return false
}

return count == 0
}

}

// MARK: - Content Migration Eligibility Provider

protocol ContentMigrationEligibilityProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ import UIKit

struct LogOutActionHandler {

private weak var windowManager: WindowManager?

init(windowManager: WindowManager? = WordPressAppDelegate.shared?.windowManager) {
self.windowManager = windowManager
}

func logOut(with viewController: UIViewController) {
let alert = UIAlertController(title: logOutAlertTitle, message: nil, preferredStyle: .alert)
alert.addActionWithTitle(Strings.alertCancelAction, style: .cancel)
alert.addActionWithTitle(Strings.alertLogoutAction, style: .destructive) { [weak viewController] _ in
viewController?.dismiss(animated: true) {
AccountHelper.logOutDefaultWordPressComAccount()
windowManager?.showSignInUI()
}
}
viewController.present(alert, animated: true)
Expand Down
1 change: 1 addition & 0 deletions WordPress/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@
<array>
<string>wordpress-oauth-v2</string>
<string>${WPCOM_SCHEME}</string>
<string>wordpressmigration+v1</string>
<string>wordpressnotificationmigration</string>
</array>
</dict>
Expand Down
50 changes: 43 additions & 7 deletions WordPress/Jetpack/Classes/System/JetpackWindowManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,62 @@ class JetpackWindowManager: WindowManager {
/// receives migration flow updates in order to dismiss it when needed.
private var cancellable: AnyCancellable?

var shouldImportMigrationData: Bool {
return !AccountHelper.isLoggedIn && !UserPersistentStoreFactory.instance().isJPContentImportComplete
}

override func showUI(for blog: Blog?) {
// If the user is logged in and has blogs sync'd to their account
if AccountHelper.isLoggedIn && AccountHelper.hasBlogs {
shouldShowMigrationUI ? showMigrationUI(blog) : showAppUI(for: blog)
showAppUI(for: blog)
return
}

// Show the sign in UI if the user isn't logged in
guard AccountHelper.isLoggedIn else {
showSignInUI()
if shouldImportMigrationData {
importAndShowMigrationContent(blog) { [weak self] in
self?.showSignInUI()
}
} else {
showSignInUI()
}
return
}

// If the user doesn't have any blogs, but they're still logged in, log them out
// the `logOutDefaultWordPressComAccount` method will trigger the `showSignInUI` automatically
AccountHelper.logOutDefaultWordPressComAccount()
}

private func showMigrationUI(_ blog: Blog?) {
func importAndShowMigrationContent(_ blog: Blog?, failureCompletion: (() -> ())?) {
DataMigrator().importData() { [weak self] result in
guard let self else {
return
}

switch result {
case .success:
UserPersistentStoreFactory.instance().isJPContentImportComplete = true
NotificationCenter.default.post(name: .WPAccountDefaultWordPressComAccountChanged, object: nil)
self.showMigrationUIIfNeeded(blog)
self.sendMigrationEmail()
case .failure:
failureCompletion?()
}
}
}

private func sendMigrationEmail() {
Task {
let service = try? MigrationEmailService()
try? await service?.sendMigrationEmail()
}
}

private func showMigrationUIIfNeeded(_ blog: Blog?) {
guard shouldShowMigrationUI else {
return
}

let container = MigrationDependencyContainer()
cancellable = container.migrationCoordinator.$currentStep
.receive(on: DispatchQueue.main)
Expand All @@ -41,8 +78,7 @@ class JetpackWindowManager: WindowManager {
showAppUI(for: blog)
}

// TODO: Add logic in here to trigger migration UI if needed
private var shouldShowMigrationUI: Bool {
return FeatureFlag.contentMigration.enabled
return FeatureFlag.contentMigration.enabled && AccountHelper.isLoggedIn
}
}
20 changes: 0 additions & 20 deletions WordPress/Jetpack/Classes/Utility/DataMigrator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ protocol ContentDataMigrating {
}

enum DataMigrationError: Error {
case localDraftsNotSynced
case databaseCopyError
case sharedUserDefaultsNil
}
Expand Down Expand Up @@ -81,10 +80,6 @@ final class DataMigrator {
extension DataMigrator: ContentDataMigrating {

func exportData(completion: ((Result<Void, DataMigrationError>) -> Void)? = nil) {
guard isLocalDraftsSynced() else {
completion?(.failure(.localDraftsNotSynced))
return
}
guard let backupLocation, copyDatabase(to: backupLocation) else {
completion?(.failure(.databaseCopyError))
return
Expand Down Expand Up @@ -119,21 +114,6 @@ extension DataMigrator: ContentDataMigrating {

private extension DataMigrator {

func isLocalDraftsSynced() -> Bool {
let fetchRequest = NSFetchRequest<Post>(entityName: String(describing: Post.self))
fetchRequest.predicate = NSPredicate(format: "status = %@ && (remoteStatusNumber = %@ || remoteStatusNumber = %@ || remoteStatusNumber = %@ || remoteStatusNumber = %@)",
BasePost.Status.draft.rawValue,
NSNumber(value: AbstractPostRemoteStatus.pushing.rawValue),
NSNumber(value: AbstractPostRemoteStatus.failed.rawValue),
NSNumber(value: AbstractPostRemoteStatus.local.rawValue),
NSNumber(value: AbstractPostRemoteStatus.pushingMedia.rawValue))
guard let count = try? coreDataStack.mainContext.count(for: fetchRequest) else {
return false
}

return count == 0
}

func copyDatabase(to destination: URL) -> Bool {
do {
try coreDataStack.createStoreCopy(to: destination)
Expand Down
Loading