rename folder
This commit is contained in:
parent
9ea5443639
commit
30c6355973
@ -2033,7 +2033,7 @@
|
||||
D29DF11921E68467003B2FB9 /* Containers */,
|
||||
D22D1F582204D2590077CEC0 /* Legacy */,
|
||||
D29DF10F21E67A7D003B2FB9 /* BaseControllers */,
|
||||
D29DF11E21E6851E003B2FB9 /* TopAlert */,
|
||||
D29DF11E21E6851E003B2FB9 /* TopNotification */,
|
||||
D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */,
|
||||
D29DF0D021E404D4003B2FB9 /* Info.plist */,
|
||||
);
|
||||
@ -2156,9 +2156,10 @@
|
||||
path = Containers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D29DF11E21E6851E003B2FB9 /* TopAlert */ = {
|
||||
D29DF11E21E6851E003B2FB9 /* TopNotification */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AFA4932129E5EF2E001A9663 /* TopNotificationHandler.swift */,
|
||||
D2ED2814254B0EE400A1C293 /* MVMCoreGlobalTopAlertDelegateProtocol.h */,
|
||||
D2ED2805254B0EB700A1C293 /* MVMCoreTopAlertAnimationDelegateProtocol.h */,
|
||||
D2ED2809254B0EB700A1C293 /* MVMCoreTopAlertDelegateProtocol.h */,
|
||||
@ -2180,7 +2181,7 @@
|
||||
D29DF12721E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.h */,
|
||||
D29DF12121E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m */,
|
||||
);
|
||||
path = TopAlert;
|
||||
path = TopNotification;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D29DF13321E68604003B2FB9 /* Styles */ = {
|
||||
@ -2531,7 +2532,6 @@
|
||||
AF7E509729E477C0009DC2AD /* AlertController.swift */,
|
||||
AF7E509629E477C0009DC2AD /* AlertHandler.swift */,
|
||||
AFA4931F29E5CA73001A9663 /* AlertOperation.swift */,
|
||||
AFA4932129E5EF2E001A9663 /* TopNotificationHandler.swift */,
|
||||
);
|
||||
path = Alerts;
|
||||
sourceTree = "<group>";
|
||||
|
||||
@ -80,7 +80,7 @@ CGFloat const PanelAnimationDuration = 0.2;
|
||||
return [MVMCoreActionUtility initializerClassCheck:[MVMCoreUISession sharedGlobal].splitViewController classToVerify:self];
|
||||
}
|
||||
|
||||
+ (nullable instancetype)setup:(nullable UIViewController <MVMCoreUIPanelProtocol> *)leftPanel rightPanel:(nullable UIViewController <MVMCoreUIPanelProtocol> *)rightPanel {
|
||||
+ (nullable instancetype)setup:(nullable UIViewController <MVMCoreUIPanelProtocol> *)leftPanel rightPanel:(nullable UIViewController <MVMCoreUIPanelProtocol> *)rightPanel topAlertView:(nonnull UIView <MVMCoreTopAlertViewProtocol>*)topAlertView {
|
||||
MVMCoreUISplitViewController *splitViewController = [[self alloc] initWithLeftPanel:leftPanel rightPanel:rightPanel];
|
||||
[MVMCoreUISession sharedGlobal].splitViewController = splitViewController;
|
||||
return splitViewController;
|
||||
|
||||
@ -10,7 +10,6 @@ import UIKit
|
||||
import MVMCore
|
||||
|
||||
@objcMembers open class CoreUIObject: MVMCoreObject {
|
||||
public var globalTopAlertDelegate: MVMCoreGlobalTopAlertDelegateProtocol?
|
||||
public var alertHandler: AlertHandler?
|
||||
public var topNotificationHandler: TopNotificationHandler?
|
||||
|
||||
|
||||
@ -7,6 +7,233 @@
|
||||
//
|
||||
|
||||
import MVMCore
|
||||
import Dispatch
|
||||
|
||||
public protocol NotificationTransitionDelegateProtocol {
|
||||
@MainActor
|
||||
func show(notification: UIView) async
|
||||
|
||||
@MainActor
|
||||
func hide(notification: UIView) async
|
||||
|
||||
@MainActor
|
||||
func update(with model: TopNotificationModel)
|
||||
}
|
||||
|
||||
public class NotificationOperation: MVMCoreOperation {
|
||||
|
||||
private let notification: UIView
|
||||
|
||||
private var notificationModel: TopNotificationModel
|
||||
|
||||
/// The delegate that manages transitioning the notification.
|
||||
private let transitionDelegate: NotificationTransitionDelegateProtocol
|
||||
|
||||
/// The notification animation transition operation (show or hide).
|
||||
private var transitionOperation: MVMCoreOperation?
|
||||
|
||||
/// The stop timer for non-persistent notifications.
|
||||
private var timerSource: DispatchSourceTimer?
|
||||
|
||||
/// Determines if the operation is ready. Certain notifications are only meant to be displayed on certain pages.
|
||||
public var isDisplayable: Bool {
|
||||
get {
|
||||
var isDisplayable: Bool = true
|
||||
displayableQueue.sync {
|
||||
isDisplayable = _isDisplayable
|
||||
}
|
||||
return isDisplayable
|
||||
}
|
||||
set {
|
||||
guard newValue != isDisplayable else { return }
|
||||
displayableQueue.async(flags: .barrier) { [weak self] in
|
||||
self?._isDisplayable = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
private var displayableQueue = DispatchQueue(label: "displayable", attributes: .concurrent)
|
||||
private var _isDisplayable: Bool = true {
|
||||
willSet {
|
||||
guard super.isReady else { return }
|
||||
willChangeValue(for: \.isReady)
|
||||
}
|
||||
didSet {
|
||||
guard super.isReady else { return }
|
||||
didChangeValue(for: \.isReady)
|
||||
}
|
||||
}
|
||||
|
||||
/// This operation is ready only if this notification is allowed to show.
|
||||
public override var isReady: Bool {
|
||||
get {
|
||||
guard !isCancelled else { return super.isReady }
|
||||
return super.isReady && isDisplayable
|
||||
}
|
||||
}
|
||||
|
||||
private actor Properties {
|
||||
private var isDisplayed: Bool = false
|
||||
private var isAnimating: Bool = false
|
||||
|
||||
func set(displayed: Bool) {
|
||||
isDisplayed = displayed
|
||||
}
|
||||
|
||||
func getIsDisplayed() -> Bool {
|
||||
return isDisplayed
|
||||
}
|
||||
|
||||
func set(animating: Bool) {
|
||||
isAnimating = animating
|
||||
}
|
||||
|
||||
func getIsAnimating() -> Bool {
|
||||
return isAnimating
|
||||
}
|
||||
}
|
||||
private var properties = Properties()
|
||||
|
||||
// A flag for tracking if the operation needs to be re-added because it was cancelled for a higher priority notification.
|
||||
public var reAddAfterCancel = false
|
||||
|
||||
public init(with notification: UIView, notificationModel: TopNotificationModel, transitionDelegate: NotificationTransitionDelegateProtocol) {
|
||||
self.notification = notification
|
||||
self.notificationModel = notificationModel
|
||||
self.transitionDelegate = transitionDelegate
|
||||
super.init()
|
||||
queuePriority = notificationModel.priority
|
||||
}
|
||||
|
||||
public override func main() {
|
||||
guard !checkAndHandleForCancellation() else { return }
|
||||
add {
|
||||
await self.showNotification()
|
||||
guard !self.isCancelled else {
|
||||
// Cancelled, dismiss immediately.
|
||||
self.stop()
|
||||
return
|
||||
}
|
||||
self.updateStopTimer()
|
||||
}
|
||||
}
|
||||
|
||||
public func stop() {
|
||||
if let timerSource = timerSource {
|
||||
timerSource.cancel()
|
||||
}
|
||||
Task {
|
||||
guard await properties.getIsDisplayed(),
|
||||
await !properties.getIsAnimating() else { return }
|
||||
add {
|
||||
await self.hideNotification()
|
||||
guard !self.isCancelled,
|
||||
!self.notificationModel.persistent else { return }
|
||||
self.markAsFinished()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the transition of the notification to the queue.
|
||||
private func add(transition: @escaping () async -> Void) {
|
||||
Task {
|
||||
guard await properties.getIsDisplayed(),
|
||||
await !properties.getIsAnimating() else { return }
|
||||
transitionOperation = MVMCoreBlockOperation(block: { [weak self] blockOperation in
|
||||
guard !blockOperation.checkAndHandleForCancellation() else { return }
|
||||
guard let self = self else {
|
||||
blockOperation.markAsFinished()
|
||||
return
|
||||
}
|
||||
Task {
|
||||
await transition()
|
||||
blockOperation.markAsFinished()
|
||||
}
|
||||
})
|
||||
transitionOperation?.completionBlock = { [weak self] in
|
||||
self?.transitionOperation = nil
|
||||
}
|
||||
// Add the animation to the navigation queue to avoid animation collisions.
|
||||
await MVMCoreNavigationHandler.shared()?.addNavigationOperation(transitionOperation!)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateStopTimer() {
|
||||
if let timerSource = timerSource {
|
||||
timerSource.cancel()
|
||||
}
|
||||
guard !notificationModel.persistent else { return }
|
||||
timerSource = DispatchSource.makeTimerSource()
|
||||
timerSource?.setEventHandler(handler: { [weak self] in
|
||||
print("SSSS TIMER EVENT FIRED FOR: \(String(describing: self?.notificationModel.type))")
|
||||
guard let self = self,
|
||||
!self.isFinished,
|
||||
!self.checkAndHandleForCancellation() else { return }
|
||||
self.stop()
|
||||
})
|
||||
timerSource?.setCancelHandler(handler: { [weak self] in
|
||||
print("SSSS TIMER EVENT CANCELLED FOR: \(String(describing: self?.notificationModel.type))")
|
||||
})
|
||||
timerSource?.schedule(deadline: .now() + .seconds(notificationModel.dismissTime))
|
||||
}
|
||||
|
||||
public override func cancel() {
|
||||
super.cancel()
|
||||
Task {
|
||||
if await !properties.getIsDisplayed() {
|
||||
// Cancel any pending show transitions.
|
||||
transitionOperation?.cancel()
|
||||
}
|
||||
|
||||
// Do nothing if animating.
|
||||
guard await !properties.getIsAnimating() else { return }
|
||||
if await properties.getIsDisplayed() {
|
||||
stop()
|
||||
} else if isExecuting {
|
||||
markAsFinished()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func showNotification() async {
|
||||
await properties.set(animating: true)
|
||||
await transitionDelegate.show(notification: notification)
|
||||
await properties.set(displayed: true)
|
||||
await properties.set(animating: false)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func hideNotification() async {
|
||||
await properties.set(animating: true)
|
||||
await transitionDelegate.hide(notification: notification)
|
||||
await properties.set(displayed: false)
|
||||
await properties.set(animating: false)
|
||||
}
|
||||
|
||||
/// Updates the notification with the new model.
|
||||
public func update(with model: TopNotificationModel) {
|
||||
self.notificationModel = model
|
||||
queuePriority = model.priority
|
||||
guard isExecuting,
|
||||
!isCancelled else { return }
|
||||
updateStopTimer()
|
||||
Task { @MainActor in
|
||||
transitionDelegate.update(with: notificationModel)
|
||||
}
|
||||
}
|
||||
|
||||
func copy(with zone: NSZone? = nil) -> Any {
|
||||
let operation = NotificationOperation(with: notification, notificationModel: notificationModel, transitionDelegate: transitionDelegate)
|
||||
operation.reAddAfterCancel = reAddAfterCancel
|
||||
operation.isDisplayable = isDisplayable
|
||||
for dependency in dependencies {
|
||||
operation.addDependency(dependency)
|
||||
}
|
||||
operation.name = name
|
||||
operation.qualityOfService = qualityOfService
|
||||
return operation
|
||||
}
|
||||
}
|
||||
|
||||
public class TopNotificationHandler {
|
||||
|
||||
@ -234,3 +461,10 @@ extension TopNotificationHandler: MVMCorePresentationDelegateProtocol {
|
||||
reevaluteQueue()
|
||||
}
|
||||
}
|
||||
|
||||
extension NotificationOperation {
|
||||
/// Updates if the operation is displayable based on the page type.
|
||||
func updateDisplayable(by pageType: String) {
|
||||
isDisplayable = notificationModel.pages?.contains(pageType) ?? true
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user