Files
2022-05-31 18:34:44 +04:00

348 lines
14 KiB
Swift

//
// ConnectionPanelView.swift
// PrivadoVPN
//
// Created by Juraldinio on 1/31/21.
// Copyright © 2021 Privado LLC. All rights reserved.
//
import Foundation
import AppKit
protocol ConnectionPanelViewInput: AnyObject {
func hide()
func progress(status: String, type: ConnectionPanelState)
func interactionEnabled(_ enable: Bool)
func location(connected: Bool, ip: String, city: String, secured: Bool, status: String)
}
protocol ConnectionPanelViewOutput: AnyObject {
func viewIsReady()
func viewWillDisappear()
func viewConnectButtonClicked()
}
final class ConnectionPanelView: NSView {
private enum Constants {
enum Image {
static let buttonDisconnected = "disconnected"
static let buttonConnected = "connected"
static let buttonProgress = "connecting"
static let connectionProtected = "connection.lock"
static let connectionUnProtected = "connection.unlock"
}
enum LocalizedString {
static let connecting = "connection.status.connecting"
static let disconnecting = "connection.status.disconnecting"
}
enum Color {
static let background = NSColor(calibratedRed: 21 / 255.0, green: 25 / 255.0, blue: 53 / 255.0, alpha: 1.0)
static let statusDisconnected = NSColor(calibratedRed: 228 / 255.0, green: 59 / 255.0, blue: 105 / 255.0, alpha: 1.0)
static let statusProgress = NSColor(calibratedRed: 255 / 255.0, green: 195 / 255.0, blue: 65 / 255.0, alpha: 1.0)
static let statusConnected = NSColor(calibratedRed: 40 / 255.0, green: 215 / 255.0, blue: 153 / 255.0, alpha: 1.0)
static let address = NSColor(red: 155, green: 160, blue: 224)
static let location = NSColor(calibratedRed: 122 / 255.0, green: 134 / 255.0, blue: 190 / 255.0, alpha: 1.0)
static let protection = NSColor(calibratedRed: 122 / 255.0, green: 134 / 255.0, blue: 190 / 255.0, alpha: 1.0)
static let loaderDisconnected = NSColor(red: 211, green: 73, blue: 107)
static let loaderConnected = NSColor(red: 104, green: 212, blue: 158)
static let loaderConnecting = NSColor(red: 246, green: 197, blue: 91)
static let loaderBack = NSColor(red: 46, green: 51, blue: 80)
static let loaderArc = NSColor(red: 126, green: 133, blue: 182)
}
enum Font {
// swiftlint:disable nesting
enum Name {
static let location = PrivadoConstants.Font.light
static let address = PrivadoConstants.Font.semiBold
static let status = PrivadoConstants.Font.bold
}
enum Size {
static let status: CGFloat = 15.0
static let address: CGFloat = 13.0
static let location: CGFloat = 13.0
}
// swiftlint:enable nesting
}
enum Geometry {
static let height: CGFloat = 269
static let buttonWidth: CGFloat = 110
static let buttonHeight: CGFloat = 110
static let buttonleading: CGFloat = 30
static let buttonBetween: CGFloat = 25
static let connectionStatusBottom: CGFloat = 7
static let secureImageWidth: CGFloat = 14
static let secureImageHeight: CGFloat = 14
static let secureImageTrailing: CGFloat = 5.6
static let connectionAddress: CGFloat = 10
}
}
private let output: ConnectionPanelViewOutput
private var connectionStatus: NSTextField?
private var connectionSecureStatus: ImageView?
private var connectionIpAddress: NSTextField?
private var connectionLocation: NSTextField?
private var connectionLoader: PrivadoLoader?
private var connectionButton: ConnectionButton?
private var status = ""
private var connectStatus: Bool?
private var inprogress = false
// MARK: - Init
init(output: ConnectionPanelViewOutput) {
self.output = output
super.init(frame: .zero)
self.isHidden = true
self.translatesAutoresizingMaskIntoConstraints = false
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()
}
}
override func removeFromSuperview() {
self.output.viewWillDisappear()
super.removeFromSuperview()
}
override func updateLayer() {
super.updateLayer()
self.connectionLoader?.animation(clockwise: false)
}
// MARK: - Private
private func createUI() {
let button = ConnectionButton(frame: .zero)
button.cornerRadius = 19
button.textFont = NSFont(name: PrivadoConstants.Font.bold, size: 14)
self.addSubview(button)
button.target = self
button.action = #selector(self.handleConnectionClick)
self.connectionButton = button
self.addSubview(button)
let statusFont = NSFont(name: PrivadoConstants.Font.semiBold, size: 14)
let status = self.initializeTextField(color: Constants.Color.statusDisconnected, font: statusFont, alignment: .left)
status.isHidden = true
status.textColor = .white
self.connectionStatus = status
self.addSubview(status)
let secureStatus = self.initializeImageView(resource: Constants.Image.connectionUnProtected, color: Constants.Color.protection)
self.connectionSecureStatus = secureStatus
self.addSubview(secureStatus)
let addressFont = NSFont(name: PrivadoConstants.Font.medium, size: 15)
let ipAddress = self.initializeTextField(color: Constants.Color.address, font: addressFont, alignment: .center)
self.connectionIpAddress = ipAddress
self.addSubview(ipAddress)
let locationFont = NSFont(name: PrivadoConstants.Font.semiBold, size: 16)
let location = self.initializeTextField(color: Constants.Color.address, font: locationFont, alignment: .center)
self.connectionLocation = location
self.addSubview(location)
let loader = PrivadoLoader(frame: .zero, isButton: true)
loader.translatesAutoresizingMaskIntoConstraints = false
loader.isEnabled = true
loader.target = self
loader.action = #selector(self.handleConnectionClick)
loader.backCircleColor = .clear
loader.arcColor = Constants.Color.loaderArc
// loader.keyholeColor = Constants.Color.loaderArc
loader.keyholeColor = NSColor(red: 229, green: 93, blue: 129)
loader.arcWidth = 4.5
loader.hasShadow = false
self.connectionLoader = loader
self.addSubview(loader)
}
private func activateLayout() {
guard let connectionLoader = self.connectionLoader
, let connectionStatus = self.connectionStatus
, let connectionSecureStatus = self.connectionSecureStatus
, let connectionIpAddress = self.connectionIpAddress
, let connectionLocation = self.connectionLocation
, let connectionButton = self.connectionButton else {
return
}
NSLayoutConstraint.activate([
self.heightAnchor.constraint(equalToConstant: Constants.Geometry.height),
//
connectionLoader.widthAnchor.constraint(equalToConstant: Constants.Geometry.buttonWidth),
connectionLoader.heightAnchor.constraint(equalToConstant: Constants.Geometry.buttonHeight),
connectionLoader.centerXAnchor.constraint(equalTo: self.centerXAnchor),
connectionLoader.topAnchor.constraint(equalTo: connectionIpAddress.bottomAnchor, constant: 20),
//
connectionButton.widthAnchor.constraint(equalToConstant: 200),
connectionButton.heightAnchor.constraint(equalToConstant: 40),
connectionButton.topAnchor.constraint(equalTo: connectionLoader.bottomAnchor, constant: 20),
connectionButton.centerXAnchor.constraint(equalTo: self.centerXAnchor),
//
connectionStatus.topAnchor.constraint(equalTo: self.topAnchor),
connectionStatus.centerXAnchor.constraint(equalTo: self.centerXAnchor),
//
connectionLocation.topAnchor.constraint(equalTo: self.topAnchor),
connectionLocation.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20),
connectionLocation.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20),
//
connectionIpAddress.topAnchor.constraint(equalTo: connectionLocation.bottomAnchor, constant: 10),
connectionIpAddress.centerXAnchor.constraint(equalTo: self.centerXAnchor),
//
connectionSecureStatus.trailingAnchor.constraint(equalTo: connectionIpAddress.leadingAnchor, constant: -5),
connectionSecureStatus.topAnchor.constraint(equalTo: connectionLocation.bottomAnchor, constant: 10),
connectionSecureStatus.widthAnchor.constraint(equalToConstant: Constants.Geometry.secureImageWidth),
connectionSecureStatus.heightAnchor.constraint(equalToConstant: Constants.Geometry.secureImageHeight),
])
}
// MARK: - Handlers
@objc
private func handleConnectionClick() {
self.output.viewConnectButtonClicked()
self.connectionLoader?.startAnimation(clockwise: false)
}
override func layoutSubtreeIfNeeded() {
super.layoutSubtreeIfNeeded()
self.connectionLoader?.startAnimation(clockwise: false)
}
}
// MARK: - ConnectionPanelViewInput
extension ConnectionPanelView: ConnectionPanelViewInput {
func interactionEnabled(_ enable: Bool) {
self.connectionLoader?.isEnabled = enable
}
func hide() {
self.isHidden = true
}
func progress(status: String, type: ConnectionPanelState) {
self.inprogress = true
//
if status != self.status {
self.status = status
switch type {
case .connecting:
self.connectionStatus?.isHidden = false
self.connectionStatus?.stringValue = status
self.connectionButton?.buttonIsEnabled = false
self.connectionLoader?.connecting = true
self.connectionLoader?.startConnectionAnimation {
if self.inprogress {
self.connectionLoader?.connectingAnimation()
}
}
case .disconnecting:
self.connectionStatus?.isHidden = false
self.connectionStatus?.stringValue = status
self.connectionButton?.buttonIsEnabled = false
self.connectionLoader?.connecting = false
self.connectionLoader?.disconnectAnimation()
case .reconnecting:
self.connectionStatus?.isHidden = false
self.connectionStatus?.stringValue = status
self.connectionButton?.buttonIsEnabled = false
self.connectStatus = false
self.connectionLoader?.connecting = true
self.connectionLoader?.startConnectionAnimation {
if self.inprogress {
self.connectionLoader?.connectingAnimation()
}
}
default:
break
}
}
//
self.connectionIpAddress?.stringValue = ""
self.connectionLocation?.stringValue = ""
self.connectionSecureStatus?.image = nil
//
self.isHidden = false
}
// swiftlint:disable function_parameter_count
func location(connected: Bool, ip: String, city: String, secured: Bool, status: String) {
if let connectionButton = self.connectionButton {
connectionButton.buttonIsEnabled = true
connectionButton.state = connected ? .disconnect : .connect
}
if let connectionLoader = self.connectionLoader {
if connected != self.connectStatus {
self.connectStatus = connected
connectionLoader.isDisconnected = !connected
if connected {
self.inprogress = false
connectionLoader.connecting = false
connectionLoader.connectedAnimation()
}
}
}
if let connectionStatus = self.connectionStatus {
connectionStatus.isHidden = !connected
connectionStatus.stringValue = status
}
if let connectionIpAddress = self.connectionIpAddress {
connectionIpAddress.stringValue = ip
}
if let connectionLocation = self.connectionLocation {
connectionLocation.stringValue = city
connectionLocation.isHidden = connected
}
if let connectionSecureStatus = self.connectionSecureStatus {
connectionSecureStatus.image = NSImage(named: secured ? Constants.Image.connectionProtected : Constants.Image.connectionUnProtected)
}
self.isHidden = false
}
// swiftlint:enable function_parameter_count
}