Skip to content

Commit 979e9d7

Browse files
committed
Merge branch 'trunk' into tonyli-xcode-target-authenticator
2 parents 329a895 + 6dc78ec commit 979e9d7

File tree

177 files changed

+1109
-11889
lines changed

Some content is hidden

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

177 files changed

+1109
-11889
lines changed

Gemfile

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ gem 'cocoapods', '~> 1.14'
66
gem 'commonmarker'
77
gem 'danger-dangermattic', '~> 1.0'
88
gem 'dotenv'
9-
# 2.219.0 includes a fix for a bug introduced in 2.218.0
10-
# See https://github.com/fastlane/fastlane/issues/21762#issuecomment-1875208663
11-
gem 'fastlane', '~> 2.219'
9+
# 2.221.0 includes a fix for an ASC-interfacing bug
10+
#
11+
# See https://github.com/wordpress-mobile/WordPress-iOS/pull/23118#issuecomment-2173254418
12+
# and https://github.com/fastlane/fastlane/pull/21995
13+
gem 'fastlane', '~> 2.221'
1214
gem 'fastlane-plugin-appcenter', '~> 2.1'
1315
gem 'fastlane-plugin-sentry'
1416
# This comment avoids typing to switch to a development version for testing.

Gemfile.lock

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,17 @@ GEM
2424
ast (2.4.2)
2525
atomos (0.1.3)
2626
aws-eventstream (1.3.0)
27-
aws-partitions (1.926.0)
28-
aws-sdk-core (3.194.2)
27+
aws-partitions (1.944.0)
28+
aws-sdk-core (3.197.0)
2929
aws-eventstream (~> 1, >= 1.3.0)
3030
aws-partitions (~> 1, >= 1.651.0)
3131
aws-sigv4 (~> 1.8)
3232
jmespath (~> 1, >= 1.6.1)
33-
aws-sdk-kms (1.80.0)
34-
aws-sdk-core (~> 3, >= 3.193.0)
33+
aws-sdk-kms (1.85.0)
34+
aws-sdk-core (~> 3, >= 3.197.0)
3535
aws-sigv4 (~> 1.1)
36-
aws-sdk-s3 (1.149.1)
37-
aws-sdk-core (~> 3, >= 3.194.0)
36+
aws-sdk-s3 (1.152.3)
37+
aws-sdk-core (~> 3, >= 3.197.0)
3838
aws-sdk-kms (~> 1)
3939
aws-sigv4 (~> 1.8)
4040
aws-sigv4 (1.8.0)
@@ -163,7 +163,7 @@ GEM
163163
faraday_middleware (1.2.0)
164164
faraday (~> 1.0)
165165
fastimage (2.3.1)
166-
fastlane (2.220.0)
166+
fastlane (2.221.0)
167167
CFPropertyList (>= 2.3, < 4.0.0)
168168
addressable (>= 2.8, < 3.0.0)
169169
artifactory (~> 3.0)
@@ -268,22 +268,22 @@ GEM
268268
os (>= 0.9, < 2.0)
269269
signet (>= 0.16, < 2.a)
270270
highline (2.0.3)
271-
http-cookie (1.0.5)
271+
http-cookie (1.0.6)
272272
domain_name (~> 0.5)
273273
httpclient (2.8.3)
274274
i18n (1.14.4)
275275
concurrent-ruby (~> 1.0)
276276
java-properties (0.3.0)
277277
jmespath (1.6.2)
278278
json (2.7.2)
279-
jwt (2.8.1)
279+
jwt (2.8.2)
280280
base64
281281
kramdown (2.4.0)
282282
rexml
283283
kramdown-parser-gfm (1.1.0)
284284
kramdown (~> 2.0)
285285
language_server-protocol (3.17.0.3)
286-
mini_magick (4.12.0)
286+
mini_magick (4.13.1)
287287
mini_mime (1.1.5)
288288
mini_portile2 (2.8.5)
289289
minitest (5.22.3)
@@ -329,7 +329,8 @@ GEM
329329
trailblazer-option (>= 0.1.1, < 0.2.0)
330330
uber (< 0.2.0)
331331
retriable (3.1.2)
332-
rexml (3.2.6)
332+
rexml (3.2.9)
333+
strscan
333334
rmagick (3.2.0)
334335
rouge (2.0.7)
335336
rubocop (1.63.1)
@@ -363,6 +364,7 @@ GEM
363364
simctl (1.6.10)
364365
CFPropertyList
365366
naturally
367+
strscan (3.1.0)
366368
terminal-notifier (2.0.0)
367369
terminal-table (3.0.2)
368370
unicode-display_width (>= 1.1.1, < 3)
@@ -398,7 +400,7 @@ DEPENDENCIES
398400
commonmarker
399401
danger-dangermattic (~> 1.0)
400402
dotenv
401-
fastlane (~> 2.219)
403+
fastlane (~> 2.221)
402404
fastlane-plugin-appcenter (~> 2.1)
403405
fastlane-plugin-sentry
404406
fastlane-plugin-wpmreleasetoolkit (~> 11.0)

Gutenberg/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
#
1010
# LOCAL_GUTENBERG=../my-gutenberg-fork bundle exec pod install
1111
ref:
12-
tag: v1.120.0
12+
tag: v1.120.1
1313
github_org: wordpress-mobile
1414
repo_name: gutenberg-mobile

Podfile.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ PODS:
1515
- Gravatar (2.0.0)
1616
- GravatarUI (2.0.0):
1717
- Gravatar (= 2.0.0)
18-
- Gutenberg (1.120.0)
18+
- Gutenberg (1.120.1)
1919
- Kanvas (1.4.9):
2020
- CropViewController
2121
- MediaEditor (1.2.2):
@@ -36,7 +36,7 @@ DEPENDENCIES:
3636
- FSInteractiveMap (from `https://github.com/wordpress-mobile/FSInteractiveMap.git`, tag `0.2.0`)
3737
- Gravatar (= 2.0.0)
3838
- GravatarUI (= 2.0.0)
39-
- Gutenberg (from `https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-v1.120.0.podspec`)
39+
- Gutenberg (from `https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-v1.120.1.podspec`)
4040
- Kanvas (~> 1.4.4)
4141
- MediaEditor (>= 1.2.2, ~> 1.2)
4242
- Specta (= 1.0.7)
@@ -67,7 +67,7 @@ EXTERNAL SOURCES:
6767
:git: https://github.com/wordpress-mobile/FSInteractiveMap.git
6868
:tag: 0.2.0
6969
Gutenberg:
70-
:podspec: https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-v1.120.0.podspec
70+
:podspec: https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-v1.120.1.podspec
7171

7272
CHECKOUT OPTIONS:
7373
FSInteractiveMap:
@@ -81,7 +81,7 @@ SPEC CHECKSUMS:
8181
FSInteractiveMap: a396f610f48b76cb540baa87139d056429abda86
8282
Gravatar: 54fc63ea6298e9afca7329007815be25868f1dfe
8383
GravatarUI: bb5e03cda2da61e54aa1c20d4dde32b7153fa8b4
84-
Gutenberg: a0cc56d764e14caf66c8a057255106a62808dd98
84+
Gutenberg: 0699e7dd207afb591ccd5e81252a92e6e7781391
8585
Kanvas: cc027f8058de881a4ae2b5aa5f05037b6d054d08
8686
MediaEditor: d08314cfcbfac74361071a306b4bc3a39b3356ae
8787
Specta: 3e1bd89c3517421982dc4d1c992503e48bd5fe66

RELEASE-NOTES.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
25.2
22
-----
3-
3+
* [*] Simplify post list context menu sections [#23356]
4+
* [*] Fix an issue with incorrect snackbar shown when saving drafts manually [#23358]
45

56
25.1
67
-----
@@ -9,7 +10,12 @@
910
* [*] Fix an issue with a missing navigation bar background in Post Settings [#23334]
1011
* [*] Fix an issue where the app will sometimes save empty drafts [#23342]
1112
* [*] Add `.networkConnectionLost` to the list of "offline" scenarios so that the "offline changes" badge and fast retries work for this use case [#23348]
12-
13+
* [*] If draft sync fails, the app will now show the "Retry" button in the context menu along with the error message [#23355]
14+
* [*] Fix an issue with post content structure not loading in some scenarios [#23347]
15+
* [*] Fix a rare crash when updating posts with categories [#23354]
16+
* [*] Fix an issue where a Page editor will not close automatically after trying to update a permanently deleted post [#23363]
17+
* [*] Fix an issue where you could remove the scheduled date on a scheduled post [#23360]
18+
* [*] The Post List screen now fetches updates faster and shows a spinner when refreshing stale data [#23362]
1319

1420
25.0
1521
-----

WordPress/Classes/Services/PostCategoryService.m

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,19 @@ - (void)mergeCategories:(NSArray <RemotePostCategory *> *)remoteCategories forBl
165165
NSMutableArray *categories = [NSMutableArray arrayWithCapacity:remoteCategories.count];
166166

167167
for (RemotePostCategory *remoteCategory in remoteCategories) {
168-
PostCategory *category = [PostCategory lookupWithBlogObjectID:blog.objectID categoryID:remoteCategory.categoryID inContext:context];
169-
if (!category) {
170-
category = [PostCategory createWithBlogObjectID:blog.objectID inContext:context];
171-
category.categoryID = remoteCategory.categoryID;
172-
}
173-
category.categoryName = remoteCategory.name;
174-
category.parentID = remoteCategory.parentID;
168+
if (remoteCategory.categoryID) {
169+
PostCategory *category = [PostCategory lookupWithBlogObjectID:blog.objectID categoryID:remoteCategory.categoryID inContext:context];
170+
if (!category) {
171+
category = [PostCategory createWithBlogObjectID:blog.objectID inContext:context];
172+
category.categoryID = remoteCategory.categoryID;
173+
}
174+
category.categoryName = remoteCategory.name;
175+
if (remoteCategory.parentID) {
176+
category.parentID = remoteCategory.parentID;
177+
}
175178

176-
[categories addObject:category];
179+
[categories addObject:category];
180+
}
177181
}
178182
}
179183

WordPress/Classes/Services/PostCoordinator+Notices.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,15 @@ private enum Strings {
4848
static let view = NSLocalizedString("postNotice.view", value: "View", comment: "Button title. Displays a summary / sharing screen for a specific post.")
4949

5050
static func publishSuccessTitle(for post: AbstractPost, isUpdated: Bool = false) -> String {
51+
if post.status == .draft {
52+
return NSLocalizedString("postNotice.draftSaved", value: "Draft Saved", comment: "Title of notification displayed when either a new or an existing draft is saved")
53+
}
5154
switch post {
5255
case let post as Post:
5356
guard !isUpdated else {
5457
return NSLocalizedString("postNotice.postUpdated", value: "Post updated", comment: "Title of notification displayed when a post has been successfully updated.")
5558
}
5659
switch post.status {
57-
case .draft:
58-
return NSLocalizedString("postNotice.postDraftCreated", value: "Post draft uploaded", comment: "Title of notification displayed when a post has been successfully saved as a draft.")
5960
case .scheduled:
6061
return NSLocalizedString("postNotice.postScheduled", value: "Post scheduled", comment: "Title of notification displayed when a post has been successfully scheduled.")
6162
case .pending:
@@ -68,8 +69,6 @@ private enum Strings {
6869
return NSLocalizedString("postNotice.pageUpdated", value: "Page updated", comment: "Title of notification displayed when a page has been successfully updated.")
6970
}
7071
switch page.status {
71-
case .draft:
72-
return NSLocalizedString("postNotice.pageDraftCreated", value: "Page draft uploaded", comment: "Title of notification displayed when a page has been successfully saved as a draft.")
7372
case .scheduled:
7473
return NSLocalizedString("postNotice.pageScheduled", value: "Page scheduled", comment: "Title of notification displayed when a page has been successfully scheduled.")
7574
case .pending:
@@ -78,7 +77,7 @@ private enum Strings {
7877
return NSLocalizedString("postNotice.pagePublished", value: "Page published", comment: "Title of notification displayed when a page has been successfully published.")
7978
}
8079
default:
81-
assertionFailure("Unexpected post type: \(post)")
80+
wpAssertionFailure("unexpected post type", userInfo: ["post_type": String(describing: type(of: post))])
8281
return ""
8382
}
8483
}

WordPress/Classes/Services/PostCoordinator.swift

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ protocol PostCoordinatorDelegate: AnyObject {
1313
class PostCoordinator: NSObject {
1414

1515
enum SavingError: Error, LocalizedError, CustomNSError {
16+
/// One of the media uploads failed.
1617
case mediaFailure(AbstractPost, Error)
17-
case unknown
18+
/// The upload has been failing for too long.
19+
case maximumRetryTimeIntervalReached
1820

1921
var errorDescription: String? {
2022
Strings.genericErrorTitle
@@ -24,7 +26,7 @@ class PostCoordinator: NSObject {
2426
switch self {
2527
case .mediaFailure(_, let error):
2628
return [NSUnderlyingErrorKey: error]
27-
case .unknown:
29+
case .maximumRetryTimeIntervalReached:
2830
return [:]
2931
}
3032
}
@@ -52,8 +54,8 @@ class PostCoordinator: NSObject {
5254
private let mediaCoordinator: MediaCoordinator
5355
private let actionDispatcherFacade: ActionDispatcherFacade
5456

55-
/// The initial sync retry delay. By default, 8 seconds.
56-
var syncRetryDelay: TimeInterval = 8
57+
/// The initial sync retry delay. By default, 15 seconds.
58+
var syncRetryDelay: TimeInterval = 15
5759

5860
// MARK: - Initializers
5961

@@ -293,6 +295,19 @@ class PostCoordinator: NSObject {
293295
startSync(for: revision.original())
294296
}
295297

298+
func retrySync(for post: AbstractPost) {
299+
wpAssert(post.isOriginal())
300+
301+
guard let revision = post.getLatestRevisionNeedingSync() else {
302+
return
303+
}
304+
revision.confirmedChangesTimestamp = Date()
305+
ContextManager.shared.saveContextAndWait(coreDataStack.mainContext)
306+
307+
getWorker(for: post).showNextError = true
308+
startSync(for: post)
309+
}
310+
296311
/// Schedules sync for all the posts with revisions that need syncing.
297312
///
298313
/// - note: It should typically only be called once during the app launch.
@@ -354,20 +369,23 @@ class PostCoordinator: NSObject {
354369

355370
/// A manages sync for the given post. Every post has its own worker.
356371
private final class SyncWorker {
372+
/// Defines for how many days (in seconds) the app should continue trying
373+
/// to upload the post before giving up and requiring manual intervention.
374+
static let maximumRetryTimeInterval: TimeInterval = 86400 * 3 // 3 days
375+
357376
let post: AbstractPost
358377
var isPaused = false
359378
var operation: SyncOperation? // The sync operation that's currently running
360379
var error: Error? // The previous sync error
361380

362381
var nextRetryDelay: TimeInterval {
363-
retryDelay = min(60, retryDelay * 2)
382+
retryDelay = min(120, retryDelay * 2)
364383
return retryDelay
365384
}
366-
func setLongerDelay() {
367-
retryDelay = max(retryDelay, 30)
368-
}
385+
369386
var retryDelay: TimeInterval
370387
weak var retryTimer: Timer?
388+
var showNextError = false
371389

372390
deinit {
373391
self.log("deinit")
@@ -440,6 +458,13 @@ class PostCoordinator: NSObject {
440458

441459
let worker = getWorker(for: post)
442460

461+
if let date = revision.confirmedChangesTimestamp,
462+
Date.now.timeIntervalSince(date) > SyncWorker.maximumRetryTimeInterval {
463+
worker.error = PostCoordinator.SavingError.maximumRetryTimeIntervalReached
464+
postDidUpdateNotification(for: post)
465+
return worker.log("stopping – failing to upload changes for too long")
466+
}
467+
443468
guard !worker.isPaused else {
444469
return worker.log("start failed: worker is paused")
445470
}
@@ -525,8 +550,9 @@ class PostCoordinator: NSObject {
525550
worker.error = error
526551
postDidUpdateNotification(for: operation.post)
527552

528-
if PostCoordinator.isTerminalError(error) {
529-
worker.setLongerDelay()
553+
if worker.showNextError {
554+
worker.showNextError = false
555+
handleError(error, for: operation.post)
530556
}
531557

532558
let delay = worker.nextRetryDelay
@@ -544,21 +570,6 @@ class PostCoordinator: NSObject {
544570
}
545571
}
546572

547-
/// Returns `true` if the error can't be resolved by simply retrying and
548-
/// requires user interventions, for example, resolving a conflict.
549-
static func isTerminalError(_ error: Error) -> Bool {
550-
if let saveError = error as? PostRepository.PostSaveError {
551-
switch saveError {
552-
case .deleted, .conflict:
553-
return true
554-
}
555-
}
556-
if let error = error as? SavingError, case .mediaFailure(_, let error) = error {
557-
return MediaCoordinator.isTerminalError(error)
558-
}
559-
return false
560-
}
561-
562573
private func didRetryTimerFire(for worker: SyncWorker) {
563574
worker.log("retry timer fired")
564575
startSync(for: worker.post)

WordPress/Classes/Services/PostHelper+Metadata.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,22 @@ extension PostHelper {
1111
value: dictionary["value"] as? String
1212
)
1313
}
14+
15+
@objc(createOrUpdateCategoryForRemoteCategory:blog:context:)
16+
class func createOrUpdateCategory(for remoteCategory: RemotePostCategory, in blog: Blog, in context: NSManagedObjectContext) -> PostCategory? {
17+
guard let categoryID = remoteCategory.categoryID else {
18+
wpAssertionFailure("remote category missing categoryID")
19+
return nil
20+
}
21+
if let category = try? PostCategory.lookup(withBlogID: blog.objectID, categoryID: categoryID, in: context) {
22+
return category
23+
}
24+
let category = PostCategory(context: context)
25+
// - warning: these PostCategory fields are explicitly unwrapped optionals!
26+
category.blog = blog
27+
category.categoryID = categoryID
28+
category.categoryName = remoteCategory.name ?? ""
29+
category.parentID = remoteCategory.parentID ?? 0 // `0` means "no parent"
30+
return category
31+
}
1432
}

0 commit comments

Comments
 (0)