Skip to content

Commit 636023e

Browse files
authored
Merge pull request #19712 from wordpress-mobile/release/21.3
Merge 21.3.0.1 to trunk
2 parents ed74281 + 8aff3df commit 636023e

File tree

101 files changed

+5534
-3354
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+5534
-3354
lines changed

Gemfile.lock

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -188,15 +188,14 @@ GEM
188188
xcpretty-travis-formatter (>= 0.0.3)
189189
fastlane-plugin-appcenter (1.11.1)
190190
fastlane-plugin-sentry (1.11.0)
191-
fastlane-plugin-wpmreleasetoolkit (6.0.0)
191+
fastlane-plugin-wpmreleasetoolkit (6.1.0)
192192
activesupport (~> 5)
193193
bigdecimal (~> 1.4)
194194
buildkit (~> 1.5)
195195
chroma (= 0.2.0)
196196
diffy (~> 3.3)
197197
git (~> 1.3)
198198
google-cloud-storage (~> 1.31)
199-
jsonlint (~> 0.3)
200199
nokogiri (~> 1.11)
201200
octokit (~> 4.18)
202201
parallel (~> 1.14)
@@ -213,7 +212,7 @@ GEM
213212
rchardet (~> 1.8)
214213
google-apis-androidpublisher_v3 (0.26.0)
215214
google-apis-core (>= 0.7, < 2.a)
216-
google-apis-core (0.9.0)
215+
google-apis-core (0.9.1)
217216
addressable (~> 2.5, >= 2.5.1)
218217
googleauth (>= 0.16.2, < 2.a)
219218
httpclient (>= 2.8.1, < 3.a)
@@ -222,8 +221,8 @@ GEM
222221
retriable (>= 2.0, < 4.a)
223222
rexml
224223
webrick
225-
google-apis-iamcredentials_v1 (0.15.0)
226-
google-apis-core (>= 0.9.0, < 2.a)
224+
google-apis-iamcredentials_v1 (0.16.0)
225+
google-apis-core (>= 0.9.1, < 2.a)
227226
google-apis-playcustomapp_v1 (0.10.0)
228227
google-apis-core (>= 0.7, < 2.a)
229228
google-apis-storage_v1 (0.19.0)
@@ -234,15 +233,15 @@ GEM
234233
google-cloud-env (1.6.0)
235234
faraday (>= 0.17.3, < 3.0)
236235
google-cloud-errors (1.3.0)
237-
google-cloud-storage (1.43.0)
236+
google-cloud-storage (1.44.0)
238237
addressable (~> 2.8)
239238
digest-crc (~> 0.4)
240239
google-apis-iamcredentials_v1 (~> 0.1)
241240
google-apis-storage_v1 (~> 0.19.0)
242241
google-cloud-core (~> 1.6)
243242
googleauth (>= 0.16.2, < 2.a)
244243
mini_mime (~> 1.0)
245-
googleauth (1.2.0)
244+
googleauth (1.3.0)
246245
faraday (>= 0.17.3, < 3.a)
247246
jwt (>= 1.4, < 3.0)
248247
memoist (~> 0.16)
@@ -257,9 +256,6 @@ GEM
257256
concurrent-ruby (~> 1.0)
258257
jmespath (1.6.1)
259258
json (2.6.2)
260-
jsonlint (0.3.0)
261-
oj (~> 3)
262-
optimist (~> 3)
263259
jwt (2.5.0)
264260
kramdown (2.4.0)
265261
rexml
@@ -284,9 +280,7 @@ GEM
284280
octokit (4.25.1)
285281
faraday (>= 1, < 3)
286282
sawyer (~> 0.9)
287-
oj (3.13.23)
288283
open4 (1.3.4)
289-
optimist (3.0.1)
290284
options (2.3.2)
291285
optparse (0.1.1)
292286
os (1.1.4)

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
21.3
66
-----
7+
* [***] Adds a smooth, opt-in transition to the Jetpack app. [#19714]
78
* [*] [internal] When a user migrates to the Jetpack app and allows notifications, WordPress app notifications are disabled. This is released disabled and is behind a feature flag. [#19616, #19611, #19590]
89
* [*] Fixed a minor UI issue where the segmented control under My SIte was being clipped when "Home" is selected. [#19595]
910
* [*] Fixed an issue where the site wasn't removed and the app wasn't refreshed after disconnecting the site from WordPress.com. [#19634]

WordPress/Classes/Models/WPAccount.m

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#import "WordPress-Swift.h"
33

44
static NSString * const WordPressComOAuthKeychainServiceName = @"public-api.wordpress.com";
5+
static NSString * const JetpackComOAuthKeychainServiceName = @"jetpack.public-api.wordpress.com";
56

67
@interface WPAccount ()
78

@@ -85,7 +86,7 @@ - (void)setAuthToken:(NSString *)authToken
8586
NSError *error = nil;
8687
[SFHFKeychainUtils storeUsername:self.username
8788
andPassword:authToken
88-
forServiceName:WordPressComOAuthKeychainServiceName
89+
forServiceName:[WPAccount authKeychainServiceName]
8990
accessGroup:nil
9091
updateExisting:YES
9192
error:&error];
@@ -97,7 +98,7 @@ - (void)setAuthToken:(NSString *)authToken
9798
} else {
9899
NSError *error = nil;
99100
[SFHFKeychainUtils deleteItemForUsername:self.username
100-
andServiceName:WordPressComOAuthKeychainServiceName
101+
andServiceName:[WPAccount authKeychainServiceName]
101102
accessGroup:nil
102103
error:&error];
103104
if (error) {
@@ -133,8 +134,9 @@ - (BOOL)hasAtomicSite {
133134
+ (NSString *)tokenForUsername:(NSString *)username
134135
{
135136
NSError *error = nil;
137+
[WPAccount migrateAuthKeyForUsername:username];
136138
NSString *authToken = [SFHFKeychainUtils getPasswordForUsername:username
137-
andServiceName:WordPressComOAuthKeychainServiceName
139+
andServiceName:[WPAccount authKeychainServiceName]
138140
accessGroup:nil
139141
error:&error];
140142
if (error) {
@@ -144,6 +146,22 @@ + (NSString *)tokenForUsername:(NSString *)username
144146
return authToken;
145147
}
146148

149+
+ (void)migrateAuthKeyForUsername:(NSString *)username
150+
{
151+
static dispatch_once_t onceToken;
152+
dispatch_once(&onceToken, ^{
153+
if ([AppConfiguration isJetpack]) {
154+
SharedDataIssueSolver *sharedDataIssueSolver = [SharedDataIssueSolver instance];
155+
[sharedDataIssueSolver migrateAuthKeyFor:username];
156+
}
157+
});
158+
}
159+
160+
+ (NSString *)authKeychainServiceName
161+
{
162+
return [AppConfiguration isWordPress] ? WordPressComOAuthKeychainServiceName : JetpackComOAuthKeychainServiceName;
163+
}
164+
147165
#pragma mark - API Helpers
148166

149167
- (WordPressComRestApi *)wordPressComRestApi

WordPress/Classes/System/WordPressAppDelegate+openURL.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ import AutomatticTracks
2020
return true
2121
}
2222

23+
/// WordPress only. Handle deeplink from JP that requests data export.
24+
let wordPressExportRouter = MigrationDeepLinkRouter(urlForScheme: URL(string: AppScheme.wordpressMigrationV1.rawValue),
25+
routes: [WordPressExportRoute()])
26+
if AppConfiguration.isWordPress,
27+
wordPressExportRouter.canHandle(url: url) {
28+
wordPressExportRouter.handle(url: url)
29+
return true
30+
}
31+
2332
if url.scheme == JetpackNotificationMigrationService.wordPressScheme {
2433
return JetpackNotificationMigrationService.shared.handleNotificationMigrationOnWordPress()
2534
}

WordPress/Classes/System/WordPressAppDelegate.swift

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -198,12 +198,13 @@ class WordPressAppDelegate: UIResponder, UIApplicationDelegate {
198198
updateFeatureFlags()
199199
updateRemoteConfig()
200200

201-
#if JETPACK
201+
#if JETPACK
202+
// JetpackWindowManager is only available in the Jetpack target.
202203
if let windowManager = windowManager as? JetpackWindowManager,
203204
windowManager.shouldImportMigrationData {
204-
windowManager.importAndShowMigrationContent(nil, failureCompletion: nil)
205+
windowManager.importAndShowMigrationContent()
205206
}
206-
#endif
207+
#endif
207208
}
208209

209210
func applicationWillResignActive(_ application: UIApplication) {
@@ -629,17 +630,28 @@ extension WordPressAppDelegate {
629630
extension WordPressAppDelegate {
630631

631632
var currentlySelectedScreen: String {
632-
// Check if the post editor or login view is up
633-
let rootViewController = window?.rootViewController
634-
if let presentedViewController = rootViewController?.presentedViewController {
635-
if presentedViewController is EditPostViewController {
636-
return "Post Editor"
637-
} else if presentedViewController is LoginNavigationController {
638-
return "Login View"
639-
}
633+
guard let rootViewController = window?.rootViewController else {
634+
DDLogInfo("\(#function) is called when `rootViewController` is nil.")
635+
return String()
640636
}
641637

642-
return WPTabBarController.sharedInstance().currentlySelectedScreen()
638+
// NOTE: This logic doesn't cover all the scenarios properly yet. If we want to know what screen was actually seen,
639+
// there should be a recursive check to get to the visible view controller (or call `UINavigationController`'s `visibleViewController`).
640+
//
641+
// Read more here: https://github.com/wordpress-mobile/WordPress-iOS/pull/19677#pullrequestreview-1199885009
642+
//
643+
switch rootViewController.presentedViewController ?? rootViewController {
644+
case is EditPostViewController:
645+
return "Post Editor"
646+
case is LoginNavigationController:
647+
return "Login View"
648+
#if JETPACK
649+
case is MigrationNavigationController:
650+
return "Jetpack Migration View"
651+
#endif
652+
default:
653+
return WPTabBarController.sharedInstance().currentlySelectedScreen()
654+
}
643655
}
644656

645657
var isWelcomeScreenVisible: Bool {
@@ -785,8 +797,7 @@ extension WordPressAppDelegate {
785797
@objc fileprivate func handleDefaultAccountChangedNotification(_ notification: NSNotification) {
786798
// If the notification object is not nil, then it's a login
787799
if notification.object != nil {
788-
setupShareExtensionToken()
789-
configureNotificationExtension()
800+
setupWordPressExtensions()
790801
startObservingAppleIDCredentialRevoked()
791802
AccountService.loadDefaultAccountCookies()
792803
} else {

WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -415,11 +415,6 @@ import Foundation
415415
case jetpackSiteCreationOverlayButtonTapped
416416
case jetpackSiteCreationOverlayDismissed
417417

418-
// WordPress to Jetpack Migration
419-
case migrationEmailTriggered
420-
case migrationEmailSent
421-
case migrationEmailFailed
422-
423418
/// A String that represents the event
424419
var value: String {
425420
switch self {
@@ -1129,14 +1124,6 @@ import Foundation
11291124
case .jetpackSiteCreationOverlayDismissed:
11301125
return "remove_site_creation_overlay_dismissed"
11311126

1132-
// WordPress to Jetpack Migration
1133-
case .migrationEmailTriggered:
1134-
return "migration_email_triggered"
1135-
case .migrationEmailSent:
1136-
return "migration_email_sent"
1137-
case .migrationEmailFailed:
1138-
return "migration_email_failed"
1139-
11401127
} // END OF SWITCH
11411128
}
11421129

WordPress/Classes/Utility/Blogging Reminders/BloggingRemindersScheduler.swift

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,30 +160,47 @@ class BloggingRemindersScheduler {
160160

161161
private static func copyStoreToSharedFile() {
162162
guard let store = try? defaultStore(),
163-
let fileUrl = try? defaultDataFileURL(),
164163
let sharedFileUrl = sharedDataFileURL() else {
165164
return
166165
}
167166

168-
// Only copy the file if we have at least one reminder schedule
169-
if store.configuration.count > 0 {
170-
try? FileManager.default.copyItem(at: fileUrl, to: sharedFileUrl)
167+
ContextManager.shared.performAndSave { context in
168+
var configuration = [String: ScheduledReminders]()
169+
for (blogIdentifier, schedule) in store.configuration {
170+
guard let objectID = context.persistentStoreCoordinator?.managedObjectID(forURIRepresentation: blogIdentifier),
171+
let blog = context.object(with: objectID) as? Blog,
172+
let url = blog.url else {
173+
continue
174+
}
175+
configuration[url] = schedule
176+
}
177+
178+
if configuration.count > 0 {
179+
try? PropertyListEncoder().encode(configuration).write(to: sharedFileUrl)
180+
}
171181
}
172182
}
173183

174184
private static func copyStoreToLocalFile() {
175185
guard let localStore = try? defaultStore(),
176186
let sharedFileUrl = sharedDataFileURL(),
177187
FileManager.default.fileExists(at: sharedFileUrl),
178-
let sharedStore = try? BloggingRemindersStore(dataFileURL: sharedFileUrl) else {
188+
let data = try? Data(contentsOf: sharedFileUrl),
189+
let sharedConfig = try? PropertyListDecoder().decode([String: ScheduledReminders].self, from: data) else {
179190
return
180191
}
181192

182193
// Only copy if the existing local store contains no schedules
183194
if localStore.configuration.count == 0 {
184-
for blogIdentifier in sharedStore.configuration.keys {
185-
let schedule = sharedStore.scheduledReminders(for: blogIdentifier)
186-
try? localStore.save(scheduledReminders: schedule, for: blogIdentifier)
195+
ContextManager.shared.performAndSave { context in
196+
for (blogUrl, schedule) in sharedConfig {
197+
guard let blog = try? BlogQuery().hostname(matching: blogUrl).blog(in: context) else {
198+
continue
199+
}
200+
let blogIdentifier = blog.objectID.uriRepresentation()
201+
try? localStore.save(scheduledReminders: schedule, for: blogIdentifier)
202+
}
203+
try? FileManager.default.removeItem(at: sharedFileUrl)
187204
}
188205
}
189206
}

WordPress/Classes/Utility/BuildInformation/FeatureFlag.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,9 @@ enum FeatureFlag: Int, CaseIterable, OverrideableFlag {
113113
case .jetpackPowered:
114114
return true
115115
case .jetpackPoweredBottomSheet:
116-
return false
116+
return true
117117
case .contentMigration:
118-
return false
118+
return true
119119
case .newJetpackLandingScreen:
120120
return true
121121
case .newWordPressLandingScreen:
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/// A router that specifically handles deeplinks.
2+
/// Note that the capability of this router is very limited; it can only handle up to one path component (e.g.: `wordpress://intent`).
3+
///
4+
/// This is meant to be used during the WP->JP migratory period. Once we decide to move on from this phase, this class may be removed.
5+
///
6+
struct MigrationDeepLinkRouter: LinkRouter {
7+
8+
let routes: [Route]
9+
10+
/// when this is set, the router ensures that the URL has a scheme that matches this value.
11+
private var scheme: String? = nil
12+
13+
init(routes: [Route]) {
14+
self.routes = routes
15+
}
16+
17+
init(scheme: String?, routes: [Route]) {
18+
self.init(routes: routes)
19+
self.scheme = scheme
20+
}
21+
22+
init(urlForScheme: URL?, routes: [Route]) {
23+
self.init(scheme: urlForScheme?.scheme, routes: routes)
24+
}
25+
26+
func canHandle(url: URL) -> Bool {
27+
// if the scheme is set, check if the URL fulfills the requirement.
28+
if let scheme, url.scheme != scheme {
29+
return false
30+
}
31+
32+
/// deeplinks have their paths start at `host`, unlike universal links.
33+
/// e.g. wordpress://intent -> "intent" is the URL's host.
34+
///
35+
/// Ensure that the deeplink URL has a "host" that we can run against the `routes`' path.
36+
guard let deepLinkPath = url.host else {
37+
return false
38+
}
39+
40+
return routes
41+
.map { $0.path.removingPrefix("/") }
42+
.contains { $0 == deepLinkPath }
43+
}
44+
45+
func handle(url: URL, shouldTrack track: Bool = false, source: DeepLinkSource? = nil) {
46+
guard let deepLinkPath = url.host,
47+
let route = routes.filter({ $0.path.removingPrefix("/") == deepLinkPath }).first else {
48+
return
49+
}
50+
51+
// there's no need to pass any arguments or parameters since most of the migration deeplink routes are standalone.
52+
route.action.perform([:], source: nil, router: self)
53+
}
54+
}

0 commit comments

Comments
 (0)