From 3d8bf7ded44a540f3c1c8f9814309e31eb604603 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 21 Oct 2020 11:44:43 -0400 Subject: [PATCH] Fixes for pages and priority --- .../MVMCoreAlertHandler+Extension.swift | 77 +++++++++++-------- .../AlertHandling/MVMCoreAlertHandler.h | 6 +- .../AlertHandling/MVMCoreAlertHandler.m | 53 +++++++------ .../AlertHandling/MVMCoreTopAlertOperation.m | 14 ++++ 4 files changed, 92 insertions(+), 58 deletions(-) diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler+Extension.swift b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler+Extension.swift index 9ee8ff9..309ea8f 100644 --- a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler+Extension.swift +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler+Extension.swift @@ -36,43 +36,60 @@ public extension MVMCoreAlertHandler { operation.addDependency(pagesOperation) } - /// checks top alerts for page dependencies with the new pageType - @objc func checkPagesDependency(for pageType: String?) { - // If the current running operation should not show on the current page, cancel it. If it's persistent, re-add it. If another operation has had the dependency remove previously, add it back. + /// Updates the pages requirement for the operation + @objc func updatePages(for operation: MVMCoreTopAlertOperation, with topAlertObject: MVMCoreTopAlertObject) { + // Add new dependencies, remove old. + let previousPages = operation.dependencies.filter { $0 as? NotificationPagesOperation != nil } + addPagesDependency(to: operation) + for pageOperation in previousPages { + pageOperation.cancel() + operation.removeDependency(pageOperation) + } + } + + /// Handles page dependencies for all top alerts with the new pageType + @objc func handleAllPagesDependency(for pageType: String?) { + var currentOperation: MVMCoreTopAlertOperation? = nil for case let operation as MVMCoreTopAlertOperation in topAlertQueue.operations { - if !operation.isCancelled, - let pages = operation.topAlertObject.json?.optionalArrayForKey("pages") as? [String], - pages.count > 0, - (pageType == nil || !pages.contains(pageType!)) { - if operation.isExecuting { - operation.reAddAfterCancel = operation.topAlertObject.persistent - operation.cancel() - } else if !operation.dependencies.contains(where: { !$0.isFinished }) { - let pagesOperation = NotificationPagesOperation(with: pages) - pageOperations.addOperation(pagesOperation) - operation.addDependency(pagesOperation) - } + // Handle the currently executing opration last to avoid race conditions. + guard !operation.isExecuting else { + currentOperation = operation + continue + } + handlePageDependency(for: operation, with: pageType) + } + if let operation = currentOperation { + handlePageDependency(for: operation, with: pageType) + } + } + + /// Updates the operation based on the page type and its dependencies. + @objc func handlePageDependency(for operation: MVMCoreTopAlertOperation, with pageType: String?) { + guard !operation.isCancelled else { return } + + if let pages = operation.topAlertObject.json?.optionalArrayForKey("pages") as? [String], + pages.count > 0, + (pageType == nil || !pages.contains(pageType!)) { + if let dependency = operation.dependencies.first(where: { $0.isFinished && ($0 as? NotificationPagesOperation)?.pages == pages }) { + // Re-add the dependency if it was previously cancelled. + let pagesOperation = NotificationPagesOperation(with: pages) + pageOperations.addOperation(pagesOperation) + operation.addDependency(pagesOperation) + operation.removeDependency(dependency) + } + if operation.isExecuting { + // If the current running operation should not show on the current page, cancel it. If it's persistent, re-add it + operation.reAddAfterCancel = operation.topAlertObject.persistent + operation.cancel() } } - - // Remove the dependency if it contains the current page. + + // Cancel any dependency if it contains the current page. guard let pageType = pageType else { return } - for case let operation as NotificationPagesOperation in pageOperations.operations { + for case let operation as NotificationPagesOperation in operation.dependencies { if operation.pages.contains(pageType) { operation.cancel() } } } - - /// Checks for existing top alert object of same type and updates it. Only happens for molecular top alerts. Returns true if we updated. - @objc func checkAndUpdateExisting(with topAlertObject: MVMCoreTopAlertObject) -> Bool { - // ToDo: do pages and priority. - for case let operation as MVMCoreTopAlertOperation in topAlertQueue.operations { - guard topAlertObject.json != nil, - operation.topAlertObject.type == topAlertObject.type else { continue } - operation.update(with: topAlertObject) - return true - } - return false - } } diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.h b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.h index e41cff4..bba7741 100644 --- a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.h +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.h @@ -13,6 +13,7 @@ #import @class MVMCoreAlertObject; +@class MVMCoreTopAlertOperation; @interface MVMCoreAlertHandler : NSObject @@ -88,9 +89,12 @@ #pragma mark - Top Alert Functions -/// Show based on the object. Will be used by the architecture. +/// Show based on the object. Will be used by the architecture. Creates an operation and calls addTopAlertOperation. - (void)showTopAlertWithObject:(nullable MVMCoreTopAlertObject *)topAlertObject; +/// Adds the top alert operation to the queue. +- (void)addTopAlertOperation:(nonnull MVMCoreTopAlertOperation *)topAlertOperation; + /// Convenience functions - (void)showTopAlertErrorWithMessage:(nullable NSString *)message; - (void)showTopAlertConfirmationWithMessage:(nullable NSString *)message; diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.m b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.m index 1dab33b..dce4056 100644 --- a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.m +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.m @@ -158,39 +158,38 @@ #pragma mark - Top Alert Functions -- (void)showTopAlertWithObject:(nullable MVMCoreTopAlertObject *)topAlertObject { +- (void)addTopAlertOperation:(nonnull MVMCoreTopAlertOperation *)topAlertOperation { + __block MVMCoreTopAlertOperation *alertOperation = topAlertOperation; + __weak typeof(self) weakSelf = self; + [alertOperation setCompletionBlock:^{ + + // If the alert was cancelled to show another with higher priority, re-add to the operation when cancelled to the queue. + if (alertOperation.reAddAfterCancel) { + MVMCoreTopAlertOperation *newOperation = [alertOperation copy]; + newOperation.reAddAfterCancel = NO; + [weakSelf addTopAlertOperation:newOperation]; + } + alertOperation = nil; + }]; - // Check if we need to add a new operation. - if (topAlertObject && ![self checkAndUpdateExistingWith:topAlertObject]) { + [self.topAlertQueue addOperation:alertOperation]; + + // If the current running operation is persistent and has a lower queue priority then the added operation, cancel it and re-add it. + for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) { - __block MVMCoreTopAlertOperation *alertOperation = [[MVMCoreTopAlertOperation alloc] initWithTopAlertObject:topAlertObject]; - __weak typeof(self) weakSelf = self; - [alertOperation setCompletionBlock:^{ - - // If the alert was cancelled to show another with higher priority, re-add to the operation when cancelled to the queue. - if (alertOperation.reAddAfterCancel) { - [weakSelf showTopAlertWithObject:alertOperation.topAlertObject]; - } - alertOperation = nil; - }]; - - // This notification may only show for certain pages. - [self addPagesDependencyTo:alertOperation]; - - [self.topAlertQueue addOperation:alertOperation]; - - // If the current running operation is persistent and has a lower queue priority then the added operation, cancel it and re-add it. - for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) { - - if (operation.isExecuting && !operation.isCancelled && operation.topAlertObject.persistent && operation.queuePriority < alertOperation.queuePriority) { - operation.reAddAfterCancel = YES; - [operation cancel]; - break; - } + if (operation.isExecuting && !operation.isCancelled && operation.topAlertObject.persistent && operation.queuePriority < alertOperation.queuePriority && alertOperation.isReady) { + operation.reAddAfterCancel = YES; + [operation cancel]; + break; } } } +- (void)showTopAlertWithObject:(nullable MVMCoreTopAlertObject *)topAlertObject { + MVMCoreTopAlertOperation *alertOperation = [[MVMCoreTopAlertOperation alloc] initWithTopAlertObject:topAlertObject]; + [self addTopAlertOperation:alertOperation]; +} + - (void)showTopAlertErrorWithMessage:(nullable NSString *)message { MVMCoreTopAlertObject *topAlertObject = [[MVMCoreTopAlertObject alloc] initWithType:ValueTypeError message:message]; diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertOperation.m b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertOperation.m index bab7707..6eb8f04 100644 --- a/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertOperation.m +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertOperation.m @@ -247,4 +247,18 @@ [self dismissAlertView:YES forceful:YES]; } +- (id)copyWithZone:(nullable NSZone *)zone { + MVMCoreTopAlertOperation *copyObject = [[MVMCoreTopAlertOperation alloc] init]; + copyObject.topAlertObject = self.topAlertObject; + copyObject.paused = self.paused; + copyObject.reAddAfterCancel = self.reAddAfterCancel; + copyObject.queuePriority = self.queuePriority; + for (NSOperation *dependency in self.dependencies) { + [copyObject addDependency:dependency]; + } + copyObject.name = self.name; + copyObject.qualityOfService = self.qualityOfService; + return copyObject; +} + @end