Skip to content

Commit 489dbbd

Browse files
author
Giorgio Ruscigno
committed
Merge branch trunk into task/19570-migration-notifications-screen-ui
2 parents 9214568 + 8b8a4df commit 489dbbd

File tree

128 files changed

+2040
-1575
lines changed

Some content is hidden

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

128 files changed

+2040
-1575
lines changed

Podfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ abstract_target 'Apps' do
218218

219219
pod 'NSURL+IDN', '~> 0.4'
220220

221-
pod 'WPMediaPicker', '~> 1.8.4'
221+
pod 'WPMediaPicker', '~> 1.8.7-beta.1'
222222
# pod 'WPMediaPicker', :git => 'https://github.com/wordpress-mobile/MediaPicker-iOS.git', :tag => '1.7.0'
223223
## while PR is in review:
224224
# pod 'WPMediaPicker', :git => 'https://github.com/wordpress-mobile/MediaPicker-iOS.git', :branch => ''

Podfile.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ PODS:
512512
- CocoaLumberjack (~> 3.4)
513513
- FormatterKit/TimeIntervalFormatter (~> 1.8)
514514
- WordPressUI (1.12.5)
515-
- WPMediaPicker (1.8.6)
515+
- WPMediaPicker (1.8.7-beta.1)
516516
- wpxmlrpc (0.9.0)
517517
- Yoga (1.14.0)
518518
- ZendeskCommonUISDK (6.1.2)
@@ -605,7 +605,7 @@ DEPENDENCIES:
605605
- WordPressKit (>= 4.58.2, ~> 4.58)
606606
- WordPressShared (~> 1.18.0)
607607
- WordPressUI (~> 1.12.5)
608-
- WPMediaPicker (~> 1.8.4)
608+
- WPMediaPicker (~> 1.8.7-beta.1)
609609
- Yoga (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.85.0/third-party-podspecs/Yoga.podspec.json`)
610610
- ZendeskSupportSDK (= 5.3.0)
611611
- ZIPFoundation (~> 0.9.8)
@@ -868,7 +868,7 @@ SPEC CHECKSUMS:
868868
WordPressKit: d8bc00bce09273fc241cf171b0e61ef8f4f29479
869869
WordPressShared: e5a479220643f46dc4d7726ef8dd45f18bf0c53b
870870
WordPressUI: c5be816f6c7b3392224ac21de9e521e89fa108ac
871-
WPMediaPicker: 749ebfa75fb2b6df4f5e5d9d0847e9512ad74d28
871+
WPMediaPicker: 59135aebb058a95a507045f93e478516729e5c0f
872872
wpxmlrpc: bf55a43a7e710bd2a4fb8c02dfe83b1246f14f13
873873
Yoga: 2ca978c40e0fd6d7f54bcb1602bc0cbbc79454a7
874874
ZendeskCommonUISDK: 5f0a83f412e07ae23701f18c412fe783b3249ef5
@@ -880,6 +880,6 @@ SPEC CHECKSUMS:
880880
ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba
881881
ZIPFoundation: ae5b4b813d216d3bf0a148773267fff14bd51d37
882882

883-
PODFILE CHECKSUM: 879b8a4d9da3b3bad6cfed2de24977912ce560b0
883+
PODFILE CHECKSUM: 936784ec2e9d8246acccd6b123c0ba5b1b6e13b9
884884

885885
COCOAPODS: 1.11.2

RELEASE-NOTES.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
21.2
22
-----
3-
3+
* [*] [internal] Refactored fetching posts in the Reader tab. [#19539]
4+
* [*] Fixed an issue where the message "No media matching your search" for the media picker is not visible [#19555]
45

56
21.1
67
-----

WordPress/Classes/Extensions/Media+Blog.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,19 @@ extension Media {
5959

6060
return media
6161
}
62+
63+
/// Returns a list of Media objects from a post, that should be autoUploaded on the next attempt.
64+
///
65+
/// - Parameters:
66+
/// - post: the post to look auto-uploadable media for.
67+
/// - automatedRetry: whether the media to upload is the result of an automated retry.
68+
///
69+
/// - Returns: the Media objects that should be autoUploaded.
70+
///
71+
class func failedForUpload(in post: AbstractPost, automatedRetry: Bool) -> [Media] {
72+
post.media.filter { media in
73+
media.remoteStatus == .failed
74+
&& (!automatedRetry || media.autoUploadFailureCount.intValue < Media.maxAutoUploadFailureCount)
75+
}
76+
}
6277
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import Foundation
2+
3+
extension Media {
4+
5+
/// Returns a list of Media objects that should be uploaded for the given input parameters.
6+
///
7+
/// - Parameters:
8+
/// - automatedRetry: whether the media to upload is the result of an automated retry.
9+
///
10+
/// - Returns: the Media objects that should be uploaded for the given input parameters.
11+
///
12+
static func failedMediaForUpload(automatedRetry: Bool, in context: NSManagedObjectContext) -> [Media] {
13+
let request = NSFetchRequest<Media>(entityName: Media.entityName())
14+
let failedMediaPredicate = NSPredicate(format: "\(#keyPath(Media.remoteStatusNumber)) == %d", MediaRemoteStatus.failed.rawValue)
15+
16+
if automatedRetry {
17+
let autoUploadFailureCountPredicate = NSPredicate(format: "\(#keyPath(Media.autoUploadFailureCount)) < %d", Media.maxAutoUploadFailureCount)
18+
19+
request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [failedMediaPredicate, autoUploadFailureCountPredicate])
20+
} else {
21+
request.predicate = failedMediaPredicate
22+
}
23+
24+
let media = (try? context.fetch(request)) ?? []
25+
26+
return media
27+
}
28+
29+
/// This method checks the status of all media objects and updates them to the correct status if needed.
30+
/// The main cause of wrong status is the app being killed while uploads of media are happening.
31+
///
32+
/// - Parameters:
33+
/// - onCompletion: block to invoke when status update is finished.
34+
/// - onError: block to invoke if any error occurs while the update is being made.
35+
///
36+
static func refreshMediaStatus(using coreDataStack: CoreDataStack, onCompletion: (() -> Void)? = nil, onError: ((Error) -> Void)? = nil) {
37+
coreDataStack.performAndSave { context in
38+
let fetch = NSFetchRequest<Media>(entityName: Media.classNameWithoutNamespaces())
39+
let pushingPredicate = NSPredicate(format: "remoteStatusNumber = %@", NSNumber(value: MediaRemoteStatus.pushing.rawValue))
40+
let processingPredicate = NSPredicate(format: "remoteStatusNumber = %@", NSNumber(value: MediaRemoteStatus.processing.rawValue))
41+
let errorPredicate = NSPredicate(format: "remoteStatusNumber = %@", NSNumber(value: MediaRemoteStatus.failed.rawValue))
42+
fetch.predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [pushingPredicate, processingPredicate, errorPredicate])
43+
let mediaPushing = try context.fetch(fetch)
44+
for media in mediaPushing {
45+
// If file were in the middle of being pushed or being processed they now are failed.
46+
if media.remoteStatus == .pushing || media.remoteStatus == .processing {
47+
media.remoteStatus = .failed
48+
}
49+
// If they failed to upload themselfs because no local copy exists then we need to delete this media object
50+
// This scenario can happen when media objects were created based on an asset that failed to import to the WordPress App.
51+
// For example a PHAsset that is stored on the iCloud storage and because of the network connection failed the import process.
52+
if media.remoteStatus == .failed,
53+
let error = media.error as NSError?, error.domain == MediaServiceErrorDomain && error.code == MediaServiceError.fileDoesNotExist.rawValue {
54+
context.delete(media)
55+
}
56+
}
57+
} completion: { result in
58+
DispatchQueue.main.async {
59+
switch result {
60+
case .success:
61+
onCompletion?()
62+
case let .failure(error):
63+
DDLogError("Error while attempting to clean local media: \(error.localizedDescription)")
64+
onError?(error)
65+
}
66+
}
67+
}
68+
}
69+
70+
}

WordPress/Classes/Extensions/UIImageView+SiteIcon.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ extension UIImageView {
145145

146146
// MARK: - Private Methods
147147
//
148-
private extension UIImageView {
148+
extension UIImageView {
149149
/// Returns the Size Optimized URL for a given Path.
150150
///
151151
func optimizedURL(for path: String) -> URL? {

WordPress/Classes/Services/MediaCoordinator.swift

Lines changed: 61 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ import enum Alamofire.AFError
1111
class MediaCoordinator: NSObject {
1212
@objc static let shared = MediaCoordinator()
1313

14-
private(set) var backgroundContext: NSManagedObjectContext = {
15-
let context = ContextManager.sharedInstance().newDerivedContext()
16-
context.automaticallyMergesChangesFromParent = true
17-
return context
14+
private let coreDataStack: CoreDataStack
15+
16+
private var mainContext: NSManagedObjectContext {
17+
coreDataStack.mainContext
18+
}
19+
20+
private let syncOperationQueue: OperationQueue = {
21+
let queue = OperationQueue()
22+
queue.name = "org.wordpress.mediauploadcoordinator.sync"
23+
queue.maxConcurrentOperationCount = 1
24+
return queue
1825
}()
19-
private let mainContext = ContextManager.sharedInstance().mainContext
2026

2127
private let queue = DispatchQueue(label: "org.wordpress.mediauploadcoordinator")
2228

@@ -36,8 +42,9 @@ class MediaCoordinator: NSObject {
3642

3743
private let mediaServiceFactory: MediaService.Factory
3844

39-
init(_ mediaServiceFactory: MediaService.Factory = MediaService.Factory()) {
45+
init(_ mediaServiceFactory: MediaService.Factory = MediaService.Factory(), coreDataStack: CoreDataStack = ContextManager.shared) {
4046
self.mediaServiceFactory = mediaServiceFactory
47+
self.coreDataStack = coreDataStack
4148

4249
super.init()
4350

@@ -54,12 +61,11 @@ class MediaCoordinator: NSObject {
5461
/// - Returns: `true` if all media in the post is uploading or was uploaded, `false` otherwise.
5562
///
5663
func uploadMedia(for post: AbstractPost, automatedRetry: Bool = false) -> Bool {
57-
let mediaService = mediaServiceFactory.create(backgroundContext)
5864
let failedMedia: [Media] = post.media.filter({ $0.remoteStatus == .failed })
5965
let mediasToUpload: [Media]
6066

6167
if automatedRetry {
62-
mediasToUpload = mediaService.failedMediaForUpload(in: post, automatedRetry: automatedRetry)
68+
mediasToUpload = Media.failedForUpload(in: post, automatedRetry: automatedRetry)
6369
} else {
6470
mediasToUpload = failedMedia
6571
}
@@ -286,25 +292,23 @@ class MediaCoordinator: NSObject {
286292
func delete(media: [Media], onProgress: ((Progress?) -> Void)? = nil, success: (() -> Void)? = nil, failure: (() -> Void)? = nil) {
287293
media.forEach({ self.cancelUpload(of: $0) })
288294

289-
let service = mediaServiceFactory.create(backgroundContext)
290-
service.deleteMedia(media,
291-
progress: { onProgress?($0) },
292-
success: success,
293-
failure: failure)
295+
coreDataStack.performAndSave { context in
296+
let service = self.mediaServiceFactory.create(context)
297+
service.deleteMedia(media,
298+
progress: { onProgress?($0) },
299+
success: success,
300+
failure: failure)
301+
}
294302
}
295303

296304
@discardableResult
297305
private func uploadMedia(_ media: Media, automatedRetry: Bool = false) -> Progress {
298-
let service = mediaServiceFactory.create(backgroundContext)
299-
300-
var progress: Progress? = nil
306+
let resultProgress = Progress.discreteProgress(totalUnitCount: 100)
301307

302-
service.uploadMedia(media,
303-
automatedRetry: automatedRetry,
304-
progress: &progress,
305-
success: {
306-
self.end(media)
307-
}, failure: { error in
308+
let success: () -> Void = {
309+
self.end(media)
310+
}
311+
let failure: (Error?) -> Void = { error in
308312
// Ideally the upload service should always return an error. This may be easier to enforce
309313
// if we update the service to Swift, but in the meanwhile I'm instantiating an unknown upload
310314
// error whenever the service doesn't provide one.
@@ -324,12 +328,19 @@ class MediaCoordinator: NSObject {
324328

325329
self.coordinator(for: media).attach(error: nserror, toMediaID: media.uploadID)
326330
self.fail(nserror, media: media)
327-
})
328-
var resultProgress = Progress.discreteCompletedProgress()
329-
if let taskProgress = progress {
330-
resultProgress = taskProgress
331331
}
332+
333+
coreDataStack.performAndSave { context in
334+
let service = self.mediaServiceFactory.create(context)
335+
var progress: Progress? = nil
336+
service.uploadMedia(media, automatedRetry: automatedRetry, progress: &progress, success: success, failure: failure)
337+
if let progress {
338+
resultProgress.addChild(progress, withPendingUnitCount: resultProgress.totalUnitCount)
339+
}
340+
}
341+
332342
uploading(media, progress: resultProgress)
343+
333344
return resultProgress
334345
}
335346

@@ -634,16 +645,30 @@ class MediaCoordinator: NSObject {
634645
/// - parameter blog: The blog from where to sync the media library from.
635646
///
636647
@objc func syncMedia(for blog: Blog, success: (() -> Void)? = nil, failure: ((Error) ->Void)? = nil) {
637-
let service = mediaServiceFactory.create(backgroundContext)
638-
service.syncMediaLibrary(for: blog, success: success, failure: failure)
648+
syncOperationQueue.addOperation(AsyncBlockOperation { done in
649+
self.coreDataStack.performAndSave { context in
650+
let service = self.mediaServiceFactory.create(context)
651+
service.syncMediaLibrary(
652+
for: blog,
653+
success: {
654+
done()
655+
success?()
656+
},
657+
failure: { error in
658+
done()
659+
failure?(error)
660+
}
661+
)
662+
}
663+
})
664+
639665
}
640666

641667
/// This method checks the status of all media objects and updates them to the correct status if needed.
642668
/// The main cause of wrong status is the app being killed while uploads of media are happening.
643669
///
644670
@objc func refreshMediaStatus() {
645-
let service = mediaServiceFactory.create(backgroundContext)
646-
service.refreshMediaStatus()
671+
Media.refreshMediaStatus(using: coreDataStack)
647672
}
648673
}
649674

@@ -693,10 +718,12 @@ extension MediaCoordinator: MediaProgressCoordinatorDelegate {
693718

694719
extension MediaCoordinator: Uploader {
695720
func resume() {
696-
let service = mediaServiceFactory.create(backgroundContext)
697-
698-
service.failedMediaForUpload(automatedRetry: true).forEach() {
699-
retryMedia($0, automatedRetry: true)
721+
coreDataStack.performAndSave { context in
722+
Media
723+
.failedMediaForUpload(automatedRetry: true, in: context)
724+
.forEach() {
725+
self.retryMedia($0, automatedRetry: true)
726+
}
700727
}
701728
}
702729
}

WordPress/Classes/Services/MediaService.m

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,10 +549,17 @@ - (void) getMediaWithID:(NSNumber *) mediaID inBlog:(Blog *) blog
549549
media = [Media makeMediaWithBlog:blog];
550550
}
551551
[self updateMedia:media withRemoteMedia:remoteMedia];
552+
553+
[[ContextManager sharedInstance] saveContextAndWait:self.managedObjectContext];
554+
552555
if (success){
553556
success(media);
557+
558+
if ([media hasChanges]) {
559+
NSCAssert(NO, @"The success callback should not modify the Media instance");
560+
[[ContextManager sharedInstance] saveContext:self.managedObjectContext];
561+
}
554562
}
555-
[[ContextManager sharedInstance] saveContext:self.managedObjectContext];
556563
}];
557564
} failure:^(NSError *error) {
558565
if (failure) {

0 commit comments

Comments
 (0)