Files

322 lines
12 KiB
Swift

//
// 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()
}
}
*/