// // TrafficPanelView.swift // PrivadoVPN // // Created by Juraldinio on 1/31/21. // Copyright © 2021 Privado LLC. All rights reserved. // import Foundation import AppKit import SwiftUI protocol TrafficPanelViewInput: AnyObject { func updateView(progress: Double) func updateView(message: NSAttributedString) func updateView(visibility: Bool) func updateView(button: String) } protocol TrafficPanelViewOutput: AnyObject { func viewIsReady(viewInput: TrafficPanelViewInput) func viewWillDisappear(viewInput: TrafficPanelViewInput) func viewButtonClicked(viewInput: TrafficPanelViewInput) } final class TrafficPanelView: NSView { private enum Constants { enum Color { static let progressBackground = NSColor(calibratedRed: 24 / 255.0, green: 28 / 255.0, blue: 76 / 255.0, alpha: 5.0) static let progress = NSColor(calibratedRed: 122 / 255.0, green: 134 / 255.0, blue: 190 / 255.0, alpha: 1.0) static let buttonBackground = NSColor(calibratedRed: 40 / 255.0, green: 215 / 255.0, blue: 153 / 255.0, alpha: 1.0) static let buttonActive = NSColor(calibratedRed: 40 / 255.0, green: 215 / 255.0, blue: 153 / 255.0, alpha: 1.0) static let buttonText = NSColor(calibratedRed: 45 / 255.0, green: 51 / 255.0, blue: 82 / 255.0, alpha: 1.0) // static let progressLow = NSColor(calibratedRed: 40 / 255.0, green: 215 / 255.0, blue: 153 / 255.0, alpha: 1.0) static let progressMedium = NSColor(calibratedRed: 240 / 255.0, green: 131 / 255.0, blue: 51 / 255.0, alpha: 1.0) static let progressHigh = NSColor(calibratedRed: 228 / 255.0, green: 59 / 255.0, blue: 105 / 255.0, alpha: 1.0) // static let messageProgress = NSColor(calibratedRed: 21 / 255.0, green: 25 / 255.0, blue: 53 / 255.0, alpha: 1.0) } enum Font { // swiftlint:disable nesting enum Name { static let black = PrivadoConstants.Font.bold static let progress = PrivadoConstants.Font.medium } enum Size { static let progress: CGFloat = 11.0 static let upgrade: CGFloat = 10.0 } // swiftlint:enable nesting } enum Geometry { static let height: CGFloat = 31 static let buttonWidth: CGFloat = 97 static let remainingLeading: CGFloat = 25 static let remainingTrailing: CGFloat = 25 } enum LocalizedString { static let actionButton = "trafficUsage.button.text" } } private let output: TrafficPanelViewOutput private var progressView: ProgressView? private var remainField: NSTextField? private var upgradeButton: FlatButton? private var trailingProgressConstraint: NSLayoutConstraint? // MARK: - Init init(output: TrafficPanelViewOutput) { self.output = output super.init(frame: .zero) self.translatesAutoresizingMaskIntoConstraints = false self.viewBackgroundColor = NSColor(red: 43, green: 50, blue: 137) self.isHidden = true self.createUI() self.activateLayout() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Lyfecycle override func viewDidMoveToSuperview() { super.viewDidMoveToSuperview() if self.superview.isExist { self.output.viewIsReady(viewInput: self) } } override func removeFromSuperview() { self.output.viewWillDisappear(viewInput: self) super.removeFromSuperview() } // MARK: - Private private func createUI() { let remainingField = NSTextField() remainingField.usesSingleLineMode = true remainingField.maximumNumberOfLines = 1 remainingField.isBezeled = false remainingField.drawsBackground = false remainingField.isEditable = false remainingField.isSelectable = false remainingField.translatesAutoresizingMaskIntoConstraints = false // remainingField.font = NSFont(name: PrivadoConstants.Font.medium, size: 12) remainingField.textColor = .white remainingField.alignment = .left self.remainField = remainingField self.addSubview(remainingField) let progressView = ProgressView() progressView.progressColor = Constants.Color.progress progressView.viewBackgroundColor = Constants.Color.progressBackground self.progressView = progressView self.addSubview(progressView) let upgradeFont = NSFont(name: PrivadoConstants.Font.bold, size: Constants.Font.Size.upgrade) let upgradeButton = self.initializeUpgradeButton(backgound: Constants.Color.buttonBackground, font: upgradeFont) upgradeButton.target = self upgradeButton.action = #selector(self.handleUpgradeButton) self.upgradeButton = upgradeButton self.addSubview(upgradeButton) } private func activateLayout() { guard let progressView = self.progressView , let upgradeButton = self.upgradeButton , let remainField = self.remainField else { return } NSLayoutConstraint.activate([ self.heightAnchor.constraint(equalToConstant: 56), upgradeButton.widthAnchor.constraint(equalToConstant: 80), upgradeButton.heightAnchor.constraint(equalToConstant: 30), upgradeButton.topAnchor.constraint(equalTo: progressView.bottomAnchor, constant: 9), upgradeButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20), // progressView.leadingAnchor.constraint(equalTo: self.leadingAnchor), progressView.topAnchor.constraint(equalTo: self.topAnchor), progressView.heightAnchor.constraint(equalToConstant: 4), progressView.trailingAnchor.constraint(equalTo: self.trailingAnchor), // remainField.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20), remainField.topAnchor.constraint(equalTo: self.topAnchor, constant: 20), remainField.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -20) ]) } @objc private func handleUpgradeButton() { self.output.viewButtonClicked(viewInput: self) } } // MARK: - TrafficPanelViewInput extension TrafficPanelView: TrafficPanelViewInput { func updateView(progress: Double) { guard let progressView = self.progressView else { return } let progressColor: NSColor switch progress { case 0..<0.5: progressColor = Constants.Color.progressLow case 0.5..<0.9: progressColor = Constants.Color.progressMedium default: progressColor = Constants.Color.progressHigh } progressView.progress = CGFloat(progress) progressView.progressColor = progressColor } func updateView(message: NSAttributedString) { guard let remainField = self.remainField else { return } remainField.attributedStringValue = message } func updateView(visibility: Bool) { self.isHidden = !visibility } func updateView(button title: String) { guard let upgradeButton = self.upgradeButton else { return } let attributedTitle = NSMutableAttributedString(string: title) let range = NSRange(location: 0, length: attributedTitle.length) attributedTitle.addAttributes([.foregroundColor: Constants.Color.buttonText], range: range) upgradeButton.attributedTitle = attributedTitle } } /* protocol TrafficCounterViewOutput: AnyObject { func viewIsReady() func didTapUpgradeButton() } final class TrafficCounterView: UIView, TrafficCounterViewInput { override func layoutSubviews() { super.layoutSubviews() self.dropShadow(shadowColor: Constants.Color.shadow, offset: Constants.Geometry.shadowOffset, shadowRadius: Constants.Geometry.shadowRadius, cornerRadius: Constants.Geometry.cornerRadius, corners: [.topLeft, .topRight]) } // MARK: - Private private func configureUI() { self.translatesAutoresizingMaskIntoConstraints = false self.backgroundColor = Constants.Color.background self.layer.cornerRadius = Constants.Geometry.cornerRadius self.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] self.clipsToBounds = false self.addSubview(self.containerView) NSLayoutConstraint.activate([ self.containerView.topAnchor.constraint(equalTo: self.topAnchor), self.containerView.leadingAnchor.constraint(equalTo: self.leadingAnchor), self.containerView.trailingAnchor.constraint(equalTo: self.trailingAnchor), self.containerView.bottomAnchor.constraint(equalTo: self.bottomAnchor) ]) let progressView = self.initializeProgressBar() NSLayoutConstraint.activate([ progressView.topAnchor.constraint(equalTo: self.topAnchor), progressView.leadingAnchor.constraint(equalTo: self.leadingAnchor), progressView.trailingAnchor.constraint(equalTo: self.trailingAnchor), progressView.heightAnchor.constraint(equalToConstant: Constants.Geometry.progressHeight) ]) /// TODO: - uncomment when IAP is ready. // let actionButton = self.initializeActionButton() // NSLayoutConstraint.activate([ // actionButton.topAnchor.constraint(equalTo: progressView.bottomAnchor, constant: Constants.Geometry.actionButtonTop), // actionButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -Constants.Geometry.actionButtonIndentX), // actionButton.heightAnchor.constraint(equalToConstant: Constants.Geometry.actionButtonHeight) // ]) let messageLabel = self.initializeMessageLabel() /// TODO: - add when IAP is ready. // NSLayoutConstraint.activate([ // messageLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: Constants.Geometry.messageLeft), // messageLabel.trailingAnchor.constraint(lessThanOrEqualTo: actionButton.leadingAnchor, constant: -Constants.Geometry.actionButtonIndentX), // messageLabel.centerYAnchor.constraint(equalTo: actionButton.centerYAnchor), // ]) NSLayoutConstraint.activate([ messageLabel.leadingAnchor.constraint(greaterThanOrEqualTo: self.leadingAnchor, constant: Constants.Geometry.messageLeft), messageLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor), messageLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: Constants.Geometry.messageTop) ]) } private func initializeProgressBar() -> UIProgressView { let view = UIProgressView(progressViewStyle: .bar) view.translatesAutoresizingMaskIntoConstraints = false view.trackTintColor = Constants.Color.progressBarTrack view.progressTintColor = Constants.Color.progressLow self.containerView.addSubview(view) self.progressView = view return view } private func initializeMessageLabel() -> UILabel { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.numberOfLines = 0 label.lineBreakMode = .byTruncatingTail label.textAlignment = .center label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) self.containerView.addSubview(label) self.messageLabel = label return label } @objc private func onActionButtonTap() { self.output.didTapUpgradeButton() } } */