mirror of
https://github.com/confirmedcode/Lockdown-iOS.git
synced 2025-12-21 12:14:02 +01:00
471 lines
23 KiB
Swift
471 lines
23 KiB
Swift
//
|
|
// AccountVC.swift
|
|
// Lockdown
|
|
//
|
|
// Created by Oleg Dreyman on 02.10.2020.
|
|
// Copyright © 2020 Confirmed Inc. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import PopupDialog
|
|
import PromiseKit
|
|
import CocoaLumberjackSwift
|
|
|
|
final class AccountViewController: BaseViewController, Loadable {
|
|
|
|
let tableView = StaticTableView(frame: .zero, style: .plain)
|
|
var activePlans: [Subscription.PlanType] = []
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
do {
|
|
view.addSubview(tableView)
|
|
tableView.anchors.edges.pin()
|
|
tableView.separatorStyle = .singleLine
|
|
tableView.cellLayoutMarginsFollowReadableWidth = true
|
|
tableView.deselectsCellsAutomatically = true
|
|
tableView.contentInset.top += 12
|
|
tableView.tableFooterView = UIView()
|
|
|
|
tableView.clear()
|
|
createTable()
|
|
}
|
|
|
|
do {
|
|
NotificationCenter.default.addObserver(self, selector: #selector(accountStateDidChange), name: AccountUI.accountStateDidChange, object: nil)
|
|
}
|
|
}
|
|
|
|
@objc
|
|
func accountStateDidChange() {
|
|
assert(Thread.isMainThread)
|
|
self.reloadTable()
|
|
}
|
|
|
|
func reloadTable() {
|
|
tableView.clear()
|
|
createTable()
|
|
tableView.reloadData()
|
|
}
|
|
|
|
func createTable() {
|
|
var title = NSLocalizedString("⚠️ Not Signed In", comment: "")
|
|
var message: String? = NSLocalizedString("Sign up below to unlock benefits of a Lockdown account.", comment: "")
|
|
var firstButton = DefaultCell(title: NSLocalizedString("Sign Up | Sign In", comment: "")) {
|
|
// AccountViewController will update itself by observing
|
|
// AccountUI.accountStateDidChange notification
|
|
AccountUI.presentCreateAccount(on: self)
|
|
}
|
|
firstButton.backgroundView = UIView()
|
|
firstButton.backgroundView?.backgroundColor = UIColor.tunnelsBlue
|
|
firstButton.label.textColor = UIColor.white
|
|
|
|
if let apiCredentials = getAPICredentials() {
|
|
message = apiCredentials.email
|
|
if getAPICredentialsConfirmed() == true {
|
|
title = NSLocalizedString("Signed In", comment: "")
|
|
firstButton = DefaultCell(title: NSLocalizedString("Sign Out", comment: "")) {
|
|
let confirm = PopupDialog(title: NSLocalizedString("Sign Out?", comment: ""),
|
|
message: NSLocalizedString("You'll be signed out from this account.", comment: ""),
|
|
image: nil,
|
|
buttonAlignment: .horizontal,
|
|
transitionStyle: .bounceDown,
|
|
preferredWidth: 270,
|
|
tapGestureDismissal: true,
|
|
panGestureDismissal: false,
|
|
hideStatusBar: false,
|
|
completion: nil)
|
|
confirm.addButtons([
|
|
DefaultButton(title: NSLocalizedString("Cancel", comment: ""), dismissOnTap: true) {
|
|
},
|
|
DefaultButton(title: NSLocalizedString("Sign Out", comment: ""), dismissOnTap: true) { [unowned self] in
|
|
URLCache.shared.removeAllCachedResponses()
|
|
Client.clearCookies()
|
|
clearAPICredentials()
|
|
setAPICredentialsConfirmed(confirmed: false)
|
|
self.reloadTable()
|
|
self.showPopupDialog(title: NSLocalizedString("Success", comment: ""), message: NSLocalizedString("Signed out successfully.", comment: ""), acceptButton: NSLocalizedString("Okay", comment: ""))
|
|
},
|
|
])
|
|
self.present(confirm, animated: true, completion: nil)
|
|
}
|
|
firstButton.backgroundView?.backgroundColor = UIColor.clear
|
|
firstButton.label.textColor = UIColor.systemRed
|
|
}
|
|
else {
|
|
title = "⚠️ Email Not Confirmed"
|
|
firstButton = DefaultCell(title: NSLocalizedString("Confirm Email", comment: "")) {
|
|
self.showLoadingView()
|
|
|
|
firstly {
|
|
try Client.signInWithEmail(email: apiCredentials.email, password: apiCredentials.password)
|
|
}
|
|
.done { (signin: SignIn) in
|
|
self.hideLoadingView()
|
|
// successfully signed in with no errors, show confirmation success
|
|
setAPICredentialsConfirmed(confirmed: true)
|
|
|
|
// logged in and confirmed - update this email with the receipt and refresh VPN credentials
|
|
firstly { () -> Promise<SubscriptionEvent> in
|
|
try Client.subscriptionEvent()
|
|
}
|
|
.then { (result: SubscriptionEvent) -> Promise<GetKey> in
|
|
try Client.getKey()
|
|
}
|
|
.done { (getKey: GetKey) in
|
|
try setVPNCredentials(id: getKey.id, keyBase64: getKey.b64)
|
|
if (getUserWantsVPNEnabled() == true) {
|
|
VPNController.shared.restart()
|
|
}
|
|
}
|
|
.catch { error in
|
|
// it's okay for this to error out with "no subscription in receipt"
|
|
DDLogError("HomeViewController ConfirmEmail subscriptionevent error (ok for it to be \"no subscription in receipt\"): \(error)")
|
|
}
|
|
|
|
let popup = PopupDialog(title: "Success! 🎉",
|
|
message: NSLocalizedString("Your account has been confirmed and you're now signed in. You'll get the latest block lists, access to Lockdown Mac, and get critical announcements.", comment: ""),
|
|
image: nil,
|
|
buttonAlignment: .horizontal,
|
|
transitionStyle: .bounceDown,
|
|
preferredWidth: 270,
|
|
tapGestureDismissal: true,
|
|
panGestureDismissal: false,
|
|
hideStatusBar: false,
|
|
completion: nil)
|
|
popup.addButtons([
|
|
DefaultButton(title: NSLocalizedString("Okay", comment: ""), dismissOnTap: true) {
|
|
self.reloadTable()
|
|
}
|
|
])
|
|
self.present(popup, animated: true, completion: nil)
|
|
}
|
|
.catch { error in
|
|
self.hideLoadingView()
|
|
let popup = PopupDialog(title: NSLocalizedString("Check Your Inbox", comment: ""),
|
|
message: "\(NSLocalizedString("To complete your signup, click the confirmation link we sent to", comment: "Used in To complete your signup, click the confirmation link we sent to you@gmail.com")) \(apiCredentials.email). \(NSLocalizedString("Be sure to check your spam folder in case it got stuck there.\n\nYou can also request a re-send of the confirmation.", comment: ""))",
|
|
image: nil,
|
|
buttonAlignment: .vertical,
|
|
transitionStyle: .bounceDown,
|
|
preferredWidth: 270,
|
|
tapGestureDismissal: true,
|
|
panGestureDismissal: false,
|
|
hideStatusBar: false,
|
|
completion: nil)
|
|
popup.addButtons([
|
|
DefaultButton(title: NSLocalizedString("Okay", comment: ""), dismissOnTap: true) {},
|
|
DefaultButton(title: NSLocalizedString("Sign Out", comment: ""), dismissOnTap: true) {
|
|
URLCache.shared.removeAllCachedResponses()
|
|
Client.clearCookies()
|
|
clearAPICredentials()
|
|
setAPICredentialsConfirmed(confirmed: false)
|
|
self.reloadTable()
|
|
self.showPopupDialog(title: NSLocalizedString("Success", comment: ""), message: NSLocalizedString("Signed out successfully.", comment: ""), acceptButton: NSLocalizedString("Okay", comment: ""))
|
|
},
|
|
DefaultButton(title: NSLocalizedString("Re-send", comment: ""), dismissOnTap: true) {
|
|
firstly {
|
|
try Client.resendConfirmCode(email: apiCredentials.email)
|
|
}
|
|
.done { (success: Bool) in
|
|
var message = NSLocalizedString("Successfully re-sent your email confirmation to ", comment: "") + apiCredentials.email
|
|
if (success == false) {
|
|
message = NSLocalizedString("Failed to re-send email confirmation.", comment: "")
|
|
}
|
|
self.showPopupDialog(title: "", message: message, acceptButton: NSLocalizedString("Okay", comment: ""))
|
|
}
|
|
.catch { error in
|
|
if (self.popupErrorAsNSURLError(error)) {
|
|
return
|
|
}
|
|
else if let apiError = error as? ApiError {
|
|
_ = self.popupErrorAsApiError(apiError)
|
|
}
|
|
else {
|
|
self.showPopupDialog(title: NSLocalizedString("Error Re-sending Email Confirmation", comment: ""),
|
|
message: "\(error)",
|
|
acceptButton: NSLocalizedString("Okay", comment: ""))
|
|
}
|
|
}
|
|
},
|
|
])
|
|
self.present(popup, animated: true, completion: nil)
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
let upgradeButton = DefaultButtonCell(title: NSLocalizedString("Loading Plan", comment: "Shows when loading the details of a subscription plan")) {
|
|
self.performSegue(withIdentifier: "showUpgradePlanAccount", sender: self)
|
|
}
|
|
upgradeButton.startActivityIndicator()
|
|
upgradeButton.button.isEnabled = false
|
|
upgradeButton.selectionStyle = .none
|
|
|
|
self.activePlans = []
|
|
|
|
firstly {
|
|
try Client.signIn()
|
|
}.then { _ in
|
|
try Client.activeSubscriptions()
|
|
}.ensure {
|
|
upgradeButton.stopActivityIndicator()
|
|
}.done { subscriptions in
|
|
self.activePlans = subscriptions.map({ $0.planType })
|
|
if let active = subscriptions.first {
|
|
if active.planType == .proAnnual {
|
|
upgradeButton.button.isEnabled = false
|
|
upgradeButton.selectionStyle = .none
|
|
upgradeButton.button.setTitle(NSLocalizedString("Plan: Annual Pro", comment: ""), for: UIControl.State())
|
|
} else {
|
|
upgradeButton.button.isEnabled = true
|
|
upgradeButton.selectionStyle = .default
|
|
upgradeButton.backgroundView?.backgroundColor = UIColor.tunnelsDarkBlue
|
|
upgradeButton.button.setTitleColor(UIColor.white, for: UIControl.State())
|
|
upgradeButton.button.setTitle(NSLocalizedString("View or Upgrade Plan", comment: ""), for: UIControl.State())
|
|
}
|
|
} else {
|
|
upgradeButton.button.isEnabled = true
|
|
upgradeButton.selectionStyle = .default
|
|
upgradeButton.backgroundView?.backgroundColor = UIColor.tunnelsDarkBlue
|
|
upgradeButton.button.setTitleColor(UIColor.white, for: UIControl.State())
|
|
upgradeButton.button.setTitle(NSLocalizedString("View Upgrade Options", comment: ""), for: UIControl.State())
|
|
}
|
|
}.catch { error in
|
|
DDLogWarn(error.localizedDescription)
|
|
if let apiError = error as? ApiError {
|
|
switch apiError.code {
|
|
case kApiCodeNoSubscriptionInReceipt, kApiCodeNoActiveSubscription:
|
|
upgradeButton.button.isEnabled = true
|
|
upgradeButton.selectionStyle = .default
|
|
upgradeButton.backgroundView?.backgroundColor = UIColor.tunnelsDarkBlue
|
|
upgradeButton.button.setTitleColor(UIColor.white, for: UIControl.State())
|
|
upgradeButton.button.setTitle(NSLocalizedString("View Upgrade Options", comment:""), for: UIControl.State())
|
|
default:
|
|
upgradeButton.button.isEnabled = true
|
|
upgradeButton.selectionStyle = .default
|
|
upgradeButton.button.setTitleColor(UIColor.systemRed, for: UIControl.State())
|
|
upgradeButton.button.setTitle(NSLocalizedString("Error Loading Plan: Retry", comment: ""), for: UIControl.State())
|
|
upgradeButton.onSelect {
|
|
self.reloadTable()
|
|
}
|
|
}
|
|
} else {
|
|
upgradeButton.button.isEnabled = true
|
|
upgradeButton.selectionStyle = .default
|
|
upgradeButton.button.setTitleColor(UIColor.systemRed, for: UIControl.State())
|
|
upgradeButton.button.setTitle(NSLocalizedString("Error Loading Plan: Retry", comment: ""), for: UIControl.State())
|
|
upgradeButton.onSelect {
|
|
self.reloadTable()
|
|
}
|
|
}
|
|
}
|
|
|
|
let notificationsButton = DefaultCell(title: "", action: { })
|
|
|
|
let updateNotificationButtonTitle = { (cell: _DefaultCell) in
|
|
if PushNotifications.Authorization.getUserWantsNotificationsEnabled(forCategory: .weeklyUpdate) {
|
|
cell.label.text = NSLocalizedString("Notifications: On", comment: "")
|
|
} else {
|
|
cell.label.text = NSLocalizedString("Notifications: Off", comment: "")
|
|
}
|
|
}
|
|
|
|
updateNotificationButtonTitle(notificationsButton)
|
|
|
|
notificationsButton.onSelect { [unowned notificationsButton, unowned self] in
|
|
if PushNotifications.Authorization.getUserWantsNotificationsEnabled(forCategory: .weeklyUpdate) {
|
|
PushNotifications.Authorization.setUserWantsNotificationsEnabled(false, forCategory: .weeklyUpdate)
|
|
updateNotificationButtonTitle(notificationsButton)
|
|
} else {
|
|
PushNotifications.Authorization.requestWeeklyUpdateAuthorization(presentingDialogOn: self).done { status in
|
|
DDLogInfo("New authorization status for push notifications: \(status)")
|
|
updateNotificationButtonTitle(notificationsButton)
|
|
}.catch { error in
|
|
DDLogError("Error updating notification authorization status: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
}
|
|
|
|
tableView.addRow { (contentView) in
|
|
let stack = UIStackView()
|
|
stack.axis = .vertical
|
|
stack.spacing = 8
|
|
contentView.addSubview(stack)
|
|
stack.anchors.edges.marginsPin(insets: .init(top: 8, left: 0, bottom: 8, right: 0))
|
|
|
|
let titleLabel = UILabel()
|
|
titleLabel.text = title
|
|
titleLabel.font = fontBold18
|
|
titleLabel.textAlignment = .center
|
|
titleLabel.numberOfLines = 0
|
|
stack.addArrangedSubview(titleLabel)
|
|
|
|
let messageLabel = UILabel()
|
|
messageLabel.text = message
|
|
messageLabel.font = fontMedium18
|
|
messageLabel.textAlignment = .center
|
|
messageLabel.numberOfLines = 0
|
|
stack.addArrangedSubview(messageLabel)
|
|
}
|
|
|
|
let firstButtons: [SelectableTableViewCell] = [
|
|
firstButton,
|
|
upgradeButton,
|
|
notificationsButton,
|
|
]
|
|
|
|
for cell in firstButtons {
|
|
tableView.addCell(cell)
|
|
}
|
|
|
|
let otherCells = [
|
|
DefaultCell(title: NSLocalizedString("Tutorial", comment: "")) { [unowned self] in
|
|
self.startTutorial()
|
|
},
|
|
DefaultCell(title: NSLocalizedString("Why Trust Lockdown", comment: "")) {
|
|
self.showWhyTrustPopup()
|
|
},
|
|
DefaultCell(title: NSLocalizedString("Privacy Policy", comment: "")) {
|
|
self.showPrivacyPolicyModal()
|
|
},
|
|
DefaultCell(title: NSLocalizedString("What is VPN?", comment: "")) {
|
|
self.performSegue(withIdentifier: "showWhatIsVPN", sender: self)
|
|
},
|
|
DefaultCell(title: NSLocalizedString("Support | Feedback", comment: "")) {
|
|
self.showPopupDialog(
|
|
title: nil,
|
|
message: NSLocalizedString("Remember to check our FAQs first, for answers to the most frequently asked questions.\n\nIf it's not answered there, we're happy to provide support and take feedback by email.", comment: ""),
|
|
buttons: [
|
|
.custom(title: NSLocalizedString("View FAQs", comment: ""), completion: {
|
|
self.showFAQsModal()
|
|
}),
|
|
.custom(title: NSLocalizedString("Email Us", comment: ""), completion: {
|
|
self.emailTeam()
|
|
}),
|
|
.cancel()
|
|
]
|
|
)
|
|
},
|
|
DefaultCell(title: NSLocalizedString("FAQs", comment: "")) {
|
|
self.showFAQsModal()
|
|
},
|
|
DefaultCell(title: NSLocalizedString("Website", comment: "")) {
|
|
self.showWebsiteModal()
|
|
},
|
|
]
|
|
|
|
for cell in otherCells {
|
|
tableView.addCell(cell)
|
|
}
|
|
|
|
#if DEBUG
|
|
let fixVPNConfig = DefaultCell(title: "_Fix Firewall Config", action: {
|
|
self.showFixFirewallConnectionDialog {
|
|
FirewallController.shared.deleteConfigurationAndAddAgain()
|
|
}
|
|
})
|
|
tableView.addCell(fixVPNConfig)
|
|
#endif
|
|
|
|
tableView.addRowCell { (cell) in
|
|
cell.textLabel?.text = Bundle.main.versionString
|
|
cell.textLabel?.font = fontSemiBold17
|
|
cell.textLabel?.textColor = UIColor.systemGray
|
|
cell.textLabel?.textAlignment = .right
|
|
|
|
// removing the bottom separator
|
|
cell.separatorInset = .init(top: 0, left: 0, bottom: 0, right: .greatestFiniteMagnitude)
|
|
cell.directionalLayoutMargins = .zero
|
|
}
|
|
}
|
|
|
|
func startTutorial() {
|
|
if let tabBarController = tabBarController as? MainTabBarController {
|
|
tabBarController.selectedViewController = tabBarController.homeViewController
|
|
tabBarController.homeViewController.startTutorial()
|
|
}
|
|
}
|
|
|
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
|
switch segue.identifier {
|
|
case "showWhatIsVPN":
|
|
if let vc = segue.destination as? WhatIsVpnViewController {
|
|
vc.parentVC = (tabBarController as? MainTabBarController)?.homeViewController
|
|
}
|
|
case "showUpgradePlanAccount":
|
|
if let vc = segue.destination as? SignupViewController {
|
|
vc.parentVC = self
|
|
if activePlans.isEmpty {
|
|
vc.mode = .newSubscription
|
|
vc.enableVPNAfterSubscribe = true
|
|
} else {
|
|
vc.mode = .upgrade(active: activePlans)
|
|
vc.enableVPNAfterSubscribe = true
|
|
}
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Helpers / Extensions
|
|
|
|
class _DefaultButtonCell: SelectableTableViewCell {
|
|
let button = UIButton(type: .system)
|
|
}
|
|
|
|
func DefaultButtonCell(title: String, action: @escaping () -> ()) -> _DefaultButtonCell {
|
|
let cell = _DefaultButtonCell()
|
|
cell.backgroundView = UIView()
|
|
cell.button.setTitle(title, for: .normal)
|
|
cell.button.isUserInteractionEnabled = false
|
|
cell.button.titleLabel?.font = fontSemiBold17
|
|
cell.button.tintColor = .tunnelsBlue
|
|
cell.contentView.addSubview(cell.button)
|
|
cell.button.anchors.height.equal(21)
|
|
cell.button.anchors.edges.marginsPin(insets: .init(top: 8, left: 0, bottom: 8, right: 0))
|
|
return cell.onSelect(callback: action)
|
|
}
|
|
|
|
class _DefaultCell: SelectableTableViewCell {
|
|
let label = UILabel()
|
|
}
|
|
|
|
func DefaultCell(title: String, action: @escaping () -> ()) -> _DefaultCell {
|
|
let cell = _DefaultCell()
|
|
cell.label.text = title
|
|
cell.label.font = fontSemiBold17
|
|
cell.label.textColor = .tunnelsBlue
|
|
cell.label.textAlignment = .center
|
|
cell.contentView.addSubview(cell.label)
|
|
cell.label.anchors.edges.marginsPin(insets: .init(top: 8, left: 0, bottom: 8, right: 0))
|
|
return cell.onSelect(callback: action)
|
|
}
|
|
|
|
fileprivate extension _DefaultButtonCell {
|
|
func startActivityIndicator() {
|
|
let activity = UIActivityIndicatorView()
|
|
|
|
if let label = button.titleLabel {
|
|
label.addSubview(activity)
|
|
activity.translatesAutoresizingMaskIntoConstraints = false
|
|
activity.centerYAnchor.constraint(equalTo: label.centerYAnchor).isActive = true
|
|
activity.leadingAnchor.constraint(equalToSystemSpacingAfter: label.trailingAnchor, multiplier: 1).isActive = true
|
|
activity.startAnimating()
|
|
}
|
|
}
|
|
|
|
func stopActivityIndicator() {
|
|
if let label = button.titleLabel {
|
|
let indicators = label.subviews.compactMap { $0 as? UIActivityIndicatorView }
|
|
for indicator in indicators {
|
|
indicator.stopAnimating()
|
|
indicator.removeFromSuperview()
|
|
}
|
|
}
|
|
}
|
|
}
|