Browse Source

Merge branch 'master' of https://github.com/n0shake/Clocker

pull/113/head
Abhishek 3 years ago
parent
commit
98cc5607d6
  1. 14
      Clocker/AppDelegate.swift
  2. 4
      Clocker/Clocker/Clocker.entitlements
  3. 3
      Clocker/Clocker/ar.lproj/Localizable.strings
  4. 5
      Clocker/Clocker/ca.lproj/Localizable.strings
  5. 5
      Clocker/Clocker/de.lproj/Localizable.strings
  6. 5
      Clocker/Clocker/en.lproj/Localizable.strings
  7. 5
      Clocker/Clocker/es.lproj/Localizable.strings
  8. 5
      Clocker/Clocker/fr.lproj/Localizable.strings
  9. 5
      Clocker/Clocker/hi.lproj/Localizable.strings
  10. 5
      Clocker/Clocker/hr.lproj/Localizable.strings
  11. 5
      Clocker/Clocker/it.lproj/Localizable.strings
  12. 5
      Clocker/Clocker/ja.lproj/Localizable.strings
  13. 5
      Clocker/Clocker/ko.lproj/Localizable.strings
  14. 5
      Clocker/Clocker/nl.lproj/Localizable.strings
  15. 3
      Clocker/Clocker/pl.lproj/Localizable.strings
  16. 5
      Clocker/Clocker/pt-BR.lproj/Localizable.strings
  17. 5
      Clocker/Clocker/ru.lproj/Localizable.strings
  18. 3
      Clocker/Clocker/tr.lproj/Localizable.strings
  19. 5
      Clocker/Clocker/zh-Hans.lproj/Localizable.strings
  20. 5
      Clocker/Clocker/zh-Hant.lproj/Localizable.strings
  21. 13
      Clocker/ClockerUnitTests/AppDelegateTests.swift
  22. 9
      Clocker/ClockerUnitTests/ClockerUnitTests.swift
  23. 17
      Clocker/ClockerUnitTests/StandardMenubarHandlerTests.swift
  24. 4
      Clocker/Dependencies/Date Additions/Date+TimeAgo.swift
  25. 4
      Clocker/Events and Reminders/CalendarHandler.swift
  26. 8
      Clocker/Onboarding/OnboardingParentViewController.swift
  27. 15
      Clocker/Onboarding/OnboardingSearchController.swift
  28. 43
      Clocker/Overall App/DataStore.swift
  29. 1
      Clocker/Overall App/Strings.swift
  30. 11
      Clocker/Panel/PanelController.swift
  31. 19
      Clocker/Panel/ParentPanelController.swift
  32. 2
      Clocker/Panel/Upcoming Events/UpcomingEventViewItem.swift
  33. 12
      Clocker/Preferences/About/AboutViewController.swift
  34. 6
      Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift
  35. 17
      Clocker/Preferences/Appearance/AppearanceViewController.swift
  36. 21
      Clocker/Preferences/Calendar/CalendarViewController.swift
  37. 2
      Clocker/Preferences/General/PreferencesViewController.swift
  38. 2
      Clocker/Preferences/General/SearchDataSource.swift
  39. 6
      Clocker/Preferences/Menu Bar/MenubarTitleProvider.swift
  40. 1
      Clocker/Preferences/Menu Bar/StatusContainerView.swift
  41. 10
      Clocker/Preferences/Menu Bar/StatusItemHandler.swift
  42. 90
      Clocker/Preferences/Preferences.storyboard

14
Clocker/AppDelegate.swift

@ -8,7 +8,7 @@ import FirebaseCrashlytics
open class AppDelegate: NSObject, NSApplicationDelegate { open class AppDelegate: NSObject, NSApplicationDelegate {
private lazy var floatingWindow = FloatingWindowController.shared() private lazy var floatingWindow = FloatingWindowController.shared()
private lazy var panelController = PanelController.shared() internal lazy var panelController = PanelController(windowNibName: .panel)
private var statusBarHandler: StatusItemHandler! private var statusBarHandler: StatusItemHandler!
private var panelObserver: NSKeyValueObservation? private var panelObserver: NSKeyValueObservation?
@ -84,7 +84,6 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
let floatingWindow = FloatingWindowController.shared() let floatingWindow = FloatingWindowController.shared()
floatingWindow.openPreferences(NSButton()) floatingWindow.openPreferences(NSButton())
} else { } else {
let panelController = PanelController.shared()
panelController.openPreferences(NSButton()) panelController.openPreferences(NSButton())
} }
} }
@ -107,6 +106,11 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
} }
func continueUsually() { func continueUsually() {
// Cleanup onboarding controller after its done!
if controller != nil {
controller = nil
}
// Check if another instance of the app is already running. If so, then stop this one. // Check if another instance of the app is already running. If so, then stop this one.
checkIfAppIsAlreadyOpen() checkIfAppIsAlreadyOpen()
@ -161,12 +165,6 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
} }
} }
private func showAppAlreadyOpenMessage() {
showAlert(message: "An instance of Clocker is already open 😅",
informativeText: "This instance of Clocker will terminate now.",
buttonTitle: "Close")
}
private func showAlert(message: String, informativeText: String, buttonTitle: String) { private func showAlert(message: String, informativeText: String, buttonTitle: String) {
NSApplication.shared.activate(ignoringOtherApps: true) NSApplication.shared.activate(ignoringOtherApps: true)
let alert = NSAlert() let alert = NSAlert()

4
Clocker/Clocker/Clocker.entitlements

@ -2,10 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.developer.icloud-container-identifiers</key>
<array/>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.cs.disable-library-validation</key> <key>com.apple.security.cs.disable-library-validation</key>

3
Clocker/Clocker/ar.lproj/Localizable.strings

@ -158,3 +158,6 @@
"New Zealand" = "New Zealand"; "New Zealand" = "New Zealand";
"Florida" = "Florida"; "Florida" = "Florida";
"San Francisco" = "San Francisco"; "San Francisco" = "San Francisco";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/ca.lproj/Localizable.strings

@ -161,6 +161,9 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard"; "Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉";
"Great going." = "Great going."; "Great going." = "Great going.";
"Happy Weekend." = "Happy Weekend."; "Happy Weekend." = "Happy Weekend.";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/de.lproj/Localizable.strings

@ -161,6 +161,9 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard"; "Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉";
"Great going." = "Great going."; "Great going." = "Great going.";
"Happy Weekend." = "Happy Weekend."; "Happy Weekend." = "Happy Weekend.";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/en.lproj/Localizable.strings

@ -165,6 +165,9 @@
"Copied to Clipboard" = "Copied to Clipboard"; "Copied to Clipboard" = "Copied to Clipboard";
// Upcoming Event View // Upcoming Event View
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉";
"Great going." = "Great going."; "Great going." = "Great going.";
"Happy Weekend." = "Happy Weekend."; "Happy Weekend." = "Happy Weekend.";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/es.lproj/Localizable.strings

@ -163,6 +163,9 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard"; "Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉";
"Great going." = "Great going."; "Great going." = "Great going.";
"Happy Weekend." = "Happy Weekend."; "Happy Weekend." = "Happy Weekend.";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/fr.lproj/Localizable.strings

@ -162,6 +162,9 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard"; "Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉";
"Great going." = "Great going."; "Great going." = "Great going.";
"Happy Weekend." = "Happy Weekend."; "Happy Weekend." = "Happy Weekend.";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/hi.lproj/Localizable.strings

@ -157,6 +157,9 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard"; "Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉";
"Great going." = "Great going."; "Great going." = "Great going.";
"Happy Weekend." = "Happy Weekend."; "Happy Weekend." = "Happy Weekend.";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/hr.lproj/Localizable.strings

@ -163,6 +163,9 @@
"Daylights Saving transition will occur in < 24 hours" = "Prijelaz na ljetno računanje vremena dogodit će se za < 24 sata"; "Daylights Saving transition will occur in < 24 hours" = "Prijelaz na ljetno računanje vremena dogodit će se za < 24 sata";
"Copied to Clipboard" = "Kopirano u međuspremnik"; "Copied to Clipboard" = "Kopirano u međuspremnik";
"No upcoming events for today!" = "Za danas nema predstojećih događaja!"; "No upcoming events for today!" = "Za danas nema predstojećih događaja 🎉";
"Great going." = "Super."; "Great going." = "Super.";
"Happy Weekend." = "Uživaj vikend."; "Happy Weekend." = "Uživaj vikend.";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/it.lproj/Localizable.strings

@ -162,6 +162,9 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard"; "Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉";
"Great going." = "Great going."; "Great going." = "Great going.";
"Happy Weekend." = "Happy Weekend."; "Happy Weekend." = "Happy Weekend.";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/ja.lproj/Localizable.strings

@ -192,7 +192,7 @@
"No upcoming event." = "No upcoming event."; "No upcoming event." = "No upcoming event.";
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉";
/* Onboarding */ /* Onboarding */
"Open Clocker At Login" = "ログイン時に Clocker を開始"; "Open Clocker At Login" = "ログイン時に Clocker を開始";
@ -355,3 +355,6 @@
"start-at-login" = "ログイン時に開始"; "start-at-login" = "ログイン時に開始";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/ko.lproj/Localizable.strings

@ -164,6 +164,9 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard"; "Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉!";
"Great going." = "Great going."; "Great going." = "Great going.";
"Happy Weekend." = "Happy Weekend."; "Happy Weekend." = "Happy Weekend.";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/nl.lproj/Localizable.strings

@ -161,6 +161,9 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard"; "Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉";
"Great going." = "Great going."; "Great going." = "Great going.";
"Happy Weekend." = "Happy Weekend."; "Happy Weekend." = "Happy Weekend.";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

3
Clocker/Clocker/pl.lproj/Localizable.strings

@ -158,3 +158,6 @@
"New Zealand" = "Nowa Zelandia"; "New Zealand" = "Nowa Zelandia";
"Florida" = "Floryda"; "Florida" = "Floryda";
"San Francisco" = "San Francisco"; "San Francisco" = "San Francisco";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/pt-BR.lproj/Localizable.strings

@ -162,6 +162,9 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard"; "Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉";
"Great going." = "Great going."; "Great going." = "Great going.";
"Happy Weekend." = "Happy Weekend."; "Happy Weekend." = "Happy Weekend.";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/ru.lproj/Localizable.strings

@ -154,6 +154,9 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard"; "Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉";
"Great going." = "Great going."; "Great going." = "Great going.";
"Happy Weekend." = "Happy Weekend."; "Happy Weekend." = "Happy Weekend.";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

3
Clocker/Clocker/tr.lproj/Localizable.strings

@ -159,3 +159,6 @@ Takvimleri Görüntüle";
"New Zealand" = "New Zealand"; "New Zealand" = "New Zealand";
"Florida" = "Florida"; "Florida" = "Florida";
"San Francisco" = "San Francisco"; "San Francisco" = "San Francisco";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/zh-Hans.lproj/Localizable.strings

@ -155,6 +155,9 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard"; "Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉";
"Great going." = "Great going."; "Great going." = "Great going.";
"Happy Weekend." = "Happy Weekend."; "Happy Weekend." = "Happy Weekend.";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

5
Clocker/Clocker/zh-Hant.lproj/Localizable.strings

@ -192,7 +192,7 @@
"No upcoming event." = "沒有即將到來的行程"; "No upcoming event." = "沒有即將到來的行程";
"No upcoming events for today!" = "No upcoming events for today!"; "No upcoming events for today!" = "No upcoming events for today 🎉";
/* Onboarding */ /* Onboarding */
"Open Clocker At Login" = "在登入時打開 Clocker"; "Open Clocker At Login" = "在登入時打開 Clocker";
@ -355,3 +355,6 @@
"start-at-login" = "在登入時啟動"; "start-at-login" = "在登入時啟動";
// iCloud
"Enable iCloud Sync" = "Enable iCloud Sync";

13
Clocker/ClockerUnitTests/AppDelegateTests.swift

@ -136,17 +136,4 @@ class AppDelegateTests: XCTestCase {
UserDefaults.standard.set(0, forKey: CLMenubarCompactMode) // Set the menubar mode back to compact UserDefaults.standard.set(0, forKey: CLMenubarCompactMode) // Set the menubar mode back to compact
} }
func testTogglingPanel() {
UserDefaults.standard.set(1, forKey: CLShowAppInForeground)
let subject = NSApplication.shared.delegate as? AppDelegate
subject?.ping("MockArgument")
UserDefaults.standard.set(0, forKey: CLShowAppInForeground)
let hasActiveGetter = PanelController.shared().hasActivePanel
subject?.ping("MockArgument")
XCTAssertNotEqual(hasActiveGetter, PanelController.shared().hasActivePanel)
}
} }

9
Clocker/ClockerUnitTests/ClockerUnitTests.swift

@ -173,7 +173,7 @@ class ClockerUnitTests: XCTestCase {
XCTAssertTrue(operations.timeDifference() == ", 9h 30m ahead", "Difference was unexpectedly: \(operations.timeDifference())") XCTAssertTrue(operations.timeDifference() == ", 9h 30m ahead", "Difference was unexpectedly: \(operations.timeDifference())")
XCTAssertTrue(californiaOperations.timeDifference() == ", 3h behind", "Difference was unexpectedly: \(californiaOperations.timeDifference())") XCTAssertTrue(californiaOperations.timeDifference() == ", 3h behind", "Difference was unexpectedly: \(californiaOperations.timeDifference())")
XCTAssertTrue(floridaOperations.timeDifference() == "", "Difference was unexpectedly: \(floridaOperations.timeDifference())") XCTAssertTrue(floridaOperations.timeDifference() == "", "Difference was unexpectedly: \(floridaOperations.timeDifference())")
XCTAssertTrue(aucklandOperations.timeDifference() == ", 17h ahead", "Difference was unexpectedly: \(aucklandOperations.timeDifference())") XCTAssertTrue(aucklandOperations.timeDifference() == ", 16h ahead", "Difference was unexpectedly: \(aucklandOperations.timeDifference())")
XCTAssertTrue(omahaOperations.timeDifference() == ", an hour behind", "Difference was unexpectedly: \(omahaOperations.timeDifference())") XCTAssertTrue(omahaOperations.timeDifference() == ", an hour behind", "Difference was unexpectedly: \(omahaOperations.timeDifference())")
} }
@ -406,10 +406,9 @@ class ClockerUnitTests: XCTestCase {
func testToasty() { func testToasty() {
let view = NSView(frame: CGRect.zero) let view = NSView(frame: CGRect.zero)
view.makeToast("Hello, this is a toast") view.makeToast("Hello, this is a toast")
XCTAssertEqual(view.subviews.first?.accessibilityIdentifier(), "ToastView") XCTAssertEqual(view.subviews.first?.accessibilityIdentifier(), "ToastView")
let toastExpectation = expectation(description: "Toast View should hide after 1 second")
let expectation = expectation(description: "Toast View should hide after 1 second") let result = XCTWaiter.wait(for: [toastExpectation], timeout: 1.5) // Set 1.5 seconds here for a little leeway
let result = XCTWaiter.wait(for: [expectation], timeout: 1.5) // Set 2 seconds here for a little leeway
if result == XCTWaiter.Result.timedOut { if result == XCTWaiter.Result.timedOut {
XCTAssertTrue(view.subviews.isEmpty) XCTAssertTrue(view.subviews.isEmpty)
} }

17
Clocker/ClockerUnitTests/StandardMenubarHandlerTests.swift

@ -13,7 +13,7 @@ class StandardMenubarHandlerTests: XCTestCase {
"nextUpdate": "", "nextUpdate": "",
"latitude": "19.0759837", "latitude": "19.0759837",
"longitude": "72.8776559"] "longitude": "72.8776559"]
private func makeMockStore(with menubarMode: Int = 1) -> DataStore { private func makeMockStore(with menubarMode: Int = 1) -> DataStore {
// Wipe all timezones from UserDefaults // Wipe all timezones from UserDefaults
guard let defaults = UserDefaults(suiteName: "com.abhishek.Clocker.StandardMenubarHandlerTests") else { guard let defaults = UserDefaults(suiteName: "com.abhishek.Clocker.StandardMenubarHandlerTests") else {
@ -25,10 +25,11 @@ class StandardMenubarHandlerTests: XCTestCase {
XCTAssertNotEqual(defaults, UserDefaults.standard) XCTAssertNotEqual(defaults, UserDefaults.standard)
return DataStore(with: defaults) return DataStore(with: defaults)
} }
private func saveObject(object: TimezoneData, private func saveObject(object: TimezoneData,
in store: DataStore, in store: DataStore,
at index: Int = -1) { at index: Int = -1)
{
var defaults = store.timezones() var defaults = store.timezones()
let encodedObject = NSKeyedArchiver.archivedData(withRootObject: object as Any) let encodedObject = NSKeyedArchiver.archivedData(withRootObject: object as Any)
index == -1 ? defaults.append(encodedObject) : defaults.insert(encodedObject, at: index) index == -1 ? defaults.append(encodedObject) : defaults.insert(encodedObject, at: index)
@ -81,14 +82,14 @@ class StandardMenubarHandlerTests: XCTestCase {
// Test menubar string is absent // Test menubar string is absent
XCTAssertTrue(menubarString.count == 0) XCTAssertTrue(menubarString.count == 0)
} }
func testWithEmptyMenubarTimezones() { func testWithEmptyMenubarTimezones() {
let store = makeMockStore() let store = makeMockStore()
store.setTimezones(nil) store.setTimezones(nil)
let menubarHandler = MenubarTitleProvider(with: store) let menubarHandler = MenubarTitleProvider(with: store)
XCTAssertNil(menubarHandler.titleForMenubar()) XCTAssertNil(menubarHandler.titleForMenubar())
} }
func testWithStandardMenubarMode() { func testWithStandardMenubarMode() {
// Set mode to standard mode // Set mode to standard mode
let store = makeMockStore(with: 0) let store = makeMockStore(with: 0)
@ -96,11 +97,11 @@ class StandardMenubarHandlerTests: XCTestCase {
let dataObject = TimezoneData(with: mumbai) let dataObject = TimezoneData(with: mumbai)
dataObject.isFavourite = 1 dataObject.isFavourite = 1
saveObject(object: dataObject, in: store) saveObject(object: dataObject, in: store)
let menubarHandler = MenubarTitleProvider(with: store) let menubarHandler = MenubarTitleProvider(with: store)
XCTAssertNil(menubarHandler.titleForMenubar()) XCTAssertNil(menubarHandler.titleForMenubar())
} }
func testProviderPassingAllConditions() { func testProviderPassingAllConditions() {
// Set mode to standard mode // Set mode to standard mode
let store = makeMockStore() let store = makeMockStore()
@ -108,7 +109,7 @@ class StandardMenubarHandlerTests: XCTestCase {
let dataObject = TimezoneData(with: mumbai) let dataObject = TimezoneData(with: mumbai)
dataObject.isFavourite = 1 dataObject.isFavourite = 1
saveObject(object: dataObject, in: store) saveObject(object: dataObject, in: store)
let menubarHandler = MenubarTitleProvider(with: store) let menubarHandler = MenubarTitleProvider(with: store)
XCTAssertNotNil(menubarHandler.titleForMenubar()) XCTAssertNotNil(menubarHandler.titleForMenubar())
} }

4
Clocker/Dependencies/Date Additions/Date+TimeAgo.swift

@ -115,10 +115,10 @@ public extension Date {
return DateToolsLocalizedStrings("An hour ago") return DateToolsLocalizedStrings("An hour ago")
} else if components.minute! >= 2 { } else if components.minute! >= 2 {
return logicalLocalizedStringFromFormat(format: "%%d %@minutes ago", value: components.minute!) return logicalLocalizedStringFromFormat(format: "%%d%@m ago", value: components.minute!)
} else if components.minute! >= 1 { } else if components.minute! >= 1 {
if numericTimes { if numericTimes {
return DateToolsLocalizedStrings("1 minute ago") return DateToolsLocalizedStrings("1m ago")
} }
return DateToolsLocalizedStrings("A minute ago") return DateToolsLocalizedStrings("A minute ago")

4
Clocker/Events and Reminders/CalendarHandler.swift

@ -493,12 +493,12 @@ struct EventInfo {
if timeIntervalSinceNowForMeeting < 0, timeIntervalSinceNowForMeeting > -300 { if timeIntervalSinceNowForMeeting < 0, timeIntervalSinceNowForMeeting > -300 {
return "started \(event.startDate.shortTimeAgoSinceNow) ago." return "started \(event.startDate.shortTimeAgoSinceNow) ago."
} else if event.startDate.isToday { } else if event.startDate.isToday {
let timeSince = Date().timeAgo(since: event.startDate) let timeSince = Date().timeAgo(since: event.startDate).lowercased()
let withoutAn = timeSince.replacingOccurrences(of: "an", with: CLEmptyString) let withoutAn = timeSince.replacingOccurrences(of: "an", with: CLEmptyString)
let withoutAgo = withoutAn.replacingOccurrences(of: "ago", with: CLEmptyString) let withoutAgo = withoutAn.replacingOccurrences(of: "ago", with: CLEmptyString)
// If the user has not turned on seconds granularity for one of the timezones, // If the user has not turned on seconds granularity for one of the timezones,
// we return "in 12 seconds" which looks weird. // we return "in 12 seconds" which looks weird.
return withoutAgo.contains("seconds") ? "starts soon" : "in \(withoutAgo.lowercased())" return withoutAgo.contains("seconds") ? "started" : "in \(withoutAgo.lowercased())"
} else if event.startDate.isTomorrow { } else if event.startDate.isTomorrow {
let hoursUntil = event.startDate.hoursUntil let hoursUntil = event.startDate.hoursUntil
return "in \(hoursUntil)h" return "in \(hoursUntil)h"

8
Clocker/Onboarding/OnboardingParentViewController.swift

@ -218,15 +218,15 @@ class OnboardingParentViewController: NSViewController {
func performFinalStepsBeforeFinishing() { func performFinalStepsBeforeFinishing() {
positiveButton.tag = OnboardingType.complete.rawValue positiveButton.tag = OnboardingType.complete.rawValue
// Install the menubar option!
let appDelegate = NSApplication.shared.delegate as? AppDelegate
appDelegate?.continueUsually()
view.window?.close() view.window?.close()
if ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument) == false { if ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument) == false {
UserDefaults.standard.set(true, forKey: CLShowOnboardingFlow) UserDefaults.standard.set(true, forKey: CLShowOnboardingFlow)
} }
// Install the menubar option!
let appDelegate = NSApplication.shared.delegate as? AppDelegate
appDelegate?.continueUsually()
} }
private func addChildIfNeccessary(_ viewController: NSViewController) { private func addChildIfNeccessary(_ viewController: NSViewController) {

15
Clocker/Onboarding/OnboardingSearchController.swift

@ -40,8 +40,7 @@ class OnboardingSearchController: NSViewController {
super.viewDidLoad() super.viewDidLoad()
view.wantsLayer = true view.wantsLayer = true
searchResultsDataSource = SearchDataSource(with: searchBar, location: .onboarding)
resultsTableView.isHidden = true resultsTableView.isHidden = true
resultsTableView.delegate = self resultsTableView.delegate = self
resultsTableView.setAccessibility("ResultsTableView") resultsTableView.setAccessibility("ResultsTableView")
@ -70,6 +69,16 @@ class OnboardingSearchController: NSViewController {
setupUndoButton() setupUndoButton()
} }
override func viewWillAppear() {
super.viewWillAppear()
searchResultsDataSource = SearchDataSource(with: searchBar, location: .onboarding)
}
override func viewWillDisappear() {
super.viewWillDisappear()
searchResultsDataSource = nil
}
deinit { deinit {
if let themeDidChangeNotif = themeDidChangeNotification { if let themeDidChangeNotif = themeDidChangeNotification {
@ -440,7 +449,7 @@ class OnboardingSearchController: NSViewController {
extension OnboardingSearchController: NSTableViewDataSource { extension OnboardingSearchController: NSTableViewDataSource {
func numberOfRows(in _: NSTableView) -> Int { func numberOfRows(in _: NSTableView) -> Int {
return searchResultsDataSource.resultsCount() return searchResultsDataSource != nil ? searchResultsDataSource.resultsCount() : 0
} }
func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? { func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? {

43
Clocker/Overall App/DataStore.swift

@ -18,12 +18,13 @@ enum ViewType {
case dayInMenubar case dayInMenubar
case menubarCompactMode case menubarCompactMode
case dstTransitionInfo case dstTransitionInfo
case sync
} }
class DataStore: NSObject { class DataStore: NSObject {
private static var sharedStore = DataStore(with: UserDefaults.standard) private static var sharedStore = DataStore(with: UserDefaults.standard)
private var userDefaults: UserDefaults! private var userDefaults: UserDefaults!
private var ubiquitousStore: NSUbiquitousKeyValueStore! private var ubiquitousStore: NSUbiquitousKeyValueStore?
// Since these pref can accessed every second, let's cache this // Since these pref can accessed every second, let's cache this
private var shouldDisplayDayInMenubar: Bool = false private var shouldDisplayDayInMenubar: Bool = false
@ -41,9 +42,39 @@ class DataStore: NSObject {
init(with defaults: UserDefaults) { init(with defaults: UserDefaults) {
super.init() super.init()
userDefaults = defaults userDefaults = defaults
ubiquitousStore = NSUbiquitousKeyValueStore.default
shouldDisplayDayInMenubar = shouldDisplay(.dayInMenubar) shouldDisplayDayInMenubar = shouldDisplay(.dayInMenubar)
shouldDisplayDateInMenubar = shouldDisplay(.dateInMenubar) shouldDisplayDateInMenubar = shouldDisplay(.dateInMenubar)
setupSyncNotification()
}
func setupSyncNotification() {
if shouldDisplay(.sync) {
ubiquitousStore = NSUbiquitousKeyValueStore.default
NotificationCenter.default.addObserver(self,
selector: #selector(ubiquitousKeyValueStoreChanged),
name: NSUbiquitousKeyValueStore.didChangeExternallyNotification,
object: NSUbiquitousKeyValueStore.default)
ubiquitousStore?.synchronize()
} else {
NotificationCenter.default.removeObserver(self,
name: NSUbiquitousKeyValueStore.didChangeExternallyNotification,
object: nil)
}
}
@objc func ubiquitousKeyValueStoreChanged(_ notification: Notification) {
let userInfo = notification.userInfo ?? [:]
let ubiquitousStore = notification.object as? NSUbiquitousKeyValueStore
print("--- User Info is \(userInfo)")
let currentTimezones = userDefaults.object(forKey: CLDefaultPreferenceKey) as? [Data]
let cloudTimezones = ubiquitousStore?.object(forKey: CLDefaultPreferenceKey) as? [Data]
if cloudTimezones != currentTimezones {
Logger.info("Syncing local timezones with data from the ☁")
userDefaults.set(cloudTimezones, forKey: CLDefaultPreferenceKey)
NotificationCenter.default.post(name: DataStore.didSyncFromExternalSourceNotification,
object: self)
}
} }
func timezones() -> [Data] { func timezones() -> [Data] {
@ -57,7 +88,7 @@ class DataStore: NSObject {
func setTimezones(_ timezones: [Data]?) { func setTimezones(_ timezones: [Data]?) {
userDefaults.set(timezones, forKey: CLDefaultPreferenceKey) userDefaults.set(timezones, forKey: CLDefaultPreferenceKey)
// iCloud sync // iCloud sync
ubiquitousStore.set(timezones, forKey: CLDefaultPreferenceKey) ubiquitousStore?.set(timezones, forKey: CLDefaultPreferenceKey)
} }
func menubarTimezones() -> [Data]? { func menubarTimezones() -> [Data]? {
@ -158,6 +189,8 @@ class DataStore: NSObject {
} }
return value == 0 return value == 0
case .sync:
return shouldDisplayHelper(CLEnableSyncKey)
} }
} }
@ -170,3 +203,7 @@ class DataStore: NSObject {
return value.isEqual(to: NSNumber(value: 0)) return value.isEqual(to: NSNumber(value: 0))
} }
} }
extension DataStore {
public static let didSyncFromExternalSourceNotification: NSNotification.Name = .init("didSyncFromExternalSourceNotification")
}

1
Clocker/Overall App/Strings.swift

@ -37,3 +37,4 @@ let CLInstallHomeIndicatorObject = "installHomeIndicatorObject"
let CLSwitchToCompactModeAlert = "com.abhishek.switchToCompactMode" let CLSwitchToCompactModeAlert = "com.abhishek.switchToCompactMode"
let CLDisplayDSTTransitionInfo = "com.abhishek.showDSTTransitionInfo" let CLDisplayDSTTransitionInfo = "com.abhishek.showDSTTransitionInfo"
let CLAppleInterfaceStyleKey = "AppleInterfaceStyle" let CLAppleInterfaceStyleKey = "AppleInterfaceStyle"
let CLEnableSyncKey = "com.abhishek.enableSync"

11
Clocker/Panel/PanelController.swift

@ -6,18 +6,12 @@ import CoreLoggerKit
class PanelController: ParentPanelController { class PanelController: ParentPanelController {
@objc dynamic var hasActivePanel: Bool = false @objc dynamic var hasActivePanel: Bool = false
static var sharedWindow = PanelController(windowNibName: .panel)
@IBOutlet var backgroundView: BackgroundPanelView! @IBOutlet var backgroundView: BackgroundPanelView!
override func windowDidLoad() { override func windowDidLoad() {
super.windowDidLoad() super.windowDidLoad()
} }
class func shared() -> PanelController {
return sharedWindow
}
override func awakeFromNib() { override func awakeFromNib() {
super.awakeFromNib() super.awakeFromNib()
@ -80,6 +74,8 @@ class PanelController: ParentPanelController {
super.dismissRowActions() super.dismissRowActions()
updateDefaultPreferences() updateDefaultPreferences()
setupUpcomingEventViewCollectionViewIfNeccesary()
if DataStore.shared().timezones().isEmpty || DataStore.shared().shouldDisplay(.futureSlider) == false { if DataStore.shared().timezones().isEmpty || DataStore.shared().shouldDisplay(.futureSlider) == false {
futureSliderView.isHidden = true futureSliderView.isHidden = true
@ -309,6 +305,9 @@ class PanelController: ParentPanelController {
window?.orderOut(nil) window?.orderOut(nil)
datasource = nil datasource = nil
upcomingEventsDataSource = nil
parentTimer?.pause()
parentTimer = nil
} }
func setActivePanel(newValue: Bool) { func setActivePanel(newValue: Bool) {

19
Clocker/Panel/ParentPanelController.swift

@ -37,7 +37,7 @@ class ParentPanelController: NSWindowController {
var datasource: TimezoneDataSource? var datasource: TimezoneDataSource?
private lazy var feedbackWindow = AppFeedbackWindowController.shared() private var feedbackWindow: AppFeedbackWindowController?
private var notePopover: NotesPopover? private var notePopover: NotesPopover?
@ -195,7 +195,8 @@ class ParentPanelController: NSWindowController {
selector: #selector(systemTimezoneDidChange), selector: #selector(systemTimezoneDidChange),
name: NSNotification.Name.NSSystemTimeZoneDidChange, name: NSNotification.Name.NSSystemTimeZoneDidChange,
object: nil) object: nil)
NotificationCenter.default.addObserver(forName: NSUbiquitousKeyValueStore.didChangeExternallyNotification,
NotificationCenter.default.addObserver(forName: DataStore.didSyncFromExternalSourceNotification,
object: self, object: self,
queue: OperationQueue.main) queue: OperationQueue.main)
{ [weak self] _ in { [weak self] _ in
@ -889,9 +890,9 @@ class ParentPanelController: NSWindowController {
} else if sender.title == PanelConstants.yesWithQuestionMark { } else if sender.title == PanelConstants.yesWithQuestionMark {
ReviewController.prompted() ReviewController.prompted()
updateReviewView() updateReviewView()
feedbackWindow = AppFeedbackWindowController.shared() feedbackWindow = AppFeedbackWindowController.shared()
feedbackWindow.showWindow(nil) feedbackWindow?.appFeedbackWindowDelegate = self
feedbackWindow?.showWindow(nil)
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
} else { } else {
updateReviewView() updateReviewView()
@ -985,7 +986,9 @@ class ParentPanelController: NSWindowController {
} }
@objc func reportIssue() { @objc func reportIssue() {
feedbackWindow.showWindow(nil) feedbackWindow = AppFeedbackWindowController.shared()
feedbackWindow?.appFeedbackWindowDelegate = self
feedbackWindow?.showWindow(nil)
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
window?.orderOut(nil) window?.orderOut(nil)
@ -1146,3 +1149,9 @@ extension ParentPanelController: NSSharingServicePickerDelegate {
return clipboardCopy return clipboardCopy
} }
} }
extension ParentPanelController: AppFeedbackWindowControllerDelegate {
func appFeedbackWindowWillClose() {
feedbackWindow = nil
}
}

2
Clocker/Panel/Upcoming Events/UpcomingEventViewItem.swift

@ -90,7 +90,7 @@ class UpcomingEventViewItem: NSCollectionViewItem {
func setupEmptyState() { func setupEmptyState() {
let subtitle = NSCalendar.autoupdatingCurrent.isDateInWeekend(Date()) ? NSLocalizedString("Happy Weekend.", comment: "Button Title for no upcoming event") : NSLocalizedString("Great going.", comment: "Button Title for no upcoming event") let subtitle = NSCalendar.autoupdatingCurrent.isDateInWeekend(Date()) ? NSLocalizedString("Happy Weekend.", comment: "Button Title for no upcoming event") : NSLocalizedString("Great going.", comment: "Button Title for no upcoming event")
setAlternateState(NSLocalizedString("No upcoming events for today 🎉!", comment: "Next Event Label with no upcoming event"), setAlternateState(NSLocalizedString("No upcoming events for today!", comment: "Next Event Label with no upcoming event"),
subtitle, subtitle,
NSColor.systemGreen, NSColor.systemGreen,
nil) nil)

12
Clocker/Preferences/About/AboutViewController.swift

@ -22,7 +22,7 @@ class AboutViewController: ParentViewController {
@IBOutlet var versionField: NSTextField! @IBOutlet var versionField: NSTextField!
private var themeDidChangeNotification: NSObjectProtocol? private var themeDidChangeNotification: NSObjectProtocol?
private lazy var feedbackWindow = AppFeedbackWindowController.shared() private var feedbackWindow: AppFeedbackWindowController?
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -120,7 +120,9 @@ class AboutViewController: ParentViewController {
} }
@IBAction func reportIssue(_: Any) { @IBAction func reportIssue(_: Any) {
feedbackWindow.showWindow(nil) feedbackWindow = AppFeedbackWindowController.sharedWindow
feedbackWindow?.appFeedbackWindowDelegate = self
feedbackWindow?.showWindow(nil)
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
view.window?.orderOut(nil) view.window?.orderOut(nil)
@ -150,3 +152,9 @@ class AboutViewController: ParentViewController {
underlineTextForActionButton() underlineTextForActionButton()
} }
} }
extension AboutViewController: AppFeedbackWindowControllerDelegate {
func appFeedbackWindowWillClose() {
feedbackWindow = nil
}
}

6
Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift

@ -5,6 +5,10 @@ import CoreLoggerKit
import CoreModelKit import CoreModelKit
import FirebaseDatabase import FirebaseDatabase
protocol AppFeedbackWindowControllerDelegate: AnyObject {
func appFeedbackWindowWillClose()
}
extension NSNib.Name { extension NSNib.Name {
static let appFeedbackWindowIdentifier = NSNib.Name("AppFeedbackWindow") static let appFeedbackWindowIdentifier = NSNib.Name("AppFeedbackWindow")
static let onboardingWindowIdentifier = NSNib.Name("OnboardingWindow") static let onboardingWindowIdentifier = NSNib.Name("OnboardingWindow")
@ -36,6 +40,7 @@ class AppFeedbackWindowController: NSWindowController {
@IBOutlet var progressIndicator: NSProgressIndicator! @IBOutlet var progressIndicator: NSProgressIndicator!
@IBOutlet var quickCommentsLabel: PointingHandCursorButton! @IBOutlet var quickCommentsLabel: PointingHandCursorButton!
public weak var appFeedbackWindowDelegate: AppFeedbackWindowControllerDelegate?
private var themeDidChangeNotification: NSObjectProtocol? private var themeDidChangeNotification: NSObjectProtocol?
private var serialNumber: String? { private var serialNumber: String? {
let platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")) let platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"))
@ -307,6 +312,7 @@ extension AppFeedbackWindowController: NSWindowDelegate {
emailField.stringValue = CLEmptyString emailField.stringValue = CLEmptyString
feedbackTextView.string = CLEmptyString feedbackTextView.string = CLEmptyString
isActivityInProgress = false isActivityInProgress = false
appFeedbackWindowDelegate?.appFeedbackWindowWillClose()
} }
func bringPreferencesWindowToFront() { func bringPreferencesWindowToFront() {

17
Clocker/Preferences/Appearance/AppearanceViewController.swift

@ -16,6 +16,8 @@ class AppearanceViewController: ParentViewController {
@IBOutlet var includePlaceNameControl: NSSegmentedControl! @IBOutlet var includePlaceNameControl: NSSegmentedControl!
@IBOutlet var appearanceTab: NSTabView! @IBOutlet var appearanceTab: NSTabView!
@IBOutlet var appDisplayControl: NSSegmentedControl! @IBOutlet var appDisplayControl: NSSegmentedControl!
@IBOutlet var syncLabel: NSTextField!
@IBOutlet var syncSegementedControl: NSSegmentedControl!
private var themeDidChangeNotification: NSObjectProtocol? private var themeDidChangeNotification: NSObjectProtocol?
@ -134,6 +136,10 @@ class AppearanceViewController: ParentViewController {
// True is Menubar Only and False is Menubar + Dock // True is Menubar Only and False is Menubar + Dock
let appDisplayOptions = DataStore.shared().shouldDisplay(.appDisplayOptions) let appDisplayOptions = DataStore.shared().shouldDisplay(.appDisplayOptions)
appDisplayControl.setSelected(true, forSegment: appDisplayOptions ? 0 : 1) appDisplayControl.setSelected(true, forSegment: appDisplayOptions ? 0 : 1)
// Set the Sync value from NSUbiqutousKeyValueStore
let syncEnabled = NSUbiquitousKeyValueStore.default.bool(forKey: CLEnableSyncKey)
syncSegementedControl.setSelected(true, forSegment: syncEnabled ? 0 : 1)
} }
@IBOutlet var timeFormatLabel: NSTextField! @IBOutlet var timeFormatLabel: NSTextField!
@ -150,7 +156,6 @@ class AppearanceViewController: ParentViewController {
@IBOutlet var menubarModeLabel: NSTextField! @IBOutlet var menubarModeLabel: NSTextField!
@IBOutlet var previewLabel: NSTextField! @IBOutlet var previewLabel: NSTextField!
@IBOutlet var miscelleaneousLabel: NSTextField! @IBOutlet var miscelleaneousLabel: NSTextField!
@IBOutlet var dstTransitionField: NSTextField!
// Panel Preview // Panel Preview
@IBOutlet var previewPanelTableView: NSTableView! @IBOutlet var previewPanelTableView: NSTableView!
@ -162,6 +167,7 @@ class AppearanceViewController: ParentViewController {
showSliderLabel.stringValue = "Time Scroller".localized() showSliderLabel.stringValue = "Time Scroller".localized()
showSunriseLabel.stringValue = "Show Sunrise/Sunset".localized() showSunriseLabel.stringValue = "Show Sunrise/Sunset".localized()
largerTextLabel.stringValue = "Larger Text".localized() largerTextLabel.stringValue = "Larger Text".localized()
syncLabel.stringValue = "Enable iCloud Sync".localized()
futureSliderRangeLabel.stringValue = "Future Slider Range".localized() futureSliderRangeLabel.stringValue = "Future Slider Range".localized()
includeDateLabel.stringValue = "Include Date".localized() includeDateLabel.stringValue = "Include Date".localized()
includeDayLabel.stringValue = "Include Day".localized() includeDayLabel.stringValue = "Include Day".localized()
@ -172,9 +178,9 @@ class AppearanceViewController: ParentViewController {
[timeFormatLabel, panelTheme, [timeFormatLabel, panelTheme,
dayDisplayOptionsLabel, showSliderLabel, dayDisplayOptionsLabel, showSliderLabel,
showSunriseLabel, largerTextLabel, futureSliderRangeLabel, showSunriseLabel, largerTextLabel, syncLabel, futureSliderRangeLabel,
includeDayLabel, includeDateLabel, includePlaceLabel, appDisplayLabel, menubarModeLabel, includeDayLabel, includeDateLabel, includePlaceLabel, appDisplayLabel, menubarModeLabel,
previewLabel, miscelleaneousLabel, dstTransitionField].forEach { previewLabel, miscelleaneousLabel].forEach {
$0?.textColor = Themer.shared().mainTextColor() $0?.textColor = Themer.shared().mainTextColor()
} }
@ -353,8 +359,9 @@ class AppearanceViewController: ParentViewController {
previewPanelTableView.reloadData() previewPanelTableView.reloadData()
} }
@IBAction func toggleDSTTransitionOption(_: Any) { @IBAction func toggleSync(_ sender: NSSegmentedControl) {
previewPanelTableView.reloadData() NSUbiquitousKeyValueStore.default.set(sender.selectedSegment == 0, forKey: CLEnableSyncKey)
DataStore.shared().setupSyncNotification()
} }
} }

21
Clocker/Preferences/Calendar/CalendarViewController.swift

@ -5,29 +5,26 @@ import CoreLoggerKit
import EventKit import EventKit
class ClockerTextBackgroundView: NSView { class ClockerTextBackgroundView: NSView {
private var themeDidChangeNotification: NSObjectProtocol?
override func awakeFromNib() { override func awakeFromNib() {
wantsLayer = true wantsLayer = true
layer?.cornerRadius = 8.0 layer?.cornerRadius = 8.0
layer?.masksToBounds = false layer?.masksToBounds = false
layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor
themeDidChangeNotification = NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { _ in
self.layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor
}
}
deinit { NotificationCenter.default.addObserver(self,
if let themeDidChangeNotif = themeDidChangeNotification { selector: #selector(updateBackgroundColor),
NotificationCenter.default.removeObserver(themeDidChangeNotif) name: .themeDidChangeNotification,
} object: nil)
} }
override func updateLayer() { override func updateLayer() {
super.updateLayer() super.updateLayer()
layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor
} }
@objc func updateBackgroundColor() {
layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor
}
} }
class CalendarViewController: ParentViewController { class CalendarViewController: ParentViewController {

2
Clocker/Preferences/General/PreferencesViewController.swift

@ -93,7 +93,7 @@ class PreferencesViewController: ParentViewController {
name: NSNotification.Name.customLabelChanged, name: NSNotification.Name.customLabelChanged,
object: nil) object: nil)
NotificationCenter.default.addObserver(forName: NSUbiquitousKeyValueStore.didChangeExternallyNotification, NotificationCenter.default.addObserver(forName: DataStore.didSyncFromExternalSourceNotification,
object: self, object: self,
queue: OperationQueue.main) queue: OperationQueue.main)
{ [weak self] _ in { [weak self] _ in

2
Clocker/Preferences/General/SearchDataSource.swift

@ -21,7 +21,7 @@ struct TimezoneMetadata {
} }
class SearchDataSource: NSObject { class SearchDataSource: NSObject {
private var searchField: NSSearchField! private weak var searchField: NSSearchField!
private var finalArray: [RowType] = [] private var finalArray: [RowType] = []
private var location: SearchLocation = .preferences private var location: SearchLocation = .preferences
private var dataTask: URLSessionDataTask? = .none private var dataTask: URLSessionDataTask? = .none

6
Clocker/Preferences/Menu Bar/MenubarTitleProvider.swift

@ -7,12 +7,12 @@ import EventKit
class MenubarTitleProvider: NSObject { class MenubarTitleProvider: NSObject {
private let store: DataStore private let store: DataStore
init(with dataStore: DataStore) { init(with dataStore: DataStore) {
self.store = dataStore store = dataStore
super.init() super.init()
} }
func titleForMenubar() -> String? { func titleForMenubar() -> String? {
if let nextEvent = checkForUpcomingEvents() { if let nextEvent = checkForUpcomingEvents() {
return nextEvent return nextEvent

1
Clocker/Preferences/Menu Bar/StatusContainerView.swift

@ -239,6 +239,7 @@ class StatusContainerView: NSView {
// NSView move animation // NSView move animation
NSAnimationContext.runAnimationGroup({ context in NSAnimationContext.runAnimationGroup({ context in
context.duration = 0.2 context.duration = 0.2
context.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
let newFrame = CGRect(x: frame.origin.x, y: frame.origin.y, width: newWidth, height: frame.size.height) let newFrame = CGRect(x: frame.origin.x, y: frame.origin.y, width: newWidth, height: frame.size.height)
// The view will animate to the new origin // The view will animate to the new origin
self.animator().frame = newFrame self.animator().frame = newFrame

10
Clocker/Preferences/Menu Bar/StatusItemHandler.swift

@ -240,23 +240,21 @@ class StatusItemHandler: NSObject {
} }
func updateCompactMenubar() { func updateCompactMenubar() {
if let upcomingEvent = menubarTitleHandler.checkForUpcomingEvents() { if menubarTitleHandler.checkForUpcomingEvents() != nil {
print("Need to construct upcoming event view \(upcomingEvent)")
// Iterate and see if we're showing the calendar item view // Iterate and see if we're showing the calendar item view
let upcomingEventView = retrieveUpcomingEventStatusView() let upcomingEventView = retrieveUpcomingEventStatusView()
// If not, reconstruct Status Container View with another view // If not, reconstruct Status Container View with another view
if upcomingEventView == nil { if upcomingEventView == nil {
constructCompactView(with: true) constructCompactView(with: true)
} }
} else { } else if let upcomingEventView = retrieveUpcomingEventStatusView() {
let upcomingEventView = retrieveUpcomingEventStatusView() upcomingEventView.removeFromSuperview()
upcomingEventView?.removeFromSuperview()
constructCompactView() // So that Status Container View reclaims the space constructCompactView() // So that Status Container View reclaims the space
} }
// This will internally call `statusItemViewSetNeedsDisplay` on all subviews ensuring all text in the menubar is up-to-date. // This will internally call `statusItemViewSetNeedsDisplay` on all subviews ensuring all text in the menubar is up-to-date.
statusContainerView?.updateTime() statusContainerView?.updateTime()
} }
private func removeUpcomingStatusItemView() { private func removeUpcomingStatusItemView() {
NSAnimationContext.runAnimationGroup({ context in NSAnimationContext.runAnimationGroup({ context in
context.duration = 0.2 context.duration = 0.2

90
Clocker/Preferences/Preferences.storyboard

@ -791,8 +791,8 @@
<gridRow id="YtR-o1-nGp"/> <gridRow id="YtR-o1-nGp"/>
<gridRow id="vjb-Ch-BZs"/> <gridRow id="vjb-Ch-BZs"/>
<gridRow id="ad4-FM-AWq"/> <gridRow id="ad4-FM-AWq"/>
<gridRow id="yUG-1W-BQ2"/>
<gridRow id="fbk-ef-Req"/> <gridRow id="fbk-ef-Req"/>
<gridRow id="B74-Mt-1h8"/>
</rows> </rows>
<columns> <columns>
<gridColumn xPlacement="trailing" id="ARE-A4-4k4"/> <gridColumn xPlacement="trailing" id="ARE-A4-4k4"/>
@ -924,67 +924,66 @@
</connections> </connections>
</segmentedControl> </segmentedControl>
</gridCell> </gridCell>
<gridCell row="yUG-1W-BQ2" column="ARE-A4-4k4" id="M8P-5s-oL4"> <gridCell row="fbk-ef-Req" column="ARE-A4-4k4" id="Vth-MP-nfS">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" preferredMaxLayoutWidth="120" translatesAutoresizingMaskIntoConstraints="NO" id="ZWy-WW-6H9"> <textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" preferredMaxLayoutWidth="120" translatesAutoresizingMaskIntoConstraints="NO" id="xwt-pY-1w9">
<rect key="frame" x="50" y="42" width="163" height="18"/> <rect key="frame" x="9" y="43" width="204" height="18"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="159" id="NX0-Bo-SU8"/> <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="200" id="7fj-Em-Lyh"/>
</constraints> </constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Show DST transition info" id="i0m-6h-scA"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Larger Text" id="LOM-yg-tI0">
<font key="font" size="13" name="Avenir-Light"/> <font key="font" size="13" name="Avenir-Light"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
</gridCell> </gridCell>
<gridCell row="yUG-1W-BQ2" column="YBI-pK-gPQ" id="N12-lC-Pe1"> <gridCell row="fbk-ef-Req" column="YBI-pK-gPQ" id="dCk-cz-no0">
<segmentedControl key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="DCB-IB-UxK"> <slider key="contentView" verticalHuggingPriority="750" alphaValue="0.59999999999999998" translatesAutoresizingMaskIntoConstraints="NO" id="3cU-IS-3Qu">
<rect key="frame" x="229" y="39" width="81" height="24"/> <rect key="frame" x="229" y="36" width="104" height="28"/>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="selectOne" id="PZZ-H7-LgV"> <sliderCell key="cell" state="on" alignment="left" minValue="4" maxValue="7" doubleValue="4" tickMarkPosition="above" numberOfTickMarks="4" allowsTickMarkValuesOnly="YES" sliderType="linear" id="eAh-k3-cof"/>
<font key="font" size="12" name="Avenir-Light"/>
<segments>
<segment label="Yes" width="36"/>
<segment label="No" width="36" selected="YES" tag="1"/>
</segments>
</segmentedCell>
<connections> <connections>
<action selector="toggleDSTTransitionOption:" target="1aL-zR-8L4" id="9bi-NL-gMv"/> <action selector="fontSliderChanged:" target="1aL-zR-8L4" id="YAW-aA-5aR"/>
<binding destination="Gpv-Gr-MxZ" name="selectedIndex" keyPath="values.com.abhishek.showDSTTransitionInfo" id="eG0-sL-5hE"/> <binding destination="Gpv-Gr-MxZ" name="value" keyPath="values.userFontSize" id="Dzw-Zc-qN5"/>
</connections> </connections>
</segmentedControl> </slider>
</gridCell> </gridCell>
<gridCell row="fbk-ef-Req" column="ARE-A4-4k4" id="Vth-MP-nfS"> <gridCell row="B74-Mt-1h8" column="ARE-A4-4k4" id="PbY-PK-2xB">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" preferredMaxLayoutWidth="120" translatesAutoresizingMaskIntoConstraints="NO" id="xwt-pY-1w9"> <textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" preferredMaxLayoutWidth="120" translatesAutoresizingMaskIntoConstraints="NO" id="kkn-L8-yOj">
<rect key="frame" x="9" y="1" width="204" height="18"/> <rect key="frame" x="9" y="2" width="204" height="18"/>
<constraints> <constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="200" id="7fj-Em-Lyh"/> <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="200" id="eD6-Fv-2tz"/>
</constraints> </constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Larger Text" id="LOM-yg-tI0"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Enable iCloud Sync" id="MDa-w4-4Xw">
<font key="font" size="13" name="Avenir-Light"/> <font key="font" size="13" name="Avenir-Light"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
</gridCell> </gridCell>
<gridCell row="fbk-ef-Req" column="YBI-pK-gPQ" id="dCk-cz-no0"> <gridCell row="B74-Mt-1h8" column="YBI-pK-gPQ" id="kVc-I2-8Kp">
<slider key="contentView" verticalHuggingPriority="750" alphaValue="0.59999999999999998" translatesAutoresizingMaskIntoConstraints="NO" id="3cU-IS-3Qu"> <segmentedControl key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ncK-Q6-ag9">
<rect key="frame" x="229" y="-6" width="104" height="28"/> <rect key="frame" x="229" y="-1" width="81" height="24"/>
<sliderCell key="cell" state="on" alignment="left" minValue="4" maxValue="7" doubleValue="4" tickMarkPosition="above" numberOfTickMarks="4" allowsTickMarkValuesOnly="YES" sliderType="linear" id="eAh-k3-cof"/> <segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="selectOne" id="bio-ov-phY">
<font key="font" size="12" name="Avenir-Light"/>
<segments>
<segment label="Yes" width="36"/>
<segment label="No" width="36" selected="YES" tag="1"/>
</segments>
</segmentedCell>
<connections> <connections>
<action selector="fontSliderChanged:" target="1aL-zR-8L4" id="YAW-aA-5aR"/> <action selector="toggleSync:" target="1aL-zR-8L4" id="4Mg-ie-F7U"/>
<binding destination="Gpv-Gr-MxZ" name="value" keyPath="values.userFontSize" id="Dzw-Zc-qN5"/>
</connections> </connections>
</slider> </segmentedControl>
</gridCell> </gridCell>
</gridCells> </gridCells>
</gridView> </gridView>
<scrollView borderType="line" autohidesScrollers="YES" horizontalLineScroll="113" horizontalPageScroll="10" verticalLineScroll="113" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ytg-0u-Mtu"> <scrollView borderType="line" autohidesScrollers="YES" horizontalLineScroll="113" horizontalPageScroll="10" verticalLineScroll="113" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ytg-0u-Mtu">
<rect key="frame" x="70" y="39" width="400" height="100"/> <rect key="frame" x="70" y="39" width="400" height="100"/>
<clipView key="contentView" ambiguous="YES" id="gnX-f5-31D"> <clipView key="contentView" id="gnX-f5-31D">
<rect key="frame" x="1" y="1" width="398" height="98"/> <rect key="frame" x="1" y="1" width="398" height="98"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="111" rowSizeStyle="automatic" viewBased="YES" id="KbJ-p4-i6E"> <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="111" rowSizeStyle="automatic" viewBased="YES" id="KbJ-p4-i6E">
<rect key="frame" x="0.0" y="0.0" width="412" height="113"/> <rect key="frame" x="0.0" y="0.0" width="412" height="113"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/> <size key="intercellSpacing" width="3" height="2"/>
@ -1468,7 +1467,6 @@
<outlet property="appDisplayLabel" destination="HNj-dh-8gr" id="A6U-gp-sje"/> <outlet property="appDisplayLabel" destination="HNj-dh-8gr" id="A6U-gp-sje"/>
<outlet property="appearanceTab" destination="SGu-yd-JQh" id="CJn-3E-Ujd"/> <outlet property="appearanceTab" destination="SGu-yd-JQh" id="CJn-3E-Ujd"/>
<outlet property="dayDisplayOptionsLabel" destination="gFE-hZ-J92" id="tVv-pC-MSW"/> <outlet property="dayDisplayOptionsLabel" destination="gFE-hZ-J92" id="tVv-pC-MSW"/>
<outlet property="dstTransitionField" destination="ZWy-WW-6H9" id="psJ-Wm-6Re"/>
<outlet property="futureSliderRangeLabel" destination="GaR-Qm-7u4" id="0aB-BK-7fN"/> <outlet property="futureSliderRangeLabel" destination="GaR-Qm-7u4" id="0aB-BK-7fN"/>
<outlet property="includeDateInMenubarControl" destination="Axn-Tb-Cdx" id="M5x-Qt-zvs"/> <outlet property="includeDateInMenubarControl" destination="Axn-Tb-Cdx" id="M5x-Qt-zvs"/>
<outlet property="includeDateLabel" destination="fTA-lS-4wJ" id="J1i-yl-JmT"/> <outlet property="includeDateLabel" destination="fTA-lS-4wJ" id="J1i-yl-JmT"/>
@ -1487,6 +1485,8 @@
<outlet property="showSliderLabel" destination="8Jv-Cf-blJ" id="UHb-hK-VVl"/> <outlet property="showSliderLabel" destination="8Jv-Cf-blJ" id="UHb-hK-VVl"/>
<outlet property="showSunriseLabel" destination="4lt-X6-3uU" id="tez-s5-RzL"/> <outlet property="showSunriseLabel" destination="4lt-X6-3uU" id="tez-s5-RzL"/>
<outlet property="sliderDayRangePopup" destination="8Nx-Xq-XDU" id="PBM-yB-Yo1"/> <outlet property="sliderDayRangePopup" destination="8Nx-Xq-XDU" id="PBM-yB-Yo1"/>
<outlet property="syncLabel" destination="kkn-L8-yOj" id="egW-Df-fnH"/>
<outlet property="syncSegementedControl" destination="ncK-Q6-ag9" id="8oT-zA-Ms5"/>
<outlet property="theme" destination="89w-KN-GJ6" id="SAL-Sh-eqp"/> <outlet property="theme" destination="89w-KN-GJ6" id="SAL-Sh-eqp"/>
<outlet property="timeFormat" destination="iiG-Xu-4id" id="oM3-1Y-fAF"/> <outlet property="timeFormat" destination="iiG-Xu-4id" id="oM3-1Y-fAF"/>
<outlet property="timeFormatLabel" destination="wtO-uL-QBf" id="udS-d6-Tep"/> <outlet property="timeFormatLabel" destination="wtO-uL-QBf" id="udS-d6-Tep"/>
@ -1513,7 +1513,7 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<searchField toolTip="Search a timezone" wantsLayer="YES" focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Dha-h9-Nd0"> <searchField toolTip="Search a timezone" wantsLayer="YES" focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Dha-h9-Nd0">
<rect key="frame" x="8" y="205" width="320" height="23"/> <rect key="frame" x="8" y="121" width="320" height="23"/>
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" focusRingType="none" placeholderString="Enter a city, state, country name" usesSingleLineMode="YES" maximumRecents="5" id="ikU-Tm-0WZ"> <searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" focusRingType="none" placeholderString="Enter a city, state, country name" usesSingleLineMode="YES" maximumRecents="5" id="ikU-Tm-0WZ">
<font key="font" size="13" name="Avenir-Light"/> <font key="font" size="13" name="Avenir-Light"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -1570,13 +1570,13 @@ DQ
</connections> </connections>
</button> </button>
<scrollView focusRingType="none" borderType="none" autohidesScrollers="YES" horizontalLineScroll="32" horizontalPageScroll="10" verticalLineScroll="32" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0wY-ff-FLW"> <scrollView focusRingType="none" borderType="none" autohidesScrollers="YES" horizontalLineScroll="32" horizontalPageScroll="10" verticalLineScroll="32" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0wY-ff-FLW">
<rect key="frame" x="8" y="30" width="320" height="165"/> <rect key="frame" x="8" y="30" width="320" height="81"/>
<clipView key="contentView" drawsBackground="NO" id="rGc-3M-cCq"> <clipView key="contentView" drawsBackground="NO" id="rGc-3M-cCq">
<rect key="frame" x="0.0" y="0.0" width="320" height="165"/> <rect key="frame" x="0.0" y="0.0" width="320" height="81"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" rowHeight="30" rowSizeStyle="automatic" viewBased="YES" id="xkl-2X-ZCb"> <tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" rowHeight="30" rowSizeStyle="automatic" viewBased="YES" id="xkl-2X-ZCb">
<rect key="frame" x="0.0" y="0.0" width="320" height="165"/> <rect key="frame" x="0.0" y="0.0" width="320" height="81"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/> <size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -1666,14 +1666,14 @@ DQ
</scroller> </scroller>
</scrollView> </scrollView>
<progressIndicator wantsLayer="YES" focusRingType="none" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="0A5-gp-lay"> <progressIndicator wantsLayer="YES" focusRingType="none" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="0A5-gp-lay">
<rect key="frame" x="160" y="132" width="16" height="16"/> <rect key="frame" x="160" y="90" width="16" height="16"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="16" id="fgE-77-Vda"/> <constraint firstAttribute="height" constant="16" id="fgE-77-Vda"/>
<constraint firstAttribute="width" constant="16" id="pwe-em-e0a"/> <constraint firstAttribute="width" constant="16" id="pwe-em-e0a"/>
</constraints> </constraints>
</progressIndicator> </progressIndicator>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xgb-wU-8RU"> <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xgb-wU-8RU">
<rect key="frame" x="18" y="102" width="300" height="22"/> <rect key="frame" x="18" y="60" width="300" height="22"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="22" id="zqt-d8-yas"/> <constraint firstAttribute="height" constant="22" id="zqt-d8-yas"/>
</constraints> </constraints>
@ -1721,7 +1721,7 @@ DQ
<rect key="frame" x="0.0" y="484" width="613" height="30"/> <rect key="frame" x="0.0" y="484" width="613" height="30"/>
<subviews> <subviews>
<button toolTip="Sorts by time difference from your current timezone" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="P6d-qq-ycq"> <button toolTip="Sorts by time difference from your current timezone" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="P6d-qq-ycq">
<rect key="frame" x="285" y="3" width="8" height="25"/> <rect key="frame" x="283" y="3" width="9" height="25"/>
<constraints> <constraints>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="180" id="cAs-on-f7X"/> <constraint firstAttribute="width" relation="lessThanOrEqual" constant="180" id="cAs-on-f7X"/>
<constraint firstAttribute="height" constant="25" id="juv-QL-vMx"/> <constraint firstAttribute="height" constant="25" id="juv-QL-vMx"/>
@ -1736,7 +1736,7 @@ DQ
</connections> </connections>
</button> </button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0HL-uj-s4v"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0HL-uj-s4v">
<rect key="frame" x="331" y="3" width="8" height="25"/> <rect key="frame" x="331" y="3" width="9" height="25"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="25" id="eZL-Gr-38S"/> <constraint firstAttribute="height" constant="25" id="eZL-Gr-38S"/>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="120" id="sJk-T7-7Lm"/> <constraint firstAttribute="width" relation="lessThanOrEqual" constant="120" id="sJk-T7-7Lm"/>
@ -1751,7 +1751,7 @@ DQ
</connections> </connections>
</button> </button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="6fs-Mx-NcG"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="6fs-Mx-NcG">
<rect key="frame" x="308" y="3" width="8" height="25"/> <rect key="frame" x="307" y="3" width="9" height="25"/>
<constraints> <constraints>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="190" id="241-Rn-G6N"/> <constraint firstAttribute="width" relation="lessThanOrEqual" constant="190" id="241-Rn-G6N"/>
<constraint firstAttribute="height" constant="25" id="weP-ll-vZ8"/> <constraint firstAttribute="height" constant="25" id="weP-ll-vZ8"/>
@ -2010,7 +2010,7 @@ CA
<image name="ClockerIcon-512" width="1024" height="1024"/> <image name="ClockerIcon-512" width="1024" height="1024"/>
<image name="CurrentLocation" width="350" height="350"/> <image name="CurrentLocation" width="350" height="350"/>
<image name="Extra" width="700" height="700"/> <image name="Extra" width="700" height="700"/>
<image name="NSDescendingSortIndicator" width="8" height="8"/> <image name="NSDescendingSortIndicator" width="9" height="9"/>
<image name="NSInfo" width="32" height="32"/> <image name="NSInfo" width="32" height="32"/>
<image name="NSPreferencesGeneral" width="32" height="32"/> <image name="NSPreferencesGeneral" width="32" height="32"/>
<image name="Privacy" width="350" height="350"/> <image name="Privacy" width="350" height="350"/>

Loading…
Cancel
Save