322 lines
12 KiB
Swift
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()
|
|
}
|
|
}
|
|
|
|
*/
|