// // Copyright 2021 New Vector Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // import Foundation import CommonKit import MatrixSDK import UIKit /// A set of user interactors commonly used across the app enum UserIndicatorType { case loading(label: String, isInteractionBlocking: Bool) case success(label: String) case failure(label: String) case custom(label: String, icon: UIImage?) } /// A presenter which can handle `UserIndicatorType` by creating the underlying `UserIndicator` /// and adding it to its `UserIndicatorQueue` protocol UserIndicatorTypePresenterProtocol { /// Present a new type of user indicator, such as loading spinner or success message. /// /// The presenter will internally convert the type into a `UserIndicator` and add it to its internal queue /// of other indicators. /// /// If the queue is empty, the indicator will be displayed immediately, otherwise it will be pending /// until the previously added indicators have completed / been cancelled. /// /// To remove an indicator, `cancel` or deallocate the returned `UserIndicator` func present(_ type: UserIndicatorType) -> UserIndicator /// The queue of user indicators owned by the presenter /// /// Clients can access the queue to add custom `UserIndicatorRequest`s /// above and beyond those defined by `UserIndicatorType` var queue: UserIndicatorQueue { get } } class UserIndicatorTypePresenter: UserIndicatorTypePresenterProtocol { private let presentationContext: UserIndicatorPresentationContext let queue: UserIndicatorQueue init(presentationContext: UserIndicatorPresentationContext) { self.presentationContext = presentationContext self.queue = UserIndicatorQueue() } convenience init(presentingViewController: UIViewController) { let context = StaticUserIndicatorPresentationContext(viewController: presentingViewController) self.init(presentationContext: context) } func present(_ type: UserIndicatorType) -> UserIndicator { let request = userIndicatorRequest(for: type) return queue.add(request) } private func userIndicatorRequest(for type: UserIndicatorType) -> UserIndicatorRequest { switch type { case .loading(let label, let isInteractionBlocking): if isInteractionBlocking { return fullScreenLoadingRequest(label: label) } else { return loadingRequest(label: label) } case .success(let label): return successRequest(label: label) case .failure(let label): return failureRequest(label: label) case .custom(let label, let icon): return customRequest(label: label, icon: icon) } } private func loadingRequest(label: String) -> UserIndicatorRequest { let presenter = ToastViewPresenter( viewState: .init( style: .loading, label: label ), presentationContext: presentationContext ) return UserIndicatorRequest( presenter: presenter, dismissal: .manual ) } private func fullScreenLoadingRequest(label: String) -> UserIndicatorRequest { let presenter = FullscreenLoadingViewPresenter( label: label, presentationContext: presentationContext ) return UserIndicatorRequest( presenter: presenter, dismissal: .manual ) } private func successRequest(label: String) -> UserIndicatorRequest { let presenter = ToastViewPresenter( viewState: .init( style: .success, label: label ), presentationContext: presentationContext ) return UserIndicatorRequest( presenter: presenter, dismissal: .timeout(1.5) ) } private func failureRequest(label: String) -> UserIndicatorRequest { let presenter = ToastViewPresenter( viewState: .init( style: .failure, label: label ), presentationContext: presentationContext ) return UserIndicatorRequest( presenter: presenter, dismissal: .timeout(1.5) ) } private func customRequest(label: String, icon: UIImage?) -> UserIndicatorRequest { let presenter = ToastViewPresenter( viewState: .init( style: .custom(icon: icon), label: label ), presentationContext: presentationContext ) return UserIndicatorRequest( presenter: presenter, dismissal: .manual ) } }