// // ModalController.swift // PrivadoVPN // // Created by Juraldinio on 4/15/21. // Copyright © 2021 Privado LLC. All rights reserved. // import Foundation protocol ModalControllerInput { var modalIsVisible: Bool { get } func modalShow() func modalHide() // swiftlint:disable function_parameter_count func modalUpdate(closable: Bool, imageName: String?, title: String?, description: String?, action: String?) // swiftlint:enable function_parameter_count } protocol ModalControllerOutput: AnyObject { func modalControllerClose(_ controller: ModalControllerInput) func modalControllerAction(_ controller: ModalControllerInput) } final class ModalController: NSViewController { // swiftlint:disable nesting private enum Constants { enum Geometry { static let leadingTrailing: CGFloat = 10 enum Close { static let width: CGFloat = 23 static let height: CGFloat = 23 static let top: CGFloat = 11 static let trailing: CGFloat = 20 } enum Title { static let bottomTopDescription: CGFloat = 15 static let height: CGFloat = 54 } enum Image { static let verticalSpace: CGFloat = 10 static let width: CGFloat = 25 static let height: CGFloat = 35 } enum Description { static let height: CGFloat = 100 } enum Action { static let cornerRadius: CGFloat = 18 static let widthMultiplier: CGFloat = 0.8 static let height: CGFloat = 39 static let topBottomDescription: CGFloat = 24 } } enum Color { static let background = NSColor(red: 4, green: 8, blue: 43) // static let background = NSColor(red: 21, green: 25, blue: 53) static let title = NSColor.white static let description = NSColor(red: 122, green: 134, blue: 190) static let buttonTitle = NSColor.white static let buttonClosableBackground = NSColor(red: 42, green: 216, blue: 153) static let buttonModalBackground = NSColor(red: 255, green: 143, blue: 0) } enum Font { enum Name { static let title = PrivadoConstants.Font.light static let description = PrivadoConstants.Font.regular static let action = PrivadoConstants.Font.black } enum Size { static let title: CGFloat = 18 static let description: CGFloat = 13 static let action: CGFloat = 14 } } enum Image { static let close = "close" } } // swiftlint:enable nesting weak var output: ModalControllerOutput? private let stackManager: JSViewControllersStackManager? private let closeButton: ImageView private let uppearImageView: ImageView private let titleField: NSTextField private var descriptionField: DynamicTextField private let actionButton: FlatButton private var descriptionHeight: NSLayoutConstraint? // MARK: - Init init(stackManager: JSViewControllersStackManager?) { self.stackManager = stackManager self.closeButton = Self.createCloseButton(image: Constants.Image.close) self.uppearImageView = Self.createImageView(image: nil) self.titleField = Self.createTextField(color: Constants.Color.title, font: NSFont(name: Constants.Font.Name.title, size: Constants.Font.Size.title), numberLines: 2) self.descriptionField = Self.createDynamicTextField(color: Constants.Color.description, font: NSFont(name: Constants.Font.Name.description, size: Constants.Font.Size.description), fontName: Constants.Font.Name.description) self.actionButton = Self.createActionButton(corner: Constants.Geometry.Action.cornerRadius, normalColor: Constants.Color.buttonClosableBackground, highlightColor: Constants.Color.buttonClosableBackground, titleColor: Constants.Color.buttonTitle, font: NSFont(name: Constants.Font.Name.action, size: Constants.Font.Size.action)) super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Lifecycle override func loadView() { let view = View(frame: .zero) view.autoresizesSubviews = true view.translatesAutoresizingMaskIntoConstraints = false view.wantsLayer = true view.viewBackgroundColor = Constants.Color.background self.view = view } override func viewDidLoad() { super.viewDidLoad() self.bind() self.layoutUI() } override func viewDidAppear() { super.viewDidAppear() if let superview = self.view.superview { NSLayoutConstraint.activate([ self.view.topAnchor.constraint(equalTo: superview.topAnchor), self.view.leadingAnchor.constraint(equalTo: superview.leadingAnchor), self.view.trailingAnchor.constraint(equalTo: superview.trailingAnchor), self.view.bottomAnchor.constraint(equalTo: superview.bottomAnchor) ]) } } // MARK: - Private private func bind() { self.closeButton.target = self self.closeButton.action = #selector(self.onCloseAction) self.actionButton.target = self self.actionButton.action = #selector(self.onActionButton) [self.closeButton, self.uppearImageView, self.titleField, self.descriptionField, self.actionButton ].forEach(self.view.addSubview) } private func layoutUI() { let constraint = self.descriptionField.heightAnchor.constraint(equalToConstant: Constants.Geometry.Description.height) self.descriptionHeight = constraint NSLayoutConstraint.activate([ self.closeButton.topAnchor.constraint(equalTo: self.view.topAnchor, constant: Constants.Geometry.Close.top), self.closeButton.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -Constants.Geometry.Close.trailing), self.closeButton.widthAnchor.constraint(equalToConstant: Constants.Geometry.Close.width), self.closeButton.heightAnchor.constraint(equalToConstant: Constants.Geometry.Close.height), // self.descriptionField.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: Constants.Geometry.leadingTrailing), self.descriptionField.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -Constants.Geometry.leadingTrailing), self.descriptionField.centerYAnchor.constraint(equalTo: self.view.centerYAnchor), constraint, // self.titleField.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: Constants.Geometry.leadingTrailing), self.titleField.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -Constants.Geometry.leadingTrailing), self.titleField.bottomAnchor.constraint(equalTo: self.descriptionField.topAnchor, constant: -Constants.Geometry.Title.bottomTopDescription), self.titleField.heightAnchor.constraint(equalToConstant: Constants.Geometry.Title.height), // self.uppearImageView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), self.uppearImageView.bottomAnchor.constraint(equalTo: self.titleField.topAnchor, constant: -Constants.Geometry.Image.verticalSpace), self.uppearImageView.widthAnchor.constraint(equalToConstant: Constants.Geometry.Image.width), self.uppearImageView.heightAnchor.constraint(equalToConstant: Constants.Geometry.Image.height), // self.actionButton.heightAnchor.constraint(equalToConstant: Constants.Geometry.Action.height), self.actionButton.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: Constants.Geometry.Action.widthMultiplier), self.actionButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), self.actionButton.topAnchor.constraint(equalTo: self.descriptionField.bottomAnchor, constant: Constants.Geometry.Action.topBottomDescription) ]) } // MARK: - on Handle @objc private func onCloseAction() { self.output?.modalControllerClose(self) } @objc private func onActionButton() { self.output?.modalControllerAction(self) } } // MARK: - ModalControllerInput extension ModalController: ModalControllerInput { func modalShow() { guard let stackManager = self.stackManager , let topController = stackManager.topViewController else { return } guard !topController.children.contains(self) else { return } topController.addChild(self) topController.view.addSubview(self.view) } func modalHide() { guard self.modalIsVisible else { return } self.view.removeFromSuperview() self.removeFromParent() } var modalIsVisible: Bool { self.parent.isExist } // swiftlint:disable function_parameter_count func modalUpdate(closable: Bool, imageName: String?, title: String?, description: String?, action: String?) { self.closeButton.isHidden = !closable if let imageName = imageName , let image = NSImage(named: imageName) { self.uppearImageView.image = image self.uppearImageView.isHidden = false self.descriptionHeight?.constant = 0 } else { self.descriptionHeight?.constant = Constants.Geometry.Description.height self.uppearImageView.isHidden = true } if let title = title { self.titleField.stringValue = title self.titleField.isHidden = false } else { self.titleField.isHidden = true } if let description = description { self.descriptionField.stringValue = description self.descriptionField.isHidden = false } else { self.descriptionField.isHidden = true } if let action = action { self.actionButton.title = action self.actionButton.isHidden = false let color = closable ? Constants.Color.buttonClosableBackground : Constants.Color.buttonModalBackground self.actionButton.backgroundNormalColor = color self.actionButton.backgroundHighlightColor = color } else { self.actionButton.isHidden = true } } // swiftlint:enable function_parameter_count }