You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
12 KiB
328 lines
12 KiB
// Copyright © 2015 Abhishek Banthia |
|
|
|
import Cocoa |
|
import ServiceManagement |
|
|
|
extension NSStoryboard.SceneIdentifier { |
|
static let welcomeIdentifier = NSStoryboard.SceneIdentifier("welcomeVC") |
|
static let onboardingPermissionsIdentifier = NSStoryboard.SceneIdentifier("onboardingPermissionsVC") |
|
static let startAtLoginIdentifier = NSStoryboard.SceneIdentifier("startAtLoginVC") |
|
static let onboardingSearchIdentifier = NSStoryboard.SceneIdentifier("onboardingSearchVC") |
|
static let finalOnboardingIdentifier = NSStoryboard.SceneIdentifier("finalOnboardingVC") |
|
} |
|
|
|
private enum OnboardingType: Int { |
|
case welcome |
|
case permissions |
|
case launchAtLogin |
|
case search |
|
case final |
|
case complete // Added for logging purposes |
|
} |
|
|
|
class OnboardingParentViewController: NSViewController { |
|
@IBOutlet private var containerView: NSView! |
|
@IBOutlet private var negativeButton: NSButton! |
|
@IBOutlet private var backButton: NSButton! |
|
@IBOutlet private var positiveButton: NSButton! |
|
|
|
private lazy var welcomeVC = (storyboard?.instantiateController(withIdentifier: .welcomeIdentifier) as? WelcomeViewController) |
|
|
|
private lazy var permissionsVC = (storyboard?.instantiateController(withIdentifier: .onboardingPermissionsIdentifier) as? OnboardingPermissionsViewController) |
|
|
|
private lazy var startAtLoginVC = (storyboard?.instantiateController(withIdentifier: .startAtLoginIdentifier) as? StartAtLoginViewController) |
|
|
|
private lazy var onboardingSearchVC = (storyboard?.instantiateController(withIdentifier: .onboardingSearchIdentifier) as? OnboardingSearchController) |
|
|
|
private lazy var finalOnboardingVC = (storyboard?.instantiateController(withIdentifier: .finalOnboardingIdentifier) as? FinalOnboardingViewController) |
|
|
|
override func viewDidLoad() { |
|
super.viewDidLoad() |
|
setup() |
|
} |
|
|
|
private func setup() { |
|
setupWelcomeScreen() |
|
setupUI() |
|
} |
|
|
|
private func setupWelcomeScreen() { |
|
guard let firstVC = welcomeVC else { |
|
assertionFailure() |
|
return |
|
} |
|
|
|
addChildIfNeccessary(firstVC) |
|
containerView.addSubview(firstVC.view) |
|
firstVC.view.frame = containerView.bounds |
|
} |
|
|
|
private func setupUI() { |
|
|
|
setIdentifiersForTests() |
|
|
|
positiveButton.title = "Get Started" |
|
positiveButton.tag = OnboardingType.welcome.rawValue |
|
backButton.tag = OnboardingType.welcome.rawValue |
|
|
|
[negativeButton, backButton].forEach { $0?.isHidden = true } |
|
} |
|
|
|
private func setIdentifiersForTests() { |
|
positiveButton.setAccessibilityIdentifier("Forward") |
|
negativeButton.setAccessibilityIdentifier("Alternate") |
|
backButton.setAccessibilityIdentifier("Backward") |
|
} |
|
|
|
@IBAction func negativeAction(_: Any) { |
|
guard let fromViewController = startAtLoginVC, let toViewController = onboardingSearchVC else { |
|
assertionFailure() |
|
return |
|
} |
|
|
|
addChildIfNeccessary(toViewController) |
|
|
|
shouldStartAtLogin(false) |
|
|
|
transition(from: fromViewController, |
|
to: toViewController, |
|
options: .slideLeft) { |
|
self.positiveButton.tag = OnboardingType.search.rawValue |
|
self.backButton.tag = OnboardingType.launchAtLogin.rawValue |
|
self.positiveButton.title = "Continue" |
|
self.negativeButton.isHidden = true |
|
} |
|
} |
|
|
|
@IBAction func continueOnboarding(_: NSButton) { |
|
if positiveButton.tag == OnboardingType.welcome.rawValue { |
|
navigateToPermissions() |
|
} else if positiveButton.tag == OnboardingType.permissions.rawValue { |
|
navigateToStartAtLogin() |
|
} else if positiveButton.tag == OnboardingType.launchAtLogin.rawValue { |
|
navigateToSearch() |
|
} else if positiveButton.tag == OnboardingType.search.rawValue { |
|
navigateToFinalStage() |
|
} else { |
|
performFinalStepsBeforeFinishing() |
|
} |
|
} |
|
|
|
private func navigateToPermissions() { |
|
guard let fromViewController = welcomeVC, let toViewController = permissionsVC else { |
|
assertionFailure() |
|
return |
|
} |
|
|
|
addChildIfNeccessary(toViewController) |
|
|
|
transition(from: fromViewController, |
|
to: toViewController, |
|
options: .slideLeft) { |
|
self.positiveButton.tag = OnboardingType.permissions.rawValue |
|
self.positiveButton.title = "Continue" |
|
self.backButton.isHidden = false |
|
} |
|
} |
|
|
|
private func navigateToStartAtLogin() { |
|
guard let fromViewController = permissionsVC, let toViewController = startAtLoginVC else { |
|
assertionFailure() |
|
return |
|
} |
|
|
|
addChildIfNeccessary(toViewController) |
|
|
|
transition(from: fromViewController, |
|
to: toViewController, |
|
options: .slideLeft) { |
|
self.backButton.tag = OnboardingType.permissions.rawValue |
|
self.positiveButton.tag = OnboardingType.launchAtLogin.rawValue |
|
self.positiveButton.title = "Open Clocker At Login" |
|
self.negativeButton.isHidden = false |
|
} |
|
} |
|
|
|
private func navigateToSearch() { |
|
guard let fromViewController = startAtLoginVC, let toViewController = onboardingSearchVC else { |
|
assertionFailure() |
|
return |
|
} |
|
|
|
addChildIfNeccessary(toViewController) |
|
|
|
shouldStartAtLogin(true) |
|
|
|
transition(from: fromViewController, |
|
to: toViewController, |
|
options: .slideLeft) { |
|
self.backButton.tag = OnboardingType.launchAtLogin.rawValue |
|
self.positiveButton.tag = OnboardingType.search.rawValue |
|
self.positiveButton.title = "Continue" |
|
self.negativeButton.isHidden = true |
|
} |
|
} |
|
|
|
private func navigateToFinalStage() { |
|
guard let fromViewController = onboardingSearchVC, let toViewController = finalOnboardingVC else { |
|
assertionFailure() |
|
return |
|
} |
|
|
|
addChildIfNeccessary(toViewController) |
|
|
|
transition(from: fromViewController, |
|
to: toViewController, |
|
options: .slideLeft) { |
|
self.backButton.tag = OnboardingType.search.rawValue |
|
self.positiveButton.tag = OnboardingType.final.rawValue |
|
self.positiveButton.title = "Launch Clocker" |
|
} |
|
|
|
} |
|
|
|
private func performFinalStepsBeforeFinishing() { |
|
self.positiveButton.tag = OnboardingType.complete.rawValue |
|
|
|
// Install the menubar option! |
|
let appDelegate = NSApplication.shared.delegate as? AppDelegate |
|
appDelegate?.continueUsually() |
|
|
|
view.window?.close() |
|
|
|
if ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument) == false { |
|
UserDefaults.standard.set(true, forKey: CLShowOnboardingFlow) |
|
} |
|
} |
|
|
|
private func addChildIfNeccessary(_ viewController: NSViewController) { |
|
if children.contains(viewController) == false { |
|
addChild(viewController) |
|
} |
|
} |
|
|
|
@IBAction func back(_: Any) { |
|
if backButton.tag == OnboardingType.welcome.rawValue { |
|
goBackToWelcomeScreen() |
|
} else if backButton.tag == OnboardingType.permissions.rawValue { |
|
goBackToPermissions() |
|
} else if backButton.tag == OnboardingType.launchAtLogin.rawValue { |
|
goBackToStartAtLogin() |
|
} else if backButton.tag == OnboardingType.search.rawValue { |
|
goBackToSearch() |
|
} |
|
} |
|
|
|
private func goBackToSearch() { |
|
guard let fromViewController = finalOnboardingVC, let toViewController = onboardingSearchVC else { |
|
assertionFailure() |
|
return |
|
} |
|
|
|
transition(from: fromViewController, |
|
to: toViewController, |
|
options: .slideRight) { |
|
self.positiveButton.tag = OnboardingType.search.rawValue |
|
self.backButton.tag = OnboardingType.launchAtLogin.rawValue |
|
self.positiveButton.title = "Continue" |
|
self.negativeButton.isHidden = true |
|
} |
|
} |
|
|
|
private func goBackToStartAtLogin() { |
|
guard let fromViewController = onboardingSearchVC, let toViewController = startAtLoginVC else { |
|
assertionFailure() |
|
return |
|
} |
|
|
|
transition(from: fromViewController, |
|
to: toViewController, |
|
options: .slideRight) { |
|
self.positiveButton.tag = OnboardingType.launchAtLogin.rawValue |
|
self.backButton.tag = OnboardingType.permissions.rawValue |
|
self.positiveButton.title = "Open Clocker At Login" |
|
self.negativeButton.isHidden = false |
|
} |
|
} |
|
|
|
private func goBackToPermissions() { |
|
// We're on StartAtLogin VC and we have to go back to Permissions |
|
|
|
guard let fromViewController = startAtLoginVC, let toViewController = permissionsVC else { |
|
assertionFailure() |
|
return |
|
} |
|
|
|
transition(from: fromViewController, |
|
to: toViewController, |
|
options: .slideRight) { |
|
self.positiveButton.tag = OnboardingType.permissions.rawValue |
|
self.backButton.tag = OnboardingType.welcome.rawValue |
|
self.negativeButton.isHidden = true |
|
self.positiveButton.title = "Continue" |
|
} |
|
} |
|
|
|
private func goBackToWelcomeScreen() { |
|
guard let fromViewController = permissionsVC, let toViewController = welcomeVC else { |
|
assertionFailure() |
|
return |
|
} |
|
|
|
transition(from: fromViewController, |
|
to: toViewController, |
|
options: .slideRight) { |
|
self.positiveButton.tag = OnboardingType.welcome.rawValue |
|
self.backButton.isHidden = true |
|
self.positiveButton.title = "Get Started" |
|
} |
|
} |
|
|
|
private func shouldStartAtLogin(_ shouldStart: Bool) { |
|
|
|
// If tests are going on, we don't want to enable/disable launch at login! |
|
if ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument) { |
|
return |
|
} |
|
|
|
UserDefaults.standard.set(shouldStart ? 1 : 0, forKey: CLStartAtLogin) |
|
|
|
if !SMLoginItemSetEnabled("com.abhishek.ClockerHelper" as CFString, shouldStart) { |
|
Logger.log(object: ["Successful": "NO"], for: "Start Clocker Login") |
|
} else { |
|
Logger.log(object: ["Successful": "YES"], for: "Start Clocker Login") |
|
} |
|
|
|
if shouldStart { |
|
Logger.log(object: [:], for: "Enable Launch at Login while Onboarding") |
|
} else { |
|
Logger.log(object: [:], for: "Disable Launch at Login while Onboarding") |
|
} |
|
} |
|
|
|
func logExitPoint() { |
|
let currentViewController = currentController() |
|
print(currentViewController) |
|
Logger.log(object: currentViewController, for: "Onboarding Process Exit") |
|
} |
|
|
|
private func currentController() -> [String: String] { |
|
|
|
switch positiveButton.tag { |
|
case 0: |
|
return ["Onboarding Process Interrupted": "Welcome View"] |
|
case 1: |
|
return ["Onboarding Process Interrupted": "Onboarding Permissions View"] |
|
case 2: |
|
return ["Onboarding Process Interrupted": "Start At Login View"] |
|
case 3: |
|
return ["Onboarding Process Interrupted": "Onboarding Search View"] |
|
case 4: |
|
return ["Onboarding Process Interrupted": "Finish Onboarding View"] |
|
case 5: |
|
return ["Onboarding Process Completed": "Successfully"] |
|
default: |
|
return ["Onboarding Process Interrupted": "Error"] |
|
} |
|
} |
|
}
|
|
|