Browse Source

Few changes.

pull/92/head
Abhishek Banthia 5 years ago
parent
commit
66ae394ecb
  1. 13
      .swiftlint.yml
  2. 17
      Clocker/AppDelegate.swift
  3. 29
      Clocker/Events and Reminders/CalendarHandler.swift
  4. 282
      Clocker/Onboarding/OnboardingParentViewController.swift
  5. 61
      Clocker/Onboarding/OnboardingSearchController.swift
  6. 12
      Clocker/Overall App/NetworkManager.swift
  7. 4
      Clocker/Overall App/Themer.swift
  8. 2
      Clocker/Panel/Data Layer/TimezoneDataOperations.swift
  9. 33
      Clocker/Panel/Notes Popover/NotesPopover.swift
  10. 4
      Clocker/Panel/ParentPanelController.swift
  11. 5
      Clocker/Panel/UI/BackgroundPanelView.swift
  12. 6
      Clocker/Preferences/Appearance/AppearanceViewController.swift
  13. 10
      Clocker/Preferences/Calendar/CalendarViewController.swift
  14. 239
      Clocker/Preferences/General/PreferencesViewController.swift
  15. 8
      Clocker/Preferences/OneWindowController.swift

13
.swiftlint.yml

@ -1,18 +1,15 @@
disabled_rules: # rule identifiers to exclude from running
- colon
- comma
- control_statement
- line_length
- type_body_length
- file_length
- nesting
- function_body_length
# - file_length
# - nesting
opt_in_rules: # some rules are only opt-in
- empty_count
# included: # paths to include during linting. `--path` is ignored if present.
# - Clocker
excluded: # paths to ignore during linting. Takes precedence over `included`.
- Clocker/Dependencies
- Clocker/ClockerUnitTests
- Clocker/ClockerUITests
# - Pods
# - Source/ExcludedFolder
# - Source/ExcludedFile.swift
@ -51,4 +48,4 @@ identifier_name:
# - id
# - URL
# - GlobalAPIKey
reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit, html, emoji, sonarqube, markdown)
reporter: "xcode"

17
Clocker/AppDelegate.swift

@ -2,7 +2,7 @@
import Cocoa
open class AppDelegate : NSObject, NSApplicationDelegate {
open class AppDelegate: NSObject, NSApplicationDelegate {
lazy private var floatingWindow: FloatingWindowController = FloatingWindowController.shared()
lazy private var panelController: PanelController = PanelController.shared()
@ -13,7 +13,7 @@ open class AppDelegate : NSObject, NSApplicationDelegate {
panelObserver?.invalidate()
}
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if let path = keyPath, path == "values.globalPing" {
@ -98,7 +98,8 @@ open class AppDelegate : NSObject, NSApplicationDelegate {
}()
private func showOnboardingFlow() {
let shouldLaunchOnboarding = (DataStore.shared().retrieve(key: CLShowOnboardingFlow) == nil && DataStore.shared().timezones().isEmpty) || (ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument))
let shouldLaunchOnboarding = (DataStore.shared().retrieve(key: CLShowOnboardingFlow) == nil && DataStore.shared().timezones().isEmpty)
|| (ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument))
shouldLaunchOnboarding ? controller?.launch() : continueUsually()
}
@ -238,11 +239,17 @@ open class AppDelegate : NSObject, NSApplicationDelegate {
}
}
let informativeText = """
Clocker must be run from the Applications folder in order to work properly.
Please quit Clocker, move it to the Applications folder, and relaunch.
Current folder: \(applicationDirectory)"
"""
// Clocker is installed out of Applications directory
// This breaks start at login! Time to show an alert and terminate
showAlert(message: "Move Clocker to the Applications folder",
informativeText: "Clocker must be run from the Applications folder in order to work properly.\n\nPlease quit Clocker, move it to the Applications folder, and relaunch. Current folder: \(applicationDirectory)",
buttonTitle: "Quit")
informativeText: informativeText,
buttonTitle: "Quit")
// Terminate
NSApp.terminate(nil)

29
Clocker/Events and Reminders/CalendarHandler.swift

@ -228,23 +228,11 @@ extension EventCenter {
}
nextDate = autoupdatingCalendar.startOfDay(for: nextDate)
// Make a customized struct
let isStartDate = autoupdatingCalendar.isDate(date, inSameDayAs: event.startDate) && (event.endDate.compare(date) == .orderedDescending)
let isEndDate = autoupdatingCalendar.isDate(date, inSameDayAs: event.endDate) && (event.startDate.compare(date) == .orderedAscending)
let isAllDay = event.isAllDay || (event.startDate.compare(date) == .orderedAscending && event.endDate.compare(nextDate) == .orderedSame)
let isSingleDay = event.isAllDay && (event.startDate.compare(date) == .orderedSame && event.endDate.compare(nextDate) == .orderedSame)
let eventInfo = EventInfo(event: event,
isStartDate: isStartDate,
isEndDate: isEndDate,
isAllDay: isAllDay,
isSingleDay: isSingleDay)
if eventsForDateMapper[date] == nil {
eventsForDateMapper[date] = []
}
eventsForDateMapper[date]?.append(eventInfo)
eventsForDateMapper[date]?.append(generateEventInfo(for: event, date, nextDate))
date = nextDate
}
@ -265,6 +253,21 @@ extension EventCenter {
filterEvents()
}
private func generateEventInfo(for event: EKEvent, _ date: Date, _ nextDate: Date) -> EventInfo {
// Make a customized struct
let isStartDate = autoupdatingCalendar.isDate(date, inSameDayAs: event.startDate) && (event.endDate.compare(date) == .orderedDescending)
let isEndDate = autoupdatingCalendar.isDate(date, inSameDayAs: event.endDate) && (event.startDate.compare(date) == .orderedAscending)
let isAllDay = event.isAllDay || (event.startDate.compare(date) == .orderedAscending && event.endDate.compare(nextDate) == .orderedSame)
let isSingleDay = event.isAllDay && (event.startDate.compare(date) == .orderedSame && event.endDate.compare(nextDate) == .orderedSame)
let eventInfo = EventInfo(event: event,
isStartDate: isStartDate,
isEndDate: isEndDate,
isAllDay: isAllDay,
isSingleDay: isSingleDay)
return eventInfo
}
}
struct CalendarInfo {

282
Clocker/Onboarding/OnboardingParentViewController.swift

@ -26,15 +26,15 @@ class OnboardingParentViewController: NSViewController {
@IBOutlet private var backButton: NSButton!
@IBOutlet private var positiveButton: NSButton!
private lazy var welcomeVC: WelcomeViewController? = (storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier.welcomeIdentifier) as? WelcomeViewController)
private lazy var welcomeVC = (storyboard?.instantiateController(withIdentifier: .welcomeIdentifier) as? WelcomeViewController)
private lazy var permissionsVC: OnboardingPermissionsViewController? = (storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier.onboardingPermissionsIdentifier) as? OnboardingPermissionsViewController)
private lazy var permissionsVC = (storyboard?.instantiateController(withIdentifier: .onboardingPermissionsIdentifier) as? OnboardingPermissionsViewController)
private lazy var startAtLoginVC: StartAtLoginViewController? = (storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier.startAtLoginIdentifier) as? StartAtLoginViewController)
private lazy var startAtLoginVC = (storyboard?.instantiateController(withIdentifier: .startAtLoginIdentifier) as? StartAtLoginViewController)
private lazy var onboardingSearchVC: OnboardingSearchController? = (self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier.onboardingSearchIdentifier) as? OnboardingSearchController)
private lazy var onboardingSearchVC = (storyboard?.instantiateController(withIdentifier: .onboardingSearchIdentifier) as? OnboardingSearchController)
private lazy var finalOnboardingVC: FinalOnboardingViewController? = (self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier.finalOnboardingIdentifier) as? FinalOnboardingViewController)
private lazy var finalOnboardingVC = (storyboard?.instantiateController(withIdentifier: .finalOnboardingIdentifier) as? FinalOnboardingViewController)
override func viewDidLoad() {
super.viewDidLoad()
@ -96,84 +96,102 @@ class OnboardingParentViewController: NSViewController {
@IBAction func continueOnboarding(_: NSButton) {
if positiveButton.tag == OnboardingType.welcome.rawValue {
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
}
navigateToPermissions()
} else if positiveButton.tag == OnboardingType.permissions.rawValue {
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
}
navigateToStartAtLogin()
} else if positiveButton.tag == OnboardingType.launchAtLogin.rawValue {
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
}
navigateToSearch()
} else if positiveButton.tag == OnboardingType.search.rawValue {
guard let fromViewController = onboardingSearchVC, let toViewController = finalOnboardingVC else {
assertionFailure()
return
}
navigateToFinalStage()
} else {
performFinalStepsBeforeFinishing()
}
}
addChildIfNeccessary(toViewController)
private func navigateToPermissions() {
guard let fromViewController = welcomeVC, let toViewController = permissionsVC else {
assertionFailure()
return
}
transition(from: fromViewController,
to: toViewController,
options: .slideLeft) {
self.backButton.tag = OnboardingType.search.rawValue
self.positiveButton.tag = OnboardingType.final.rawValue
self.positiveButton.title = "Launch Clocker"
}
addChildIfNeccessary(toViewController)
} else {
transition(from: fromViewController,
to: toViewController,
options: .slideLeft) {
self.positiveButton.tag = OnboardingType.permissions.rawValue
self.positiveButton.title = "Continue"
self.backButton.isHidden = false
}
}
self.positiveButton.tag = OnboardingType.complete.rawValue
private func navigateToStartAtLogin() {
guard let fromViewController = permissionsVC, let toViewController = startAtLoginVC else {
assertionFailure()
return
}
// Install the menubar option!
let appDelegate = NSApplication.shared.delegate as? AppDelegate
appDelegate?.continueUsually()
addChildIfNeccessary(toViewController)
view.window?.close()
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
}
}
if ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument) == false {
UserDefaults.standard.set(true, forKey: CLShowOnboardingFlow)
}
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)
}
}
@ -185,64 +203,78 @@ class OnboardingParentViewController: NSViewController {
@IBAction func back(_: Any) {
if backButton.tag == OnboardingType.welcome.rawValue {
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"
}
goBackToWelcomeScreen()
} else if backButton.tag == OnboardingType.permissions.rawValue {
// 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"
}
goBackToPermissions()
} else if backButton.tag == OnboardingType.launchAtLogin.rawValue {
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
}
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
}
guard let fromViewController = finalOnboardingVC, let toViewController = onboardingSearchVC 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"
}
}
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 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"
}
}

61
Clocker/Onboarding/OnboardingSearchController.swift

@ -200,7 +200,13 @@ class OnboardingSearchController: NSViewController {
}
}
private var placeholders: [String] = ["New York", "Los Angeles", "Chicago", "Moscow", "Tokyo", "Istanbul", "Beijing", "Shanghai", "Sao Paulo", "Cairo", "Mexico City", "London", "Seoul", "Copenhagen", "Tel Aviv", "Bern", "San Francisco", "Los Angeles", "Sydney NSW", "Berlin"]
private var placeholders: [String] = ["New York", "Los Angeles", "Chicago",
"Moscow", "Tokyo", "Istanbul",
"Beijing", "Shanghai", "Sao Paulo",
"Cairo", "Mexico City", "London",
"Seoul", "Copenhagen", "Tel Aviv",
"Bern", "San Francisco", "Los Angeles",
"Sydney NSW", "Berlin"]
private func setup() {
appName.stringValue = "Quick Add Locations"
@ -278,12 +284,7 @@ class OnboardingSearchController: NSViewController {
self.results = []
if let errorPresent = error {
if errorPresent.localizedDescription == PreferencesConstants.offlineErrorMessage {
self.setInfoLabel(PreferencesConstants.noInternetConnectivityError)
} else {
self.setInfoLabel(PreferencesConstants.tryAgainMessage)
}
self.presentErrorMessage(errorPresent.localizedDescription)
setupForError()
return
}
@ -302,23 +303,7 @@ class OnboardingSearchController: NSViewController {
return
}
for result in searchResults!.results {
let location = result.geometry.location
let latitude = location.lat
let longitude = location.lng
let formattedAddress = result.formattedAddress
let totalPackage = [
"latitude": latitude,
"longitude": longitude,
CLTimezoneName: formattedAddress,
CLCustomLabel: formattedAddress,
CLTimezoneID: CLEmptyString,
CLPlaceIdentifier: result.placeId
] as [String: Any]
self.results.append(TimezoneData(with: totalPackage))
}
self.appendResultsToFilteredArray(searchResults!.results)
self.setInfoLabel(CLEmptyString)
@ -327,6 +312,34 @@ class OnboardingSearchController: NSViewController {
})
}
private func presentErrorMessage(_ errorMessage: String) {
if errorMessage == PreferencesConstants.offlineErrorMessage {
self.setInfoLabel(PreferencesConstants.noInternetConnectivityError)
} else {
self.setInfoLabel(PreferencesConstants.tryAgainMessage)
}
}
private func appendResultsToFilteredArray(_ results: [SearchResult.Result]) {
results.forEach {
let location = $0.geometry.location
let latitude = location.lat
let longitude = location.lng
let formattedAddress = $0.formattedAddress
let totalPackage = [
"latitude": latitude,
"longitude": longitude,
CLTimezoneName: formattedAddress,
CLCustomLabel: formattedAddress,
CLTimezoneID: CLEmptyString,
CLPlaceIdentifier: $0.placeId
] as [String: Any]
self.results.append(TimezoneData(with: totalPackage))
}
}
// Extracting this out for tests
private func decode(from data: Data) -> SearchResult? {
let jsonDecoder = JSONDecoder()

12
Clocker/Overall App/NetworkManager.swift

@ -10,15 +10,23 @@ class NetworkManager: NSObject {
}()
static let internalServerError: NSError = {
let localizedError = """
There was a problem retrieving your information. Please try again later.
If the problem continues please contact App Support.
"""
let userInfoDictionary: [String: Any] = [NSLocalizedDescriptionKey: "Internal Error",
NSLocalizedFailureReasonErrorKey: "There was a problem retrieving your information. Please try again later. If the problem continues please contact App Support."]
NSLocalizedFailureReasonErrorKey: localizedError]
let error = NSError(domain: "APIError", code: 100, userInfo: userInfoDictionary)
return error
}()
static let unableToGenerateURL: NSError = {
let localizedError = """
There was a problem searching the location. Please try again later.
If the problem continues please contact App Support.
"""
let userInfoDictionary: [String: Any] = [NSLocalizedDescriptionKey: "Unable to generate URL",
NSLocalizedFailureReasonErrorKey: "There was a problem searching the location. Please try again later. If the problem continues please contact App Support."]
NSLocalizedFailureReasonErrorKey: localizedError]
let error = NSError(domain: "APIError", code: 100, userInfo: userInfoDictionary)
return error
}()

4
Clocker/Overall App/Themer.swift

@ -432,6 +432,8 @@ extension Themer {
}
}
return themeIndex == .light ? NSColor(deviceRed: 241.0 / 255.0, green: 241.0 / 255.0, blue: 241.0 / 255.0, alpha: 1.0) : NSColor(deviceRed: 42.0 / 255.0, green: 55.0 / 255.0, blue: 62.0 / 255.0, alpha: 1.0)
return themeIndex == .light ?
NSColor(deviceRed: 241.0 / 255.0, green: 241.0 / 255.0, blue: 241.0 / 255.0, alpha: 1.0) :
NSColor(deviceRed: 42.0 / 255.0, green: 55.0 / 255.0, blue: 62.0 / 255.0, alpha: 1.0)
}
}

2
Clocker/Panel/Data Layer/TimezoneDataOperations.swift

@ -168,7 +168,7 @@ extension TimezoneDataOperations {
return "\(todaysDate(with: sliderValue))\(timeDifference())"
}
let errorDictionary: [String: Any] = ["Timezone" : dataObject.timezone(),
let errorDictionary: [String: Any] = ["Timezone": dataObject.timezone(),
"Current Locale": Locale.autoupdatingCurrent.identifier,
"Slider Value": sliderValue,
"Today's Date": Date()]

33
Clocker/Panel/Notes Popover/NotesPopover.swift

@ -278,34 +278,24 @@ class NotesPopover: NSViewController {
private func insertTimezoneInDefaultPreferences() {
guard let model = dataObject, var timezones = timezoneObjects else { return }
let encodedObject = NSKeyedArchiver.archivedData(withRootObject: model)
timezones[currentRow] = encodedObject
DataStore.shared().setTimezones(timezones)
}
private func updateMenubarTitles() {
guard let model = dataObject, model.isFavourite == 1, var timezones = DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data] else { return }
let menubarIndex = timezones.firstIndex { (menubarLocation) -> Bool in
if let convertedObject = TimezoneData.customObject(from: menubarLocation) {
return convertedObject.isEqual(dataObject)
}
return false
}
if let index = menubarIndex {
let encodedObject = NSKeyedArchiver.archivedData(withRootObject: model)
timezones[index] = encodedObject
UserDefaults.standard.set(timezones, forKey: CLMenubarFavorites)
}
}
@ -370,7 +360,6 @@ class NotesPopover: NSViewController {
if eventCenter.reminderAccessNotDetermined() {
eventCenter.requestAccess(to: .reminder, completionHandler: { granted in
if granted {
OperationQueue.main.addOperation {
self.createReminder()
@ -402,7 +391,6 @@ class NotesPopover: NSViewController {
private func createReminder() {
guard let model = dataObject else { return }
if setReminderCheckbox.state == .on {
let eventCenter = EventCenter.sharedCenter()
let alertIndex = alertPopupButton.indexOfSelectedItem
@ -507,29 +495,19 @@ class NotesPopover: NSViewController {
}
setInitialReminderTime()
updateTimeFormat()
updateSecondsFormat()
}
private func updateTimeFormat() {
if dataObject?.overrideFormat.rawValue == 0 {
timeFormatControl.setSelected(true, forSegment: 0)
} else if dataObject?.overrideFormat.rawValue == 1 {
timeFormatControl.setSelected(true, forSegment: 1)
} else {
timeFormatControl.setSelected(true, forSegment: 2)
if let overrideFormat = dataObject?.overrideFormat.rawValue {
timeFormatControl.setSelected(true, forSegment: overrideFormat)
}
}
private func updateSecondsFormat() {
if dataObject?.overrideSecondsFormat.rawValue == 0 {
secondsFormatControl.setSelected(true, forSegment: 0)
} else if dataObject?.overrideSecondsFormat.rawValue == 1 {
secondsFormatControl.setSelected(true, forSegment: 1)
} else {
secondsFormatControl.setSelected(true, forSegment: 2)
if let overrideFormat = dataObject?.overrideSecondsFormat.rawValue {
secondsFormatControl.setSelected(true, forSegment: overrideFormat)
}
}
@ -549,13 +527,10 @@ extension NotesPopover: NSTextFieldDelegate {
// We need to do a couple of things if the customLabel is updated
// 1. Update the userDefaults
// 2. Check if the timezone is displayed in the menubar; if so, update the model
guard let model = dataObject else { return }
model.setLabel(customLabel.stringValue)
insertTimezoneInDefaultPreferences()
updateMenubarTitles()
NotificationCenter.default.post(name: NSNotification.Name.customLabelChanged,

4
Clocker/Panel/ParentPanelController.swift

@ -536,7 +536,9 @@ class ParentPanelController: NSWindowController {
stride(from: 0, to: preferences.count, by: 1).forEach {
let current = preferences[$0]
if $0 < mainTableView.numberOfRows, let cellView = mainTableView.view(atColumn: 0, row: $0, makeIfNecessary: false) as? TimezoneCellView, let model = TimezoneData.customObject(from: current) {
if $0 < mainTableView.numberOfRows,
let cellView = mainTableView.view(atColumn: 0, row: $0, makeIfNecessary: false) as? TimezoneCellView,
let model = TimezoneData.customObject(from: current) {
if let futureSliderCell = futureSlider.cell as? CustomSliderCell, futureSliderCell.tracking == true {
return
}

5
Clocker/Panel/UI/BackgroundPanelView.swift

@ -40,7 +40,10 @@ class BackgroundPanelView: NSView {
let xOrdinate = arrowMidX - BackgroundPanelConstants.kArrowHeight - curveOffset
let yOrdinate = frame.height - BackgroundPanelConstants.kArrowHeight - BackgroundPanelConstants.kBorderWidth
arrowPath.move(to: NSPoint(x: xOrdinate, y: yOrdinate))
arrowPath.relativeCurve(to: NSPoint(x: BackgroundPanelConstants.kArrowHeight + curveOffset, y: BackgroundPanelConstants.kBorderWidth), controlPoint1: NSPoint(x: curveOffset, y: 0), controlPoint2: NSPoint(x: BackgroundPanelConstants.kArrowHeight, y: BackgroundPanelConstants.kArrowHeight))
arrowPath.relativeCurve(to: NSPoint(x: BackgroundPanelConstants.kArrowHeight + curveOffset,
y: BackgroundPanelConstants.kBorderWidth),
controlPoint1: NSPoint(x: curveOffset, y: 0),
controlPoint2: NSPoint(x: BackgroundPanelConstants.kArrowHeight, y: BackgroundPanelConstants.kArrowHeight))
}
Themer.shared().mainBackgroundColor().setFill()

6
Clocker/Preferences/Appearance/AppearanceViewController.swift

@ -112,7 +112,11 @@ class AppearanceViewController: ParentViewController {
menubarDisplayOptionsLabel.stringValue = "Menubar Display Options"
menubarModeLabel.stringValue = "Menubar Mode"
[headerLabel, timeFormatLabel, panelTheme, dayDisplayOptionsLabel, showSliderLabel, showSecondsLabel, showSunriseLabel, largerTextLabel, futureSliderRangeLabel, includeDayLabel, includeDateLabel, includePlaceLabel, menubarDisplayOptionsLabel, appDisplayLabel, menubarModeLabel].forEach {
[headerLabel, timeFormatLabel, panelTheme,
dayDisplayOptionsLabel, showSliderLabel, showSecondsLabel,
showSunriseLabel, largerTextLabel, futureSliderRangeLabel,
includeDayLabel, includeDateLabel, includePlaceLabel,
menubarDisplayOptionsLabel, appDisplayLabel, menubarModeLabel].forEach {
$0?.textColor = Themer.shared().mainTextColor()
}
}

10
Clocker/Preferences/Calendar/CalendarViewController.swift

@ -224,7 +224,9 @@ class CalendarViewController: ParentViewController {
showEventsFromLabel.stringValue = "Show events from"
truncateAccessoryLabel.stringValue = "If meeting title is \"Meeting with Neel\" and truncate length is set to 5, text in menubar will appear as \"Meeti...\""
[headerLabel, upcomingEventView, allDayMeetingsLabel, showNextMeetingLabel, nextMeetingAccessoryLabel, truncateTextLabel, showEventsFromLabel, charactersField, truncateAccessoryLabel].forEach { $0?.textColor = Themer.shared().mainTextColor() }
[headerLabel, upcomingEventView, allDayMeetingsLabel,
showNextMeetingLabel, nextMeetingAccessoryLabel, truncateTextLabel,
showEventsFromLabel, charactersField, truncateAccessoryLabel].forEach { $0?.textColor = Themer.shared().mainTextColor() }
}
}
@ -253,12 +255,14 @@ extension CalendarViewController: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if let currentSource = calendars[row] as? String, let message = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "sourceCellView"), owner: self) as? SourceTableViewCell {
if let currentSource = calendars[row] as? String,
let message = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "sourceCellView"), owner: self) as? SourceTableViewCell {
message.sourceName.stringValue = currentSource
return message
}
if let currentSource = calendars[row] as? CalendarInfo, let calendarCell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "calendarCellView"), owner: self) as? CalendarTableViewCell {
if let currentSource = calendars[row] as? CalendarInfo,
let calendarCell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "calendarCellView"), owner: self) as? CalendarTableViewCell {
calendarCell.calendarName.stringValue = currentSource.calendar.title
calendarCell.calendarSelected.state = currentSource.selected ? NSControl.StateValue.on : NSControl.StateValue.off
calendarCell.calendarSelected.target = self

239
Clocker/Preferences/General/PreferencesViewController.swift

@ -479,7 +479,10 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate
UserDefaults.standard.set(filteredMenubars, forKey: CLMenubarFavorites)
if let appDelegate = NSApplication.shared.delegate as? AppDelegate, let menubarFavourites = DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data], menubarFavourites.isEmpty, DataStore.shared().shouldDisplay(.showMeetingInMenubar) == false {
if let appDelegate = NSApplication.shared.delegate as? AppDelegate,
let menubarFavourites = DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data],
menubarFavourites.isEmpty,
DataStore.shared().shouldDisplay(.showMeetingInMenubar) == false {
appDelegate.invalidateMenubarTimer(true)
}
@ -532,10 +535,15 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate
// Time to display the alert.
NSApplication.shared.activate(ignoringOtherApps: true)
let infoText = """
Multiple timezones occupy space and if macOS determines Clocker is occupying too much space, it'll hide Clocker entirely!
Enable Menubar Compact Mode to fit in more timezones in less space.
"""
let alert = NSAlert()
alert.showsSuppressionButton = true
alert.messageText = "More than one location added to the menubar 😅"
alert.informativeText = "Multiple timezones occupy space and if macOS determines Clocker is occupying too much space, it'll hide Clocker entirely! Enable Menubar Compact Mode to fit in more timezones in less space."
alert.informativeText = infoText
alert.addButton(withTitle: "Enable Compact Mode")
alert.addButton(withTitle: "Cancel")
@ -626,13 +634,21 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate
}
if tableColumn.identifier.rawValue == "formattedAddress" {
return arePlacesSortedInAscendingOrder ? object1.formattedAddress! > object2.formattedAddress! : object1.formattedAddress! < object2.formattedAddress!
return arePlacesSortedInAscendingOrder ?
object1.formattedAddress! > object2.formattedAddress! :
object1.formattedAddress! < object2.formattedAddress!
} else {
return arePlacesSortedInAscendingOrder ? object1.customLabel! > object2.customLabel! : object1.customLabel! < object2.customLabel!
return arePlacesSortedInAscendingOrder ?
object1.customLabel! > object2.customLabel! :
object1.customLabel! < object2.customLabel!
}
}
arePlacesSortedInAscendingOrder ? timezoneTableView.setIndicatorImage(NSImage(named: NSImage.Name("NSDescendingSortIndicator"))!, in: tableColumn) : timezoneTableView.setIndicatorImage(NSImage(named: NSImage.Name("NSAscendingSortIndicator"))!, in: tableColumn)
let indicatorImage = arePlacesSortedInAscendingOrder ?
NSImage(named: NSImage.Name("NSDescendingSortIndicator"))! :
NSImage(named: NSImage.Name("NSAscendingSortIndicator"))!
timezoneTableView.setIndicatorImage(indicatorImage, in: tableColumn)
arePlacesSortedInAscendingOrder.toggle()
@ -645,7 +661,7 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate
extension PreferencesViewController {
@objc private func search() {
var searchString = searchField.stringValue
let searchString = searchField.stringValue
if searchString.isEmpty {
dataTask?.cancel()
@ -657,8 +673,6 @@ extension PreferencesViewController {
dataTask?.cancel()
}
let userPreferredLanguage = Locale.preferredLanguages.first ?? "en-US"
OperationQueue.main.addOperation {
if self.availableTimezoneTableView.isHidden {
self.availableTimezoneTableView.isHidden = false
@ -675,26 +689,14 @@ extension PreferencesViewController {
self.placeholderLabel.placeholderString = "Searching for \(searchString)"
let words = searchString.components(separatedBy: CharacterSet.whitespacesAndNewlines)
searchString = words.joined(separator: CLEmptyString)
let urlString = "https://maps.googleapis.com/maps/api/geocode/json?address=\(searchString)&key=\(CLGeocodingKey)&language=\(userPreferredLanguage)"
self.dataTask = NetworkManager.task(with: urlString,
self.dataTask = NetworkManager.task(with: self.generateSearchURL(),
completionHandler: { [weak self] response, error in
guard let `self` = self else { return }
OperationQueue.main.addOperation {
if let errorPresent = error {
if errorPresent.localizedDescription == PreferencesConstants.offlineErrorMessage {
self.placeholderLabel.placeholderString = PreferencesConstants.noInternetConnectivityError
} else {
self.placeholderLabel.placeholderString = PreferencesConstants.tryAgainMessage
}
self.isActivityInProgress = false
self.presentError(errorPresent.localizedDescription)
return
}
@ -711,35 +713,61 @@ extension PreferencesViewController {
return
}
for result in searchResults!.results {
let location = result.geometry.location
let latitude = location.lat
let longitude = location.lng
let formattedAddress = result.formattedAddress
let totalPackage = [
"latitude": latitude,
"longitude": longitude,
CLTimezoneName: formattedAddress,
CLCustomLabel: formattedAddress,
CLTimezoneID: CLEmptyString,
CLPlaceIdentifier: result.placeId
] as [String: Any]
self.filteredArray.append(TimezoneData(with: totalPackage))
}
self.appendResultsToFilteredArray(searchResults!.results)
self.prepareUIForPresentingResults()
}
self.placeholderLabel.placeholderString = CLEmptyString
})
}
}
self.isActivityInProgress = false
private func generateSearchURL() -> String {
let userPreferredLanguage = Locale.preferredLanguages.first ?? "en-US"
self.availableTimezoneTableView.reloadData()
}
var searchString = searchField.stringValue
let words = searchString.components(separatedBy: CharacterSet.whitespacesAndNewlines)
searchString = words.joined(separator: CLEmptyString)
})
let url = "https://maps.googleapis.com/maps/api/geocode/json?address=\(searchString)&key=\(CLGeocodingKey)&language=\(userPreferredLanguage)"
return url
}
private func presentError(_ errorMessage: String) {
if errorMessage == PreferencesConstants.offlineErrorMessage {
self.placeholderLabel.placeholderString = PreferencesConstants.noInternetConnectivityError
} else {
self.placeholderLabel.placeholderString = PreferencesConstants.tryAgainMessage
}
self.isActivityInProgress = false
}
private func appendResultsToFilteredArray(_ results: [SearchResult.Result]) {
results.forEach {
let location = $0.geometry.location
let latitude = location.lat
let longitude = location.lng
let formattedAddress = $0.formattedAddress
let totalPackage = [
"latitude": latitude,
"longitude": longitude,
CLTimezoneName: formattedAddress,
CLCustomLabel: formattedAddress,
CLTimezoneID: CLEmptyString,
CLPlaceIdentifier: $0.placeId
] as [String: Any]
self.filteredArray.append(TimezoneData(with: totalPackage))
}
}
private func prepareUIForPresentingResults() {
self.placeholderLabel.placeholderString = CLEmptyString
self.isActivityInProgress = false
self.availableTimezoneTableView.reloadData()
}
// Extracting this out for tests
private func decode(from data: Data) -> SearchResult? {
let jsonDecoder = JSONDecoder()
@ -802,34 +830,8 @@ extension PreferencesViewController {
if error == nil, let json = response, let timezone = self.decodeTimezone(from: json) {
if self.availableTimezoneTableView.selectedRow >= 0 && self.availableTimezoneTableView.selectedRow < self.filteredArray.count {
guard let dataObject = self.filteredArray[self.availableTimezoneTableView.selectedRow] as? TimezoneData else {
assertionFailure("Data was unexpectedly nil")
return
}
var filteredAddress = "Error"
if let address = dataObject.formattedAddress {
filteredAddress = address.filteredName()
}
let newTimeZone = [
CLTimezoneID: timezone.timeZoneId,
CLTimezoneName: filteredAddress,
CLPlaceIdentifier: dataObject.placeID!,
"latitude": dataObject.latitude!,
"longitude": dataObject.longitude!,
"nextUpdate": CLEmptyString,
CLCustomLabel: filteredAddress
] as [String: Any]
let timezoneObject = TimezoneData(with: newTimeZone)
let operationsObject = TimezoneDataOperations(with: timezoneObject)
operationsObject.saveObject()
Logger.log(object: ["PlaceName": filteredAddress, "Timezone": timezone.timeZoneId], for: "Filtered Address")
self.installTimezone(timezone)
}
self.updateViewState()
} else {
OperationQueue.main.addOperation {
@ -846,6 +848,35 @@ extension PreferencesViewController {
}
}
private func installTimezone(_ timezone: Timezone) {
guard let dataObject = self.filteredArray[self.availableTimezoneTableView.selectedRow] as? TimezoneData else {
assertionFailure("Data was unexpectedly nil")
return
}
var filteredAddress = "Error"
if let address = dataObject.formattedAddress {
filteredAddress = address.filteredName()
}
let newTimeZone = [
CLTimezoneID: timezone.timeZoneId,
CLTimezoneName: filteredAddress,
CLPlaceIdentifier: dataObject.placeID!,
"latitude": dataObject.latitude!,
"longitude": dataObject.longitude!,
"nextUpdate": CLEmptyString,
CLCustomLabel: filteredAddress
] as [String: Any]
let timezoneObject = TimezoneData(with: newTimeZone)
let operationsObject = TimezoneDataOperations(with: timezoneObject)
operationsObject.saveObject()
Logger.log(object: ["PlaceName": filteredAddress, "Timezone": timezone.timeZoneId], for: "Filtered Address")
}
private func resetStateAndShowDisconnectedMessage() {
OperationQueue.main.addOperation {
self.showMessage()
@ -969,55 +1000,59 @@ extension PreferencesViewController {
}
} else {
let data = TimezoneData()
data.setLabel(CLEmptyString)
cleanupAfterInstallingTimezone()
}
}
if searchField.stringValue.isEmpty == false {
if timezoneFilteredArray.count <= availableTimezoneTableView.selectedRow {
return
}
private func cleanupAfterInstallingTimezone() {
let data = TimezoneData()
data.setLabel(CLEmptyString)
let currentSelection = timezoneFilteredArray[availableTimezoneTableView.selectedRow]
if searchField.stringValue.isEmpty == false {
if timezoneFilteredArray.count <= availableTimezoneTableView.selectedRow {
return
}
let metaInfo = metadata(for: currentSelection)
data.timezoneID = metaInfo.0
data.formattedAddress = metaInfo.1
let currentSelection = timezoneFilteredArray[availableTimezoneTableView.selectedRow]
} else {
let currentSelection = timezoneArray[availableTimezoneTableView.selectedRow]
let metaInfo = metadata(for: currentSelection)
data.timezoneID = metaInfo.0
data.formattedAddress = metaInfo.1
let metaInfo = metadata(for: currentSelection)
data.timezoneID = metaInfo.0
data.formattedAddress = metaInfo.1
}
} else {
let currentSelection = timezoneArray[availableTimezoneTableView.selectedRow]
data.selectionType = .timezone
let metaInfo = metadata(for: currentSelection)
data.timezoneID = metaInfo.0
data.formattedAddress = metaInfo.1
}
let operationObject = TimezoneDataOperations(with: data)
operationObject.saveObject()
data.selectionType = .timezone
timezoneFilteredArray = []
let operationObject = TimezoneDataOperations(with: data)
operationObject.saveObject()
timezoneArray = []
timezoneFilteredArray = []
availableTimezoneTableView.reloadData()
timezoneArray = []
refreshTimezoneTableView()
availableTimezoneTableView.reloadData()
refreshTimezoneTableView()
refreshMainTable()
refreshMainTable()
timezonePanel.close()
timezonePanel.close()
placeholderLabel.placeholderString = CLEmptyString
placeholderLabel.placeholderString = CLEmptyString
searchField.stringValue = CLEmptyString
searchField.stringValue = CLEmptyString
searchField.placeholderString = "Enter a city, state or country name"
searchField.placeholderString = "Enter a city, state or country name"
availableTimezoneTableView.isHidden = false
availableTimezoneTableView.isHidden = false
isActivityInProgress = false
}
isActivityInProgress = false
}
private func metadata(for selection: String) -> (String, String) {

8
Clocker/Preferences/OneWindowController.swift

@ -35,7 +35,7 @@ class OneWindowController: NSWindowController {
NSAnimationContext.runAnimationGroup({ (context) in
context.duration = 1
context.timingFunction = CAMediaTimingFunction(name:CAMediaTimingFunctionName.easeOut)
context.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
self.window?.animator().backgroundColor = Themer.shared().mainBackgroundColor()
})
@ -60,8 +60,8 @@ class OneWindowController: NSWindowController {
}
class func shared() -> OneWindowController {
if (sharedWindow == nil) {
let prefStoryboard = NSStoryboard.init(name: "Preferences", bundle: nil)
if sharedWindow == nil {
let prefStoryboard = NSStoryboard.init(name: "Preferences", bundle: nil)
sharedWindow = prefStoryboard.instantiateInitialController() as? OneWindowController
}
return sharedWindow
@ -95,7 +95,7 @@ class OneWindowController: NSWindowController {
tabViewController.tabViewItems.forEach { (tabViewItem) in
let identity = (tabViewItem.identifier as? String) ?? ""
if (identifierTOImageMapping[identity] != nil) {
if identifierTOImageMapping[identity] != nil {
tabViewItem.image = identifierTOImageMapping[identity]
}
}

Loading…
Cancel
Save