395 lines
18 KiB
Swift
395 lines
18 KiB
Swift
//
|
|
// AccountPreferencesViewController.swift
|
|
// Cyberlock
|
|
//
|
|
// Created by Jura on 9/23/19.
|
|
// Copyright © 2019 Omicronmedia. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import AppKit
|
|
|
|
enum AccountPreferencesHyperLinkType {
|
|
case termOfService
|
|
case privacyPolicy
|
|
}
|
|
|
|
protocol AccountPreferencesViewInput: AnyObject {
|
|
func current(record: AccountPreferenceRecord?, buttonVisible: Bool?, email: String?)
|
|
}
|
|
|
|
protocol AccountPreferencesViewOutput: AnyObject {
|
|
func isReady()
|
|
func signout()
|
|
func clickedHyper(link: AccountPreferencesHyperLinkType)
|
|
func viewButtonClicked()
|
|
func requireCopyUsername()
|
|
func logout()
|
|
}
|
|
|
|
final class AccountPreferencesViewController: NSViewController, PreferencePane, AccountPreferencesViewInput, HyperlinkTextFieldDelegate {
|
|
|
|
// swiftlint:disable nesting
|
|
struct Constants {
|
|
|
|
enum Image {
|
|
static let copy = "preference_copy-1"
|
|
}
|
|
|
|
enum Localized {
|
|
|
|
static let signout = "preference.authentication.signout"
|
|
static let upgrade = "preference.authentication.plan.upgrade"
|
|
static let termOfService = "preference.authentication.termsOfService"
|
|
static let privacyPolicy = "preference.authentication.privacyPolicy"
|
|
}
|
|
|
|
enum Geometry {
|
|
|
|
static let leading: CGFloat = 20
|
|
static let copyButtonTrailing: CGFloat = 5.0
|
|
static let fontSize: CGFloat = 14.0
|
|
static let accountTopSpace: CGFloat = 20
|
|
static let accountLeadingSpace: CGFloat = 30
|
|
static let usernameTopSpace: CGFloat = 20
|
|
static let innerSpace: CGFloat = 6
|
|
static let subscriptionTopSpace: CGFloat = 20
|
|
static let updateTopSpace: CGFloat = 40
|
|
static let updateLeadingSpace: CGFloat = 165
|
|
static let updateWidth: CGFloat = 200
|
|
static let updateHeight: CGFloat = 35
|
|
static let hyperlinkTop: CGFloat = 40
|
|
}
|
|
|
|
enum Color {
|
|
|
|
static let hyperlinkColor = NSColor(red: 90, green: 98, blue: 140)
|
|
static let valueColor = NSColor(red: 124, green: 134, blue: 186)
|
|
static let backgroundNormalColor = NSColor(red: 42, green: 216, blue: 153)
|
|
static let backgroundHighlightColor = NSColor(red: 23, green: 176, blue: 121)
|
|
}
|
|
|
|
enum Text {
|
|
|
|
static let accountDetails = "Account Details"
|
|
static let username = "Username"
|
|
static let subscription = "Subscription"
|
|
}
|
|
|
|
enum Font {
|
|
|
|
enum Size {
|
|
static let account: CGFloat = 16
|
|
static let username: CGFloat = 14
|
|
static let value: CGFloat = 12
|
|
static let updateButtonTitle: CGFloat = 12
|
|
}
|
|
}
|
|
}
|
|
// swiftlint:enable nesting
|
|
|
|
private let usernameCopyButton: NSButton
|
|
private let usernameValueField: NSTextField
|
|
private let usernameField: NSTextField
|
|
private let subscription: NSTextField
|
|
private let planField: NSTextField
|
|
private let currentPlantField: NSTextField
|
|
private let expirationDateField: NSTextField
|
|
private let expireField: NSTextField
|
|
private let accountDetailesField: NSTextField
|
|
private var updateButton: FlatButton
|
|
private var accountSeparator: View
|
|
private var subscriptionSeparator: View
|
|
private var logoutButton: StrokeButton
|
|
|
|
private var termsOfService: HyperlinkTextField
|
|
private var privacyPolicy: HyperlinkTextField
|
|
|
|
private let output: AccountPreferencesViewOutput
|
|
|
|
// MARK: - Init
|
|
|
|
init(output: AccountPreferencesViewOutput) {
|
|
self.output = output
|
|
|
|
let image = NSImage(named: Constants.Image.copy)
|
|
self.usernameCopyButton = Self.createButton(image: image)
|
|
self.usernameValueField = Self.createTextField()
|
|
self.planField = Self.createTextField()
|
|
self.expireField = Self.createTextField()
|
|
self.accountDetailesField = Self.createTextField()
|
|
self.usernameField = Self.createTextField()
|
|
self.subscription = Self.createTextField()
|
|
self.currentPlantField = Self.createTextField()
|
|
self.expirationDateField = Self.createTextField()
|
|
self.updateButton = FlatButton(frame: .zero)
|
|
self.privacyPolicy = HyperlinkTextField(frame: .zero, font: nil)
|
|
self.termsOfService = HyperlinkTextField(frame: .zero, font: nil)
|
|
self.accountSeparator = View(frame: .zero)
|
|
self.subscriptionSeparator = View(frame: .zero)
|
|
self.logoutButton = StrokeButton(frame: .zero)
|
|
super.init(nibName: nil, bundle: nil)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
// MARK: - Lifecycle
|
|
|
|
override func loadView() {
|
|
let view = NSView(frame: .zero)
|
|
view.translatesAutoresizingMaskIntoConstraints = false
|
|
self.view = view
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
self.configureUI()
|
|
self.layoutUI()
|
|
self.output.isReady()
|
|
}
|
|
|
|
override func viewDidAppear() {
|
|
super.viewDidAppear()
|
|
NSLayoutConstraint.activate([
|
|
view.widthAnchor.constraint(equalToConstant: 500),
|
|
view.heightAnchor.constraint(equalToConstant: 524)
|
|
])
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
private func configureUI() {
|
|
|
|
self.logoutButton.translatesAutoresizingMaskIntoConstraints = false
|
|
self.logoutButton.cornerRadius = 17
|
|
self.logoutButton.strokeNormalColor = .white
|
|
self.logoutButton.hoverBackgroundColor = NSColor(red: 138, green: 132, blue: 190)
|
|
self.logoutButton.backgroundPressedColor = NSColor(red: 189, green: 191, blue: 223)
|
|
self.logoutButton.title = NSLocalizedString(Constants.Localized.signout, comment: "")
|
|
self.logoutButton.textFont = NSFont(name: PrivadoConstants.Font.bold, size: 10)
|
|
self.logoutButton.titleColor = .white
|
|
self.logoutButton.target = self
|
|
self.logoutButton.action = #selector(self.logoutButtonTapped)
|
|
|
|
self.updateButton.momentary = true
|
|
self.updateButton.cornerRadius = 18
|
|
self.updateButton.backgroundNormalColor = Constants.Color.backgroundNormalColor
|
|
self.updateButton.backgroundHighlightColor = Constants.Color.backgroundHighlightColor
|
|
self.updateButton.titleNormalColor = .white
|
|
self.updateButton.font = NSFont(name: PrivadoConstants.Font.bold, size: Constants.Font.Size.updateButtonTitle)
|
|
self.updateButton.title = NSLocalizedString(Constants.Localized.upgrade, comment: "")
|
|
|
|
self.updateButton.target = self
|
|
self.updateButton.action = #selector(self.handleUpgrade)
|
|
|
|
self.accountDetailesField.stringValue = Constants.Text.accountDetails
|
|
self.accountDetailesField.textColor = .white
|
|
self.accountDetailesField.font = NSFont(name: PrivadoConstants.Font.semiBold, size: Constants.Font.Size.account)
|
|
|
|
if #available(macOS 10.14, *) {
|
|
self.usernameCopyButton.contentTintColor = Constants.Color.valueColor
|
|
}
|
|
self.usernameCopyButton.target = self
|
|
self.usernameCopyButton.action = #selector(self.handleCopyUsername)
|
|
|
|
self.usernameField.stringValue = Constants.Text.username
|
|
self.usernameField.textColor = Constants.Color.valueColor
|
|
self.usernameField.font = NSFont(name: PrivadoConstants.Font.regular, size: Constants.Font.Size.username)
|
|
self.usernameValueField.textColor = .white
|
|
self.usernameValueField.font = NSFont(name: PrivadoConstants.Font.medium, size: Constants.Font.Size.value)
|
|
|
|
self.accountSeparator.viewBackgroundColor = NSColor(red: 130, green: 136, blue: 216)
|
|
self.accountSeparator.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
self.subscription.stringValue = Constants.Text.subscription
|
|
self.subscription.textColor = .white
|
|
self.subscription.font = NSFont(name: PrivadoConstants.Font.semiBold, size: 16)
|
|
|
|
self.currentPlantField.stringValue = "Current plan:"
|
|
self.currentPlantField.textColor = Constants.Color.valueColor
|
|
self.currentPlantField.font = NSFont(name: PrivadoConstants.Font.regular, size: 14)
|
|
|
|
self.planField.textColor = .white
|
|
self.planField.font = NSFont(name: PrivadoConstants.Font.medium, size: 12)
|
|
|
|
self.expirationDateField.stringValue = "Expiration Date:"
|
|
self.expirationDateField.textColor = Constants.Color.valueColor
|
|
self.expirationDateField.font = NSFont(name: PrivadoConstants.Font.regular, size: 14)
|
|
|
|
self.expireField.textColor = .white
|
|
self.expireField.font = NSFont(name: PrivadoConstants.Font.medium, size: 12)
|
|
|
|
self.subscriptionSeparator.viewBackgroundColor = NSColor(red: 130, green: 136, blue: 216)
|
|
self.subscriptionSeparator.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
let termsOfService = self.initializeHyperlinkText(NSLocalizedString(Constants.Localized.termOfService, comment: ""), link: nil, font: NSFont(name: PrivadoConstants.Font.semiBold, size: 12))
|
|
self.termsOfService = termsOfService
|
|
self.termsOfService.hoveredColor = .white
|
|
|
|
let privacyPolicy = self.initializeHyperlinkText(NSLocalizedString(Constants.Localized.privacyPolicy, comment: ""), link: nil, font: NSFont(name: PrivadoConstants.Font.semiBold, size: 12))
|
|
self.privacyPolicy = privacyPolicy
|
|
self.privacyPolicy.hoveredColor = .white
|
|
|
|
[self.updateButton, self.accountDetailesField,
|
|
self.usernameCopyButton, self.usernameField, self.usernameValueField, self.accountSeparator,
|
|
self.expirationDateField, self.currentPlantField,
|
|
self.subscription, self.planField, self.subscriptionSeparator,
|
|
self.expireField, self.privacyPolicy, self.logoutButton]
|
|
.forEach(self.view.addSubview)
|
|
}
|
|
|
|
private func layoutUI() {
|
|
|
|
NSLayoutConstraint.activate([
|
|
self.accountDetailesField.topAnchor.constraint(equalTo: view.topAnchor, constant: Constants.Geometry.accountTopSpace),
|
|
self.accountDetailesField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
|
|
|
|
self.usernameField.topAnchor.constraint(equalTo: self.accountDetailesField.bottomAnchor, constant: Constants.Geometry.usernameTopSpace),
|
|
self.usernameField.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 30),
|
|
|
|
self.usernameValueField.topAnchor.constraint(equalTo: self.usernameField.bottomAnchor, constant: 15),
|
|
self.usernameValueField.leadingAnchor.constraint(equalTo: self.usernameField.leadingAnchor),
|
|
|
|
self.usernameCopyButton.leadingAnchor.constraint(equalTo: self.usernameValueField.trailingAnchor, constant: 13),
|
|
self.usernameCopyButton.centerYAnchor.constraint(equalTo: self.usernameValueField.centerYAnchor),
|
|
self.usernameCopyButton.widthAnchor.constraint(equalToConstant: 40),
|
|
self.usernameCopyButton.heightAnchor.constraint(equalToConstant: 40),
|
|
|
|
self.logoutButton.widthAnchor.constraint(equalToConstant: 80),
|
|
self.logoutButton.heightAnchor.constraint(equalToConstant: 30),
|
|
self.logoutButton.topAnchor.constraint(equalTo: self.accountDetailesField.bottomAnchor, constant: 15),
|
|
self.logoutButton.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20),
|
|
|
|
self.accountSeparator.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20),
|
|
self.accountSeparator.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20),
|
|
self.accountSeparator.topAnchor.constraint(equalTo: self.usernameValueField.bottomAnchor, constant: 15),
|
|
self.accountSeparator.heightAnchor.constraint(equalToConstant: 1),
|
|
|
|
self.subscription.topAnchor.constraint(equalTo: self.accountSeparator.bottomAnchor, constant: 15),
|
|
self.subscription.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20),
|
|
|
|
self.currentPlantField.topAnchor.constraint(equalTo: self.subscription.bottomAnchor, constant: 15),
|
|
self.currentPlantField.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 30),
|
|
|
|
self.planField.topAnchor.constraint(equalTo: self.subscription.bottomAnchor, constant: 15),
|
|
self.planField.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -50),
|
|
|
|
self.expirationDateField.topAnchor.constraint(equalTo: self.currentPlantField.bottomAnchor, constant: 15),
|
|
self.expirationDateField.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 30),
|
|
|
|
self.expireField.topAnchor.constraint(equalTo: self.currentPlantField.bottomAnchor, constant: 15),
|
|
self.expireField.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -50),
|
|
|
|
self.subscriptionSeparator.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20),
|
|
self.subscriptionSeparator.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20),
|
|
self.subscriptionSeparator.topAnchor.constraint(equalTo: self.expireField.bottomAnchor, constant: 15),
|
|
self.subscriptionSeparator.heightAnchor.constraint(equalToConstant: 1),
|
|
|
|
self.updateButton.topAnchor.constraint(equalTo: self.subscriptionSeparator.bottomAnchor, constant: 30),
|
|
self.updateButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
|
|
self.updateButton.widthAnchor.constraint(equalToConstant: Constants.Geometry.updateWidth),
|
|
self.updateButton.heightAnchor.constraint(equalToConstant: Constants.Geometry.updateHeight),
|
|
|
|
self.termsOfService.topAnchor.constraint(equalTo: self.updateButton.bottomAnchor, constant: Constants.Geometry.hyperlinkTop),
|
|
self.termsOfService.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 140),
|
|
self.privacyPolicy.topAnchor.constraint(equalTo: self.updateButton.bottomAnchor, constant: Constants.Geometry.hyperlinkTop),
|
|
self.privacyPolicy.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -140)
|
|
])
|
|
}
|
|
|
|
@objc
|
|
private func handleUpgrade() {
|
|
self.output.viewButtonClicked()
|
|
}
|
|
|
|
@objc
|
|
private func handleCopyUsername() {
|
|
self.output.requireCopyUsername()
|
|
}
|
|
|
|
@objc
|
|
private func logoutButtonTapped() {
|
|
self.output.logout()
|
|
}
|
|
|
|
private static func createButton(image: NSImage?) -> NSButton {
|
|
|
|
let button = NSButton(frame: .zero)
|
|
button.translatesAutoresizingMaskIntoConstraints = false
|
|
button.bezelStyle = .texturedSquare
|
|
button.isBordered = false
|
|
button.wantsLayer = true
|
|
button.viewBackgroundColor = .clear
|
|
button.image = image
|
|
button.imageScaling = .scaleProportionallyDown
|
|
|
|
return button
|
|
}
|
|
|
|
private static func createTextField() -> NSTextField {
|
|
let textField = NSTextField(frame: .zero)
|
|
textField.isBezeled = false
|
|
textField.drawsBackground = false
|
|
textField.isEditable = false
|
|
textField.isSelectable = false
|
|
textField.translatesAutoresizingMaskIntoConstraints = false
|
|
textField.focusRingType = .none
|
|
textField.alignment = .center
|
|
if let font = textField.font {
|
|
textField.font = NSFont(name: font.fontName, size: Constants.Geometry.fontSize)
|
|
}
|
|
textField.stringValue = ""
|
|
return textField
|
|
}
|
|
|
|
private func initializeHyperlinkText(_ text: String, link: URL?, font: NSFont?) -> HyperlinkTextField {
|
|
let hyperlink = HyperlinkTextField(frame: .zero, font: font)
|
|
hyperlink.hyperlinkDelegate = self
|
|
hyperlink.show(hyper: text, underline: true, with: link, using: Constants.Color.hyperlinkColor)
|
|
self.view.addSubview(hyperlink)
|
|
return hyperlink
|
|
}
|
|
|
|
// MARK: - HyperlinkTextFieldDelegate
|
|
|
|
func hyperlinkClicked(target: HyperlinkTextField) {
|
|
if target == self.termsOfService {
|
|
self.output.clickedHyper(link: .termOfService)
|
|
} else {
|
|
self.output.clickedHyper(link: .privacyPolicy)
|
|
}
|
|
}
|
|
|
|
// MARK: - PreferencePane
|
|
|
|
let preferencePaneIdentifier = PreferenceWindowIdentifiers.account
|
|
lazy var preferencePaneTitle = PreferenceWindowIdentifiers.title(for: self.preferencePaneIdentifier)
|
|
|
|
// MARK: - AccountPreferencesViewInput
|
|
|
|
func current(record: AccountPreferenceRecord?, buttonVisible: Bool?, email: String?) {
|
|
|
|
guard let record = record, let updateVisible = buttonVisible, let email = email else {
|
|
self.usernameValueField.stringValue = ""
|
|
self.planField.stringValue = ""
|
|
self.expireField.stringValue = ""
|
|
return
|
|
}
|
|
|
|
|
|
self.updateButton.isHidden = !updateVisible
|
|
|
|
self.usernameValueField.stringValue = email
|
|
self.usernameValueField.isHidden = false
|
|
|
|
self.planField.stringValue = record.plan
|
|
self.planField.isHidden = false
|
|
|
|
self.expireField.stringValue = record.expire
|
|
self.expireField.isHidden = false
|
|
}
|
|
}
|