Files

299 lines
11 KiB
Swift

//
// 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
}