diff --git a/Clocker/Onboarding/FinalOnboardingViewController.swift b/Clocker/Onboarding/FinalOnboardingViewController.swift index f74304a..f985511 100644 --- a/Clocker/Onboarding/FinalOnboardingViewController.swift +++ b/Clocker/Onboarding/FinalOnboardingViewController.swift @@ -1,12 +1,40 @@ // Copyright © 2015 Abhishek Banthia import Cocoa +import Firebase + +struct EmailSignupConstants { + static let CLEmailSignupEmailProperty = "email" + static let CLOperatingSystemVersion = "OS" + static let CLClockerVersion = "Clocker version" + static let CLAppFeedbackDateProperty = "date" +} class FinalOnboardingViewController: NSViewController { @IBOutlet var titleLabel: NSTextField! @IBOutlet var subtitleLabel: NSTextField! @IBOutlet var accesoryLabel: NSTextField! @IBOutlet var accessoryImageView: NSImageView! + @IBOutlet var emailTextField: NSTextField! + @IBOutlet var emailExplanationLabel: NSTextField! + + private let emailValidator = EmailTextFieldValidator() + + private var serialNumber: String? { + let platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")) + + guard platformExpert > 0 else { + return nil + } + + guard let serialNumber = (IORegistryEntryCreateCFProperty(platformExpert, kIOPlatformSerialNumberKey as CFString, kCFAllocatorDefault, 0).takeUnretainedValue() as? String) else { + return nil + } + + IOObjectRelease(platformExpert) + + return serialNumber + } override func viewDidLoad() { super.viewDidLoad() @@ -14,5 +42,74 @@ class FinalOnboardingViewController: NSViewController { subtitleLabel.stringValue = "Thank you for the details." accesoryLabel.stringValue = "You'll see a clock icon in your Menu Bar when you launch the app. If you'd like to see a dock icon, go to Preferences." accessoryImageView.image = Themer.shared().menubarOnboardingImage() + emailExplanationLabel.stringValue = "If you'd like to help us localize the app in your language or receive infrequent app-related updates, please enter your email!" + } + + override func viewWillAppear() { + super.viewWillAppear() + emailTextField.becomeFirstResponder() + } + + private func todaysDate() -> String { + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .medium + dateFormatter.timeStyle = .short + dateFormatter.timeZone = TimeZone(identifier: AppFeedbackConstants.CLCaliforniaTimezoneIdentifier) + return dateFormatter.string(from: Date()) + } + + private func extraData() -> [String: String] { + guard let validEmail = emailValidator.validate(field: emailTextField) else { + print("Not sending up email because it was invalid") + return [:] + } + + guard let shortVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String, + let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String else { + return [:] + } + let operatingSystem = ProcessInfo.processInfo.operatingSystemVersion + let osVersion = "\(operatingSystem.majorVersion).\(operatingSystem.minorVersion).\(operatingSystem.patchVersion)" + let versionInfo = "Clocker \(shortVersion) (\(appVersion))" + + return [ + EmailSignupConstants.CLEmailSignupEmailProperty: validEmail, + EmailSignupConstants.CLOperatingSystemVersion: osVersion, + EmailSignupConstants.CLClockerVersion: versionInfo, + EmailSignupConstants.CLAppFeedbackDateProperty: todaysDate(), + ] + } + + func sendUpEmailIfValid() { + let annotations = extraData() + guard let identifier = serialNumber else { + assertionFailure("Serial Identifier was unexpectedly nil") + return + } + + let myRootReference = Firebase(url: "https://fiery-heat-5237.firebaseio.com/EmailSignup") + let feedbackReference = myRootReference?.child(byAppendingPath: identifier) + feedbackReference?.setValue(annotations) + } +} + +class EmailTextFieldValidator { + func validate(field: NSTextField) -> String? { + let trimmedText = field.stringValue.trimmingCharacters(in: .whitespacesAndNewlines) + + guard let dataDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else { + return nil + } + + let range = NSMakeRange(0, NSString(string: trimmedText).length) + let allMatches = dataDetector.matches(in: trimmedText, + options: [], + range: range) + + if allMatches.count == 1, + allMatches.first?.url?.absoluteString.contains("mailto:") == true { + return trimmedText + } + return nil } } diff --git a/Clocker/Onboarding/Onboarding.storyboard b/Clocker/Onboarding/Onboarding.storyboard index ecb9e3f..ed9fdcd 100644 --- a/Clocker/Onboarding/Onboarding.storyboard +++ b/Clocker/Onboarding/Onboarding.storyboard @@ -647,25 +647,52 @@ DQ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Clocker/Onboarding/OnboardingParentViewController.swift b/Clocker/Onboarding/OnboardingParentViewController.swift index 7eabc80..8f5edd9 100644 --- a/Clocker/Onboarding/OnboardingParentViewController.swift +++ b/Clocker/Onboarding/OnboardingParentViewController.swift @@ -181,6 +181,8 @@ class OnboardingParentViewController: NSViewController { } private func performFinalStepsBeforeFinishing() { + finalOnboardingVC?.sendUpEmailIfValid() + positiveButton.tag = OnboardingType.complete.rawValue // Install the menubar option!